thunar-usagebars/plugins/thunar-apr/thunar-apr-desktop-page.c
4aiman e969f94310 thunar 4.20.2 upstream
Tree of tag thunar-4.20.2 (commit 11a519f) from https://gitlab.xfce.org/xfce/thunar.
Upstream history is not included here to keep the repository small.
2026-11-03 22:42:48 +03:00

1123 lines
42 KiB
C

/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#include <stdio.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <libxfce4ui/libxfce4ui.h>
#include <libxfce4util/libxfce4util.h>
#include <thunar-apr/thunar-apr-desktop-page.h>
/* use g_access() on win32 */
#if defined(G_OS_WIN32)
#include <glib/gstdio.h>
#else
#define g_access(filename, mode) (access ((filename), (mode)))
#endif
static void
thunar_apr_desktop_page_finalize (GObject *object);
static void
thunar_apr_desktop_page_file_changed (ThunarAprAbstractPage *abstract_page,
ThunarxFileInfo *file);
static void
thunar_apr_desktop_page_save (ThunarAprDesktopPage *desktop_page,
GtkWidget *widget);
static void
thunar_apr_desktop_page_save_widget (ThunarAprDesktopPage *desktop_page,
GtkWidget *widget,
GKeyFile *key_file);
static void
thunar_apr_desktop_page_activated (GtkWidget *entry,
ThunarAprDesktopPage *desktop_page);
static gboolean
thunar_apr_desktop_page_focus_out_event (GtkWidget *entry,
GdkEventFocus *event,
ThunarAprDesktopPage *desktop_page);
static void
thunar_apr_desktop_page_toggled (GtkWidget *button,
ThunarAprDesktopPage *desktop_page);
static void
thunar_apr_desktop_page_program_toggled (GtkWidget *button,
ThunarAprDesktopPage *desktop_page);
static void
thunar_apr_desktop_page_trusted_toggled (GtkWidget *button,
ThunarAprDesktopPage *desktop_page);
static gboolean
is_executable (GFile *gfile,
GError **error);
static gboolean
set_executable (GFile *gfile,
gboolean executable,
GError **error);
struct _ThunarAprDesktopPageClass
{
ThunarAprAbstractPageClass __parent__;
};
struct _ThunarAprDesktopPage
{
ThunarAprAbstractPage __parent__;
GtkWidget *description_entry;
GtkWidget *command_entry;
GtkWidget *path_entry;
GtkWidget *url_entry;
GtkWidget *comment_entry;
GtkWidget *snotify_button;
GtkWidget *terminal_button;
GtkWidget *program_button;
GtkWidget *trusted_button;
/* the values of the entries remember when
* the file was saved last time to avoid a
* race condition between saving the file
* and the FAM notification which is received
* with some delay and would otherwise over-
* ride the (possibly changed) values of the
* entries.
*/
gchar *description_text;
gchar *command_text;
gchar *path_text;
gchar *url_text;
gchar *comment_text;
};
THUNARX_DEFINE_TYPE (ThunarAprDesktopPage,
thunar_apr_desktop_page,
THUNAR_APR_TYPE_ABSTRACT_PAGE);
static gboolean
is_executable (GFile *gfile,
GError **error)
{
GError *error_local = NULL;
GFileInfo *info;
gboolean executable;
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
g_return_val_if_fail (G_IS_FILE (gfile), FALSE);
info = g_file_query_info (gfile,
G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE,
G_FILE_QUERY_INFO_NONE,
NULL,
&error_local);
if (error_local != NULL)
{
g_propagate_error (error, error_local);
return FALSE;
}
executable = g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE);
g_object_unref (info);
return executable;
}
/* copied from exo-die-utils.c */
static gboolean
set_executable (GFile *gfile,
gboolean executable,
GError **error)
{
GError *error_local = NULL;
guint32 mode = 0111, mask = 0111;
guint32 old_mode, new_mode;
GFileInfo *info;
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
g_return_val_if_fail (G_IS_FILE (gfile), FALSE);
info = g_file_query_info (gfile,
G_FILE_ATTRIBUTE_UNIX_MODE,
G_FILE_QUERY_INFO_NONE,
NULL,
&error_local);
if (error_local != NULL)
{
g_propagate_error (error, error_local);
return FALSE;
}
old_mode = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE);
new_mode = executable ? ((old_mode & ~mask) | mode) : (old_mode & ~mask);
if (old_mode != new_mode)
{
g_file_set_attribute_uint32 (gfile,
G_FILE_ATTRIBUTE_UNIX_MODE, new_mode,
G_FILE_QUERY_INFO_NONE,
NULL,
&error_local);
}
g_object_unref (info);
if (error_local != NULL)
{
g_propagate_error (error, error_local);
return FALSE;
}
return TRUE;
}
static void
thunar_apr_desktop_page_class_init (ThunarAprDesktopPageClass *klass)
{
ThunarAprAbstractPageClass *thunarapr_abstract_page_class;
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = thunar_apr_desktop_page_finalize;
thunarapr_abstract_page_class = THUNAR_APR_ABSTRACT_PAGE_CLASS (klass);
thunarapr_abstract_page_class->file_changed = thunar_apr_desktop_page_file_changed;
}
static void
thunar_apr_desktop_page_init (ThunarAprDesktopPage *desktop_page)
{
AtkRelationSet *relations;
PangoAttribute *attribute;
PangoAttrList *attr_list;
AtkRelation *relation;
AtkObject *object;
GtkWidget *grid;
GtkWidget *label;
GtkWidget *spacer;
guint row = 0;
GFile *gfile;
gboolean metadata_supported;
gtk_container_set_border_width (GTK_CONTAINER (desktop_page), 12);
/* allocate shared bold Pango attributes */
attr_list = pango_attr_list_new ();
attribute = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
attribute->start_index = 0;
attribute->end_index = -1;
pango_attr_list_insert (attr_list, attribute);
grid = gtk_grid_new ();
gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_container_add (GTK_CONTAINER (desktop_page), grid);
gtk_widget_show (grid);
label = gtk_label_new (_("Description:"));
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
gtk_label_set_attributes (GTK_LABEL (label), attr_list);
gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
gtk_widget_show (label);
desktop_page->description_entry = gtk_entry_new ();
gtk_widget_set_tooltip_text (desktop_page->description_entry, _("The generic name of the entry, for example \"Web Browser\" "
"in case of Firefox."));
g_signal_connect (G_OBJECT (desktop_page->description_entry), "activate", G_CALLBACK (thunar_apr_desktop_page_activated), desktop_page);
g_signal_connect (G_OBJECT (desktop_page->description_entry), "focus-out-event", G_CALLBACK (thunar_apr_desktop_page_focus_out_event), desktop_page);
gtk_widget_set_hexpand (desktop_page->description_entry, TRUE);
gtk_grid_attach (GTK_GRID (grid), desktop_page->description_entry, 1, row, 1, 1);
gtk_widget_show (desktop_page->description_entry);
g_object_bind_property (G_OBJECT (desktop_page->description_entry), "visible", G_OBJECT (label), "visible", G_BINDING_SYNC_CREATE);
/* set Atk label relation for the entry */
object = gtk_widget_get_accessible (desktop_page->description_entry);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
row++;
label = gtk_label_new (_("Command:"));
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
gtk_label_set_attributes (GTK_LABEL (label), attr_list);
gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
gtk_widget_show (label);
desktop_page->command_entry = gtk_entry_new ();
gtk_widget_set_tooltip_text (desktop_page->command_entry, _("The program to execute, possibly with arguments."));
g_signal_connect (G_OBJECT (desktop_page->command_entry), "activate", G_CALLBACK (thunar_apr_desktop_page_activated), desktop_page);
g_signal_connect (G_OBJECT (desktop_page->command_entry), "focus-out-event", G_CALLBACK (thunar_apr_desktop_page_focus_out_event), desktop_page);
gtk_widget_set_hexpand (desktop_page->command_entry, TRUE);
gtk_grid_attach (GTK_GRID (grid), desktop_page->command_entry, 1, row, 1, 1);
gtk_widget_show (desktop_page->command_entry);
g_object_bind_property (G_OBJECT (desktop_page->command_entry), "visible", G_OBJECT (label), "visible", G_BINDING_SYNC_CREATE);
/* set Atk label relation for the entry */
object = gtk_widget_get_accessible (desktop_page->command_entry);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
row++;
label = gtk_label_new (_("Working Directory:"));
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
gtk_label_set_attributes (GTK_LABEL (label), attr_list);
gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
gtk_widget_show (label);
desktop_page->path_entry = gtk_entry_new ();
gtk_widget_set_tooltip_text (desktop_page->path_entry, _("The working directory for the program."));
g_signal_connect (G_OBJECT (desktop_page->path_entry), "activate", G_CALLBACK (thunar_apr_desktop_page_activated), desktop_page);
g_signal_connect (G_OBJECT (desktop_page->path_entry), "focus-out-event", G_CALLBACK (thunar_apr_desktop_page_focus_out_event), desktop_page);
gtk_widget_set_hexpand (desktop_page->path_entry, TRUE);
gtk_grid_attach (GTK_GRID (grid), desktop_page->path_entry, 1, row, 1, 1);
gtk_widget_show (desktop_page->path_entry);
g_object_bind_property (G_OBJECT (desktop_page->path_entry), "visible", G_OBJECT (label), "visible", G_BINDING_SYNC_CREATE);
/* set Atk label relation for the entry */
object = gtk_widget_get_accessible (desktop_page->path_entry);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
row++;
label = gtk_label_new (_("URL:"));
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
gtk_label_set_attributes (GTK_LABEL (label), attr_list);
gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
gtk_widget_show (label);
desktop_page->url_entry = gtk_entry_new ();
gtk_widget_set_tooltip_text (desktop_page->url_entry, _("The URL to access."));
g_signal_connect (G_OBJECT (desktop_page->url_entry), "activate", G_CALLBACK (thunar_apr_desktop_page_activated), desktop_page);
g_signal_connect (G_OBJECT (desktop_page->url_entry), "focus-out-event", G_CALLBACK (thunar_apr_desktop_page_focus_out_event), desktop_page);
gtk_widget_set_hexpand (desktop_page->url_entry, TRUE);
gtk_grid_attach (GTK_GRID (grid), desktop_page->url_entry, 1, row, 1, 1);
gtk_widget_show (desktop_page->url_entry);
g_object_bind_property (G_OBJECT (desktop_page->url_entry), "visible", G_OBJECT (label), "visible", G_BINDING_SYNC_CREATE);
/* set Atk label relation for the entry */
object = gtk_widget_get_accessible (desktop_page->url_entry);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
row++;
label = gtk_label_new (_("Comment:"));
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
gtk_label_set_attributes (GTK_LABEL (label), attr_list);
gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
gtk_widget_show (label);
desktop_page->comment_entry = gtk_entry_new ();
gtk_widget_set_tooltip_text (desktop_page->comment_entry, _("Tooltip for the entry, for example \"View sites on the Internet\" "
"in case of Firefox. Should not be redundant with the name or the "
"description."));
g_signal_connect (G_OBJECT (desktop_page->comment_entry), "activate", G_CALLBACK (thunar_apr_desktop_page_activated), desktop_page);
g_signal_connect (G_OBJECT (desktop_page->comment_entry), "focus-out-event", G_CALLBACK (thunar_apr_desktop_page_focus_out_event), desktop_page);
gtk_widget_set_hexpand (desktop_page->comment_entry, TRUE);
gtk_grid_attach (GTK_GRID (grid), desktop_page->comment_entry, 1, row, 1, 1);
gtk_widget_show (desktop_page->comment_entry);
g_object_bind_property (G_OBJECT (desktop_page->comment_entry), "visible", G_OBJECT (label), "visible", G_BINDING_SYNC_CREATE);
/* set Atk label relation for the entry */
object = gtk_widget_get_accessible (desktop_page->comment_entry);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
row++;
/* Nothing here on purpose */
row++;
/* add spacing between the entries and the options */
spacer = g_object_new (GTK_TYPE_BOX, "orientation", GTK_ORIENTATION_VERTICAL, "height-request", 12, NULL);
gtk_grid_attach (GTK_GRID (grid), spacer, 0, row, 2, 1);
gtk_widget_show (spacer);
row++;
label = gtk_label_new (_("Options:"));
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
gtk_label_set_attributes (GTK_LABEL (label), attr_list);
gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
gtk_widget_show (label);
desktop_page->snotify_button = gtk_check_button_new_with_mnemonic (_("Use _startup notification"));
gtk_widget_set_tooltip_text (desktop_page->snotify_button, _("Select this option to enable startup notification when the command "
"is run from the file manager or the menu. Not every application supports "
"startup notification."));
g_signal_connect (G_OBJECT (desktop_page->snotify_button), "toggled", G_CALLBACK (thunar_apr_desktop_page_toggled), desktop_page);
gtk_widget_set_hexpand (desktop_page->snotify_button, TRUE);
gtk_grid_attach (GTK_GRID (grid), desktop_page->snotify_button, 1, row, 1, 1);
gtk_widget_show (desktop_page->snotify_button);
g_object_bind_property (G_OBJECT (desktop_page->snotify_button), "visible", G_OBJECT (label), "visible", G_BINDING_SYNC_CREATE);
/* set Atk label relation for the buttons */
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
object = gtk_widget_get_accessible (desktop_page->snotify_button);
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
row++;
desktop_page->terminal_button = gtk_check_button_new_with_mnemonic (_("Run in _terminal"));
gtk_widget_set_tooltip_text (desktop_page->terminal_button, _("Select this option to run the command in a terminal window."));
g_signal_connect (G_OBJECT (desktop_page->terminal_button), "toggled", G_CALLBACK (thunar_apr_desktop_page_toggled), desktop_page);
gtk_widget_set_hexpand (desktop_page->terminal_button, TRUE);
gtk_grid_attach (GTK_GRID (grid), desktop_page->terminal_button, 1, row, 1, 1);
gtk_widget_show (desktop_page->terminal_button);
/* don't bind visibility with label */
/* set Atk label relation for the buttons */
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
object = gtk_widget_get_accessible (desktop_page->terminal_button);
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
row++;
label = gtk_label_new (_("Security:"));
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
gtk_label_set_attributes (GTK_LABEL (label), attr_list);
gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
gtk_widget_show (label);
/* same function as in thunar-permission-chooser.c */
desktop_page->program_button = gtk_check_button_new_with_mnemonic (_("Allow this file to _run as a .desktop file"));
gtk_widget_set_tooltip_text (desktop_page->program_button, _("Select this to enable executable permission bit(+x). Thunar will not launch the .desktop file if not set."));
g_signal_connect (G_OBJECT (desktop_page->program_button), "toggled",
G_CALLBACK (thunar_apr_desktop_page_program_toggled), desktop_page);
gtk_widget_set_hexpand (desktop_page->program_button, TRUE);
gtk_grid_attach (GTK_GRID (grid), desktop_page->program_button, 1, row, 1, 1);
gtk_widget_show (desktop_page->program_button);
g_object_bind_property (G_OBJECT (desktop_page->program_button), "visible", G_OBJECT (label), "visible", G_BINDING_SYNC_CREATE);
xfce_gtk_label_set_a11y_relation (GTK_LABEL (label), desktop_page->program_button);
row++;
gfile = g_file_new_for_uri ("file:///");
metadata_supported = xfce_g_file_metadata_is_supported (gfile);
g_object_unref (gfile);
if (metadata_supported)
{
desktop_page->trusted_button = gtk_check_button_new_with_mnemonic (_("Set this file as trusted"));
gtk_widget_set_tooltip_text (desktop_page->trusted_button, _("Select this option to trust this .desktop file. This will generate a checksum of the file and store it via gvfs. The additional check will protect from malicious launchers which e.g. pretend to be a picture, having the executable flag pre-set"));
g_signal_connect (G_OBJECT (desktop_page->trusted_button), "toggled",
G_CALLBACK (thunar_apr_desktop_page_trusted_toggled), desktop_page);
gtk_widget_set_hexpand (desktop_page->trusted_button, TRUE);
gtk_grid_attach (GTK_GRID (grid), desktop_page->trusted_button, 1, row, 1, 1);
gtk_widget_show (desktop_page->trusted_button);
g_object_bind_property (G_OBJECT (desktop_page->trusted_button), "visible", G_OBJECT (label), "visible", G_BINDING_SYNC_CREATE);
xfce_gtk_label_set_a11y_relation (GTK_LABEL (label), desktop_page->trusted_button);
row++;
}
else
{
g_info ("metadata not supported");
desktop_page->trusted_button = NULL;
}
/* release shared bold Pango attributes */
pango_attr_list_unref (attr_list);
}
static void
thunar_apr_desktop_page_finalize (GObject *object)
{
ThunarAprDesktopPage *desktop_page = THUNAR_APR_DESKTOP_PAGE (object);
/* release the saved texts */
g_free (desktop_page->description_text);
g_free (desktop_page->command_text);
g_free (desktop_page->path_text);
g_free (desktop_page->url_text);
g_free (desktop_page->comment_text);
(*G_OBJECT_CLASS (thunar_apr_desktop_page_parent_class)->finalize) (object);
}
static void
thunar_apr_desktop_page_file_changed (ThunarAprAbstractPage *abstract_page,
ThunarxFileInfo *file)
{
ThunarAprDesktopPage *desktop_page = THUNAR_APR_DESKTOP_PAGE (abstract_page);
GKeyFile *key_file;
GFile *gfile;
gboolean writable;
gboolean enabled;
gboolean executable;
gboolean trusted;
GError *error = NULL;
gchar *filename;
gchar *value;
gchar *type;
gchar *uri;
/* allocate the key file memory */
key_file = g_key_file_new ();
/* determine the local path to the file */
uri = thunarx_file_info_get_uri (file);
filename = g_filename_from_uri (uri, NULL, NULL);
g_free (uri);
/* try to load the file contents */
if (filename != NULL && g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL))
{
/* determine the type of the .desktop file (default to "Application") */
type = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "Type", NULL);
if (G_UNLIKELY (type == NULL))
type = g_strdup ("Application");
/* change page title depending on the type */
if (strcmp (type, "Application") == 0)
thunarx_property_page_set_label (THUNARX_PROPERTY_PAGE (desktop_page), _("Launcher"));
else if (strcmp (type, "Link") == 0)
thunarx_property_page_set_label (THUNARX_PROPERTY_PAGE (desktop_page), _("Link"));
else
thunarx_property_page_set_label (THUNARX_PROPERTY_PAGE (desktop_page), type);
/* update the "Description" entry */
value = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "GenericName", NULL, NULL);
if (g_strcmp0 (value, desktop_page->description_text) != 0)
{
/* update the entry */
gtk_entry_set_text (GTK_ENTRY (desktop_page->description_entry), (value != NULL) ? value : "");
/* update the saved value */
g_free (desktop_page->description_text);
desktop_page->description_text = value;
}
else
{
g_free (value);
}
/* update the "Comment" entry */
value = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "Comment", NULL, NULL);
if (g_strcmp0 (value, desktop_page->comment_text) != 0)
{
/* update the entry */
gtk_entry_set_text (GTK_ENTRY (desktop_page->comment_entry), (value != NULL) ? value : "");
/* update the saved value */
g_free (desktop_page->comment_text);
desktop_page->comment_text = value;
}
else
{
g_free (value);
}
/* update the type dependant entries */
if (strcmp (type, "Application") == 0)
{
/* update the "Command" entry but but ignore escape sequences */
value = g_key_file_get_value (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
if (g_strcmp0 (value, desktop_page->command_text) != 0)
{
/* update the entry */
gtk_entry_set_text (GTK_ENTRY (desktop_page->command_entry), (value != NULL) ? value : "");
/* update the saved value */
g_free (desktop_page->command_text);
desktop_page->command_text = value;
}
else
{
g_free (value);
}
/* update the "Path" entry */
value = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "Path", NULL);
if (g_strcmp0 (value, desktop_page->path_text) != 0)
{
/* update the entry */
gtk_entry_set_text (GTK_ENTRY (desktop_page->path_entry), (value != NULL) ? value : "");
/* update the saved value */
g_free (desktop_page->path_text);
desktop_page->path_text = value;
}
else
{
g_free (value);
}
/* update the "Use startup notification" button */
enabled = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, "StartupNotify", &error);
g_signal_handlers_block_by_func (G_OBJECT (desktop_page->snotify_button), thunar_apr_desktop_page_toggled, desktop_page);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (desktop_page->snotify_button), (error == NULL && enabled));
g_signal_handlers_unblock_by_func (G_OBJECT (desktop_page->snotify_button), thunar_apr_desktop_page_toggled, desktop_page);
g_clear_error (&error);
/* update the "Run in terminal" button */
enabled = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, "Terminal", &error);
g_signal_handlers_block_by_func (G_OBJECT (desktop_page->terminal_button), thunar_apr_desktop_page_toggled, desktop_page);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (desktop_page->terminal_button), (error == NULL && enabled));
g_signal_handlers_unblock_by_func (G_OBJECT (desktop_page->terminal_button), thunar_apr_desktop_page_toggled, desktop_page);
g_clear_error (&error);
/* update visibility of the specific widgets */
gtk_widget_show (desktop_page->command_entry);
gtk_widget_show (desktop_page->path_entry);
gtk_widget_hide (desktop_page->url_entry);
gtk_widget_show (desktop_page->snotify_button);
gtk_widget_show (desktop_page->terminal_button);
}
else if (strcmp (type, "Link") == 0)
{
/* update the "URL" entry */
value = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "URL", NULL);
if (g_strcmp0 (value, desktop_page->url_text) != 0)
{
/* update the entry */
gtk_entry_set_text (GTK_ENTRY (desktop_page->url_entry), (value != NULL) ? value : "");
/* update the saved value */
g_free (desktop_page->url_text);
desktop_page->url_text = value;
}
else
{
g_free (value);
}
/* update visibility of the specific widgets */
gtk_widget_hide (desktop_page->command_entry);
gtk_widget_hide (desktop_page->path_entry);
gtk_widget_show (desktop_page->url_entry);
gtk_widget_hide (desktop_page->snotify_button);
gtk_widget_hide (desktop_page->terminal_button);
}
else
{
/* hide the specific widgets */
gtk_widget_hide (desktop_page->command_entry);
gtk_widget_hide (desktop_page->path_entry);
gtk_widget_hide (desktop_page->url_entry);
gtk_widget_hide (desktop_page->snotify_button);
gtk_widget_hide (desktop_page->terminal_button);
}
/* update flags */
gfile = thunarx_file_info_get_location (abstract_page->file);
executable = is_executable (gfile, &error);
if (error != NULL)
{
g_warning ("Failed to initialize program_button : %s", error->message);
g_clear_error (&error);
}
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (desktop_page->program_button), executable);
if (desktop_page->trusted_button != NULL)
{
trusted = xfce_g_file_is_trusted (gfile, NULL, &error);
if (error != NULL)
{
g_warning ("Failed to initialize trusted_button : %s", error->message);
g_clear_error (&error);
}
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (desktop_page->trusted_button), trusted);
}
g_object_unref (gfile);
/* show security */
gtk_widget_show (desktop_page->program_button);
if (desktop_page->trusted_button != NULL)
gtk_widget_show (desktop_page->trusted_button);
/* check if the file is writable... */
writable = (g_access (filename, W_OK) == 0);
/* ...and update the editability of the entries */
gtk_editable_set_editable (GTK_EDITABLE (desktop_page->description_entry), writable);
gtk_editable_set_editable (GTK_EDITABLE (desktop_page->command_entry), writable);
gtk_editable_set_editable (GTK_EDITABLE (desktop_page->path_entry), writable);
gtk_editable_set_editable (GTK_EDITABLE (desktop_page->url_entry), writable);
gtk_editable_set_editable (GTK_EDITABLE (desktop_page->comment_entry), writable);
gtk_widget_set_sensitive (desktop_page->snotify_button, writable);
gtk_widget_set_sensitive (desktop_page->terminal_button, writable);
gtk_widget_set_sensitive (desktop_page->program_button, writable);
if (desktop_page->trusted_button != NULL)
gtk_widget_set_sensitive (desktop_page->trusted_button, writable);
/* cleanup */
g_free (type);
}
else
{
/* reset page title */
thunarx_property_page_set_label (THUNARX_PROPERTY_PAGE (desktop_page), _("Unknown"));
/* hide all widgets */
gtk_widget_hide (desktop_page->description_entry);
gtk_widget_hide (desktop_page->command_entry);
gtk_widget_hide (desktop_page->path_entry);
gtk_widget_hide (desktop_page->url_entry);
gtk_widget_hide (desktop_page->comment_entry);
gtk_widget_hide (desktop_page->snotify_button);
gtk_widget_hide (desktop_page->terminal_button);
gtk_widget_hide (desktop_page->program_button);
if (desktop_page->trusted_button != NULL)
gtk_widget_hide (desktop_page->trusted_button);
}
/* cleanup */
g_key_file_free (key_file);
g_free (filename);
}
static void
thunar_apr_desktop_page_save (ThunarAprDesktopPage *desktop_page,
GtkWidget *widget)
{
GtkWidget *toplevel;
GtkWidget *message;
GKeyFile *key_file;
GError *error = NULL;
gchar *filename;
gchar *data;
gchar *uri;
gsize data_length;
FILE *fp;
GFile *gfile;
gboolean trusted;
/* verify that we still have a valid file */
if (THUNAR_APR_ABSTRACT_PAGE (desktop_page)->file == NULL)
return;
/* determine the local path to the file */
uri = thunarx_file_info_get_uri (THUNAR_APR_ABSTRACT_PAGE (desktop_page)->file);
filename = g_filename_from_uri (uri, NULL, NULL);
g_free (uri);
/* verify that we have a valid local path */
if (G_UNLIKELY (filename == NULL))
return;
/* allocate the key file resources */
key_file = g_key_file_new ();
/* try to parse the key file */
if (g_key_file_load_from_file (key_file, filename, G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, &error))
{
/* save the widget changes to the key file */
thunar_apr_desktop_page_save_widget (desktop_page, widget, key_file);
/* give empty desktop files a type */
if (!g_key_file_has_key (key_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_TYPE, NULL))
{
g_key_file_set_string (key_file,
G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_TYPE,
"Application");
}
/* determine the content of the key file */
data = g_key_file_to_data (key_file, &data_length, &error);
if (G_LIKELY (data_length > 0))
{
trusted = FALSE;
if (desktop_page->trusted_button != NULL)
trusted = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (desktop_page->trusted_button));
/* try to save the key file content to disk */
fp = fopen (filename, "w");
if (G_LIKELY (fp != NULL))
{
if (fwrite (data, data_length, 1, fp) != 1)
error = g_error_new_literal (G_FILE_ERROR, g_file_error_from_errno (errno), g_strerror (errno));
fclose (fp);
}
else
{
error = g_error_new_literal (G_FILE_ERROR, g_file_error_from_errno (errno), g_strerror (errno));
}
/* Update safety flag checksum */
if (trusted && error == NULL)
{
gfile = thunarx_file_info_get_location (THUNAR_APR_ABSTRACT_PAGE (desktop_page)->file);
xfce_g_file_set_trusted (gfile, trusted, NULL, &error);
g_object_unref (gfile);
}
}
/* cleanup */
g_free (data);
}
/* check if we succeed */
if (G_UNLIKELY (error != NULL))
{
/* display an error dialog to the user */
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (desktop_page));
message = gtk_message_dialog_new ((GtkWindow *) toplevel,
GTK_DIALOG_DESTROY_WITH_PARENT
| GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
_("Failed to save \"%s\"."), filename);
gtk_window_set_title (GTK_WINDOW (message), _("Error"));
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (message), "%s.", error->message);
gtk_dialog_run (GTK_DIALOG (message));
gtk_widget_destroy (message);
g_error_free (error);
}
/* cleanup */
g_key_file_free (key_file);
g_free (filename);
}
static void
thunar_apr_desktop_page_set_string (GKeyFile *key_file,
const gchar *key,
const gchar *value)
{
if (xfce_str_is_empty (value))
{
g_key_file_remove_key (key_file,
G_KEY_FILE_DESKTOP_GROUP,
key, NULL);
}
else
{
/* escape special characters but not for the "Exec" property */
if (g_strcmp0 (key, G_KEY_FILE_DESKTOP_KEY_EXEC) == 0)
g_key_file_set_value (key_file,
G_KEY_FILE_DESKTOP_GROUP,
key, value);
else
g_key_file_set_string (key_file,
G_KEY_FILE_DESKTOP_GROUP,
key, value);
}
}
static void
thunar_apr_desktop_page_save_widget (ThunarAprDesktopPage *desktop_page,
GtkWidget *widget,
GKeyFile *key_file)
{
const gchar *const *locale;
gchar *key;
if (widget == desktop_page->description_entry)
{
/* update the saved description text */
g_free (desktop_page->description_text);
desktop_page->description_text = gtk_editable_get_chars (GTK_EDITABLE (widget), 0, -1);
/* save the new description (localized if required) */
for (locale = g_get_language_names (); *locale != NULL; ++locale)
{
key = g_strdup_printf (G_KEY_FILE_DESKTOP_KEY_GENERIC_NAME "[%s]", *locale);
if (g_key_file_has_key (key_file, G_KEY_FILE_DESKTOP_GROUP, key, NULL))
{
thunar_apr_desktop_page_set_string (key_file, key, desktop_page->description_text);
g_free (key);
break;
}
g_free (key);
}
/* fallback to unlocalized description */
if (G_UNLIKELY (*locale == NULL))
{
thunar_apr_desktop_page_set_string (key_file,
G_KEY_FILE_DESKTOP_KEY_GENERIC_NAME,
desktop_page->description_text);
}
}
else if (widget == desktop_page->command_entry)
{
/* update the saved command */
g_free (desktop_page->command_text);
desktop_page->command_text = gtk_editable_get_chars (GTK_EDITABLE (widget), 0, -1);
/* save the unlocalized command */
thunar_apr_desktop_page_set_string (key_file,
G_KEY_FILE_DESKTOP_KEY_EXEC,
desktop_page->command_text);
}
else if (widget == desktop_page->path_entry)
{
/* update the saved command */
g_free (desktop_page->path_text);
desktop_page->path_text = gtk_editable_get_chars (GTK_EDITABLE (widget), 0, -1);
/* save the unlocalized command */
thunar_apr_desktop_page_set_string (key_file,
G_KEY_FILE_DESKTOP_KEY_PATH,
desktop_page->path_text);
}
else if (widget == desktop_page->url_entry)
{
/* update the saved URL */
g_free (desktop_page->url_text);
desktop_page->url_text = gtk_editable_get_chars (GTK_EDITABLE (widget), 0, -1);
/* save the unlocalized url */
thunar_apr_desktop_page_set_string (key_file,
G_KEY_FILE_DESKTOP_KEY_URL,
desktop_page->url_text);
}
else if (widget == desktop_page->comment_entry)
{
/* update the saved comment text */
g_free (desktop_page->comment_text);
desktop_page->comment_text = gtk_editable_get_chars (GTK_EDITABLE (widget), 0, -1);
/* save the new comment (localized if required) */
for (locale = g_get_language_names (); *locale != NULL; ++locale)
{
key = g_strdup_printf (G_KEY_FILE_DESKTOP_KEY_COMMENT "[%s]", *locale);
if (g_key_file_has_key (key_file, G_KEY_FILE_DESKTOP_GROUP, key, NULL))
{
thunar_apr_desktop_page_set_string (key_file, key, desktop_page->comment_text);
g_free (key);
break;
}
g_free (key);
}
/* fallback to unlocalized comment */
if (G_UNLIKELY (*locale == NULL))
thunar_apr_desktop_page_set_string (key_file,
G_KEY_FILE_DESKTOP_KEY_COMMENT,
desktop_page->comment_text);
}
else if (widget == desktop_page->snotify_button)
{
g_key_file_set_boolean (key_file,
G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY,
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
}
else if (widget == desktop_page->terminal_button)
{
g_key_file_set_boolean (key_file,
G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_TERMINAL,
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
}
else
{
g_assert_not_reached ();
}
}
static void
thunar_apr_desktop_page_activated (GtkWidget *entry,
ThunarAprDesktopPage *desktop_page)
{
g_return_if_fail (GTK_IS_ENTRY (entry));
g_return_if_fail (THUNAR_APR_IS_DESKTOP_PAGE (desktop_page));
/* check if the entry is editable, and if so, save the file */
if (gtk_editable_get_editable (GTK_EDITABLE (entry)))
thunar_apr_desktop_page_save (desktop_page, entry);
}
static gboolean
thunar_apr_desktop_page_focus_out_event (GtkWidget *entry,
GdkEventFocus *event,
ThunarAprDesktopPage *desktop_page)
{
g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE);
g_return_val_if_fail (THUNAR_APR_IS_DESKTOP_PAGE (desktop_page), FALSE);
/* check if the entry is editable, and if so, save the file */
if (gtk_editable_get_editable (GTK_EDITABLE (entry)))
thunar_apr_desktop_page_save (desktop_page, entry);
return FALSE;
}
static void
thunar_apr_desktop_page_toggled (GtkWidget *button,
ThunarAprDesktopPage *desktop_page)
{
g_return_if_fail (GTK_IS_TOGGLE_BUTTON (button));
g_return_if_fail (THUNAR_APR_IS_DESKTOP_PAGE (desktop_page));
/* save the file */
thunar_apr_desktop_page_save (desktop_page, button);
}
/**
* Allowed state:
*
* +-----+-----+
* |EXE |SAFE |
* +=====+=====+
* |TRUE |TRUE | Allowed to launch
* +-----+-----+
* |TRUE |FALSE| Ask before launch
* +-----+-----+
* |FALSE|FALSE| Not recognized as .desktop
* +-----+-----+
**/
static void
thunar_apr_desktop_page_program_toggled (GtkWidget *button,
ThunarAprDesktopPage *desktop_page)
{
GError *error = NULL;
GFile *gfile;
gboolean executable;
gboolean trusted;
g_return_if_fail (button == desktop_page->program_button);
g_return_if_fail (GTK_IS_TOGGLE_BUTTON (button));
g_return_if_fail (THUNAR_APR_IS_DESKTOP_PAGE (desktop_page));
g_return_if_fail (THUNARX_IS_FILE_INFO (THUNAR_APR_ABSTRACT_PAGE (desktop_page)->file));
gfile = thunarx_file_info_get_location (THUNAR_APR_ABSTRACT_PAGE (desktop_page)->file);
executable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (desktop_page->program_button));
set_executable (gfile, executable, &error);
g_object_unref (gfile);
if (error != NULL)
{
g_warning ("Error while setting execution flag : %s", error->message);
g_error_free (error);
return;
}
trusted = (desktop_page->trusted_button != NULL) ? gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (desktop_page->trusted_button)) : FALSE;
/* if the executable flag is unset, that will as well unset the trusted flag */
if (!executable && trusted)
if (desktop_page->trusted_button != NULL)
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (desktop_page->trusted_button), FALSE);
}
static void
thunar_apr_desktop_page_trusted_toggled (GtkWidget *button,
ThunarAprDesktopPage *desktop_page)
{
GError *error = NULL;
GFile *gfile;
gboolean executable;
gboolean trusted;
g_return_if_fail (button == desktop_page->trusted_button);
g_return_if_fail (GTK_IS_TOGGLE_BUTTON (button));
g_return_if_fail (THUNAR_APR_IS_DESKTOP_PAGE (desktop_page));
g_return_if_fail (THUNARX_IS_FILE_INFO (THUNAR_APR_ABSTRACT_PAGE (desktop_page)->file));
gfile = thunarx_file_info_get_location (THUNAR_APR_ABSTRACT_PAGE (desktop_page)->file);
trusted = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (desktop_page->trusted_button));
xfce_g_file_set_trusted (gfile, trusted, NULL, &error);
g_object_unref (gfile);
if (error != NULL)
{
g_warning ("Error while setting safety flag : %s", error->message);
g_error_free (error);
return;
}
executable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (desktop_page->program_button));
/* setting the trusted flag automatically sets the execute flag */
if (!executable && trusted)
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (desktop_page->program_button), TRUE);
}