Tree of tag thunar-4.20.7 (commit df0e054) from https://gitlab.xfce.org/xfce/thunar. Upstream history is not included here to keep the repository small.
1474 lines
53 KiB
C
1474 lines
53 KiB
C
/* vi:set et ai sw=2 sts=2 ts=2: */
|
|
/*-
|
|
* Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
|
|
* Copyright (c) 2009 Jannis Pohlmann <jannis@xfce.org>
|
|
* Copyright (c) 2012 Nick Schermer <nick@xfce.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program 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 General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program; 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_MEMORY_H
|
|
#include <memory.h>
|
|
#endif
|
|
#ifdef HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "thunar/thunar-dialogs.h"
|
|
#include "thunar/thunar-enum-types.h"
|
|
#include "thunar/thunar-gobject-extensions.h"
|
|
#include "thunar/thunar-gtk-extensions.h"
|
|
#include "thunar/thunar-io-jobs.h"
|
|
#include "thunar/thunar-pango-extensions.h"
|
|
#include "thunar/thunar-permissions-chooser.h"
|
|
#include "thunar/thunar-preferences.h"
|
|
#include "thunar/thunar-private.h"
|
|
#include "thunar/thunar-user.h"
|
|
|
|
#include <exo/exo.h>
|
|
#include <libxfce4ui/libxfce4ui.h>
|
|
|
|
|
|
|
|
/* Use native strlcpy() if available */
|
|
#if defined(HAVE_STRLCPY)
|
|
#define g_strlcpy(dst, src, size) (strlcpy ((dst), (src), (size)))
|
|
#endif
|
|
|
|
|
|
|
|
/* Property identifiers */
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_FILES,
|
|
PROP_MUTABLE,
|
|
};
|
|
|
|
/* Column identifiers for the group combo box */
|
|
enum
|
|
{
|
|
THUNAR_PERMISSIONS_STORE_COLUMN_NAME,
|
|
THUNAR_PERMISSIONS_STORE_COLUMN_GID,
|
|
THUNAR_PERMISSIONS_STORE_N_COLUMNS,
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
thunar_permissions_chooser_finalize (GObject *object);
|
|
static void
|
|
thunar_permissions_chooser_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
static void
|
|
thunar_permissions_chooser_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static gint
|
|
thunar_permissions_chooser_ask_recursive (ThunarPermissionsChooser *chooser);
|
|
static void
|
|
thunar_permissions_chooser_change_group (ThunarPermissionsChooser *chooser,
|
|
guint32 gid);
|
|
static gboolean
|
|
thunar_permissions_chooser_change_mode (ThunarPermissionsChooser *chooser,
|
|
ThunarFileMode dir_mask,
|
|
ThunarFileMode dir_mode,
|
|
ThunarFileMode file_mask,
|
|
ThunarFileMode file_mode);
|
|
static void
|
|
thunar_permissions_chooser_access_changed (ThunarPermissionsChooser *chooser,
|
|
GtkWidget *combo);
|
|
static void
|
|
thunar_permissions_chooser_file_changed (ThunarPermissionsChooser *chooser);
|
|
static void
|
|
thunar_permissions_chooser_group_changed (ThunarPermissionsChooser *chooser,
|
|
GtkWidget *combo);
|
|
static void
|
|
thunar_permissions_chooser_program_toggled (ThunarPermissionsChooser *chooser,
|
|
GtkWidget *button);
|
|
static void
|
|
thunar_permissions_chooser_fixperm_clicked (ThunarPermissionsChooser *chooser,
|
|
GtkWidget *button);
|
|
static ThunarJobResponse
|
|
thunar_permissions_chooser_job_ask (ThunarPermissionsChooser *chooser,
|
|
const gchar *message,
|
|
ThunarJobResponse choices,
|
|
ThunarJob *job);
|
|
static void
|
|
thunar_permissions_chooser_job_cancel (ThunarPermissionsChooser *chooser);
|
|
static void
|
|
thunar_permissions_chooser_job_error (ThunarPermissionsChooser *chooser,
|
|
GError *error,
|
|
ThunarJob *job);
|
|
static void
|
|
thunar_permissions_chooser_job_finished (ThunarPermissionsChooser *chooser,
|
|
ThunarJob *job);
|
|
static void
|
|
thunar_permissions_chooser_job_percent (ThunarPermissionsChooser *chooser,
|
|
gdouble percent,
|
|
ThunarJob *job);
|
|
static void
|
|
thunar_permissions_chooser_job_start (ThunarPermissionsChooser *chooser,
|
|
ThunarJob *job,
|
|
gboolean recursive);
|
|
static gboolean
|
|
thunar_permissions_chooser_row_separator (GtkTreeModel *model,
|
|
GtkTreeIter *iter,
|
|
gpointer data);
|
|
static GList *
|
|
thunar_permissions_chooser_get_files (ThunarPermissionsChooser *chooser);
|
|
static void
|
|
thunar_permissions_chooser_set_files (ThunarPermissionsChooser *chooser,
|
|
GList *files);
|
|
|
|
|
|
struct _ThunarPermissionsChooserClass
|
|
{
|
|
GtkVBoxClass __parent__;
|
|
};
|
|
|
|
struct _ThunarPermissionsChooser
|
|
{
|
|
GtkVBox __parent__;
|
|
|
|
GList *files;
|
|
|
|
/* the main grid widget, which contains everything but the job control stuff */
|
|
GtkWidget *grid;
|
|
|
|
GtkWidget *user_label;
|
|
GtkWidget *group_combo;
|
|
GtkWidget *access_combos[3];
|
|
GtkWidget *program_button;
|
|
GtkWidget *fixperm_label;
|
|
GtkWidget *fixperm_button;
|
|
|
|
/* job control stuff */
|
|
ThunarJob *job;
|
|
GtkWidget *job_progress;
|
|
};
|
|
|
|
|
|
|
|
G_DEFINE_TYPE (ThunarPermissionsChooser, thunar_permissions_chooser, GTK_TYPE_BOX)
|
|
|
|
|
|
|
|
static void
|
|
thunar_permissions_chooser_class_init (ThunarPermissionsChooserClass *klass)
|
|
{
|
|
GObjectClass *gobject_class;
|
|
|
|
gobject_class = G_OBJECT_CLASS (klass);
|
|
gobject_class->finalize = thunar_permissions_chooser_finalize;
|
|
gobject_class->get_property = thunar_permissions_chooser_get_property;
|
|
gobject_class->set_property = thunar_permissions_chooser_set_property;
|
|
|
|
/**
|
|
* ThunarPermissionsChooser:file:
|
|
*
|
|
* The #ThunarFile whose permissions will be edited/viewed.
|
|
**/
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_FILES,
|
|
g_param_spec_boxed ("files", "files", "files",
|
|
THUNARX_TYPE_FILE_INFO_LIST,
|
|
EXO_PARAM_READWRITE));
|
|
|
|
/**
|
|
* ThunarPermissionsChooser:mutable:
|
|
*
|
|
* Whether the current #ThunarFile<!---->s permissions are
|
|
* mutable.
|
|
**/
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_MUTABLE,
|
|
g_param_spec_boolean ("mutable",
|
|
"mutable",
|
|
"mutable",
|
|
FALSE,
|
|
EXO_PARAM_READABLE));
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_permissions_chooser_init (ThunarPermissionsChooser *chooser)
|
|
{
|
|
GtkCellRenderer *renderer_text;
|
|
GtkWidget *separator;
|
|
GtkWidget *button;
|
|
GtkWidget *label;
|
|
GtkWidget *image;
|
|
GtkWidget *hbox;
|
|
gint row = 0;
|
|
|
|
/* setup the chooser */
|
|
gtk_container_set_border_width (GTK_CONTAINER (chooser), 12);
|
|
|
|
gtk_orientable_set_orientation (GTK_ORIENTABLE (chooser), GTK_ORIENTATION_VERTICAL);
|
|
|
|
/* allocate the shared renderer for the various combo boxes */
|
|
renderer_text = gtk_cell_renderer_text_new ();
|
|
|
|
chooser->grid = gtk_grid_new ();
|
|
gtk_grid_set_column_spacing (GTK_GRID (chooser->grid), 12);
|
|
gtk_grid_set_row_spacing (GTK_GRID (chooser->grid), 6);
|
|
gtk_box_pack_start (GTK_BOX (chooser), chooser->grid, TRUE, TRUE, 0);
|
|
gtk_widget_show (chooser->grid);
|
|
|
|
label = gtk_label_new (_("Owner:"));
|
|
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
|
|
gtk_label_set_attributes (GTK_LABEL (label), thunar_pango_attr_list_bold ());
|
|
gtk_grid_attach (GTK_GRID (chooser->grid), label, 0, row, 1, 1);
|
|
gtk_widget_show (label);
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
|
gtk_widget_set_hexpand (hbox, TRUE);
|
|
gtk_grid_attach (GTK_GRID (chooser->grid), hbox, 1, row, 1, 1);
|
|
gtk_widget_show (hbox);
|
|
|
|
chooser->user_label = gtk_label_new (_("Unknown"));
|
|
gtk_label_set_xalign (GTK_LABEL (chooser->user_label), 0.0f);
|
|
gtk_box_pack_start (GTK_BOX (hbox), chooser->user_label, TRUE, TRUE, 0);
|
|
thunar_gtk_label_set_a11y_relation (GTK_LABEL (label), chooser->user_label);
|
|
gtk_widget_show (chooser->user_label);
|
|
|
|
row += 1;
|
|
|
|
label = gtk_label_new_with_mnemonic (_("_Access:"));
|
|
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
|
|
gtk_label_set_attributes (GTK_LABEL (label), thunar_pango_attr_list_bold ());
|
|
gtk_grid_attach (GTK_GRID (chooser->grid), label, 0, row, 1, 1);
|
|
gtk_widget_show (label);
|
|
|
|
chooser->access_combos[2] = gtk_combo_box_new ();
|
|
gtk_label_set_mnemonic_widget (GTK_LABEL (label), chooser->access_combos[2]);
|
|
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (chooser->access_combos[2]), renderer_text, TRUE);
|
|
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (chooser->access_combos[2]), renderer_text, "text", 0);
|
|
g_object_bind_property (G_OBJECT (chooser), "mutable",
|
|
G_OBJECT (chooser->access_combos[2]), "sensitive",
|
|
G_BINDING_SYNC_CREATE);
|
|
g_signal_connect_swapped (G_OBJECT (chooser->access_combos[2]), "changed", G_CALLBACK (thunar_permissions_chooser_access_changed), chooser);
|
|
gtk_widget_set_hexpand (chooser->access_combos[2], TRUE);
|
|
gtk_grid_attach (GTK_GRID (chooser->grid), chooser->access_combos[2], 1, row, 1, 1);
|
|
thunar_gtk_label_set_a11y_relation (GTK_LABEL (label), chooser->access_combos[2]);
|
|
gtk_widget_show (chooser->access_combos[2]);
|
|
|
|
row += 1;
|
|
|
|
separator = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
|
g_object_set (separator, "margin", 4, NULL);
|
|
gtk_grid_attach (GTK_GRID (chooser->grid), separator, 0, row, 2, 1);
|
|
gtk_widget_show (separator);
|
|
|
|
row += 1;
|
|
|
|
label = gtk_label_new_with_mnemonic (_("Gro_up:"));
|
|
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
|
|
gtk_label_set_attributes (GTK_LABEL (label), thunar_pango_attr_list_bold ());
|
|
gtk_grid_attach (GTK_GRID (chooser->grid), label, 0, row, 1, 1);
|
|
gtk_widget_show (label);
|
|
|
|
chooser->group_combo = gtk_combo_box_new ();
|
|
gtk_label_set_mnemonic_widget (GTK_LABEL (label), chooser->group_combo);
|
|
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (chooser->group_combo), renderer_text, TRUE);
|
|
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (chooser->group_combo), renderer_text, "text", THUNAR_PERMISSIONS_STORE_COLUMN_NAME);
|
|
gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (chooser->group_combo), thunar_permissions_chooser_row_separator, NULL, NULL);
|
|
g_object_bind_property (G_OBJECT (chooser), "mutable",
|
|
G_OBJECT (chooser->group_combo), "sensitive",
|
|
G_BINDING_SYNC_CREATE);
|
|
g_signal_connect_swapped (G_OBJECT (chooser->group_combo), "changed", G_CALLBACK (thunar_permissions_chooser_group_changed), chooser);
|
|
gtk_widget_set_hexpand (chooser->group_combo, TRUE);
|
|
gtk_grid_attach (GTK_GRID (chooser->grid), chooser->group_combo, 1, row, 1, 1);
|
|
thunar_gtk_label_set_a11y_relation (GTK_LABEL (label), chooser->group_combo);
|
|
gtk_widget_show (chooser->group_combo);
|
|
|
|
row += 1;
|
|
|
|
label = gtk_label_new_with_mnemonic (_("Acc_ess:"));
|
|
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
|
|
gtk_label_set_attributes (GTK_LABEL (label), thunar_pango_attr_list_bold ());
|
|
gtk_grid_attach (GTK_GRID (chooser->grid), label, 0, row, 1, 1);
|
|
gtk_widget_show (label);
|
|
|
|
chooser->access_combos[1] = gtk_combo_box_new ();
|
|
gtk_label_set_mnemonic_widget (GTK_LABEL (label), chooser->access_combos[1]);
|
|
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (chooser->access_combos[1]), renderer_text, TRUE);
|
|
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (chooser->access_combos[1]), renderer_text, "text", 0);
|
|
g_object_bind_property (G_OBJECT (chooser), "mutable",
|
|
G_OBJECT (chooser->access_combos[1]), "sensitive",
|
|
G_BINDING_SYNC_CREATE);
|
|
g_signal_connect_swapped (G_OBJECT (chooser->access_combos[1]), "changed", G_CALLBACK (thunar_permissions_chooser_access_changed), chooser);
|
|
gtk_widget_set_hexpand (chooser->access_combos[1], TRUE);
|
|
gtk_grid_attach (GTK_GRID (chooser->grid), chooser->access_combos[1], 1, row, 1, 1);
|
|
thunar_gtk_label_set_a11y_relation (GTK_LABEL (label), chooser->access_combos[1]);
|
|
gtk_widget_show (chooser->access_combos[1]);
|
|
|
|
row += 1;
|
|
|
|
separator = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
|
g_object_set (separator, "margin", 4, NULL);
|
|
gtk_grid_attach (GTK_GRID (chooser->grid), separator, 0, row, 2, 1);
|
|
gtk_widget_show (separator);
|
|
|
|
row += 1;
|
|
|
|
label = gtk_label_new (_("Others"));
|
|
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
|
|
gtk_label_set_attributes (GTK_LABEL (label), thunar_pango_attr_list_bold ());
|
|
gtk_grid_attach (GTK_GRID (chooser->grid), label, 0, row, 1, 1);
|
|
gtk_widget_show (label);
|
|
|
|
row += 1;
|
|
|
|
label = gtk_label_new_with_mnemonic (_("Acce_ss:"));
|
|
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
|
|
gtk_label_set_attributes (GTK_LABEL (label), thunar_pango_attr_list_bold ());
|
|
gtk_grid_attach (GTK_GRID (chooser->grid), label, 0, row, 1, 1);
|
|
gtk_widget_show (label);
|
|
|
|
chooser->access_combos[0] = gtk_combo_box_new ();
|
|
gtk_label_set_mnemonic_widget (GTK_LABEL (label), chooser->access_combos[0]);
|
|
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (chooser->access_combos[0]), renderer_text, TRUE);
|
|
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (chooser->access_combos[0]), renderer_text, "text", 0);
|
|
g_object_bind_property (G_OBJECT (chooser), "mutable",
|
|
G_OBJECT (chooser->access_combos[0]), "sensitive",
|
|
G_BINDING_SYNC_CREATE);
|
|
g_signal_connect_swapped (G_OBJECT (chooser->access_combos[0]), "changed", G_CALLBACK (thunar_permissions_chooser_access_changed), chooser);
|
|
gtk_widget_set_hexpand (chooser->access_combos[0], TRUE);
|
|
gtk_grid_attach (GTK_GRID (chooser->grid), chooser->access_combos[0], 1, row, 1, 1);
|
|
thunar_gtk_label_set_a11y_relation (GTK_LABEL (label), chooser->access_combos[0]);
|
|
gtk_widget_show (chooser->access_combos[0]);
|
|
|
|
row += 1;
|
|
|
|
separator = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
|
g_object_set (separator, "margin", 4, NULL);
|
|
gtk_grid_attach (GTK_GRID (chooser->grid), separator, 0, row, 2, 1);
|
|
gtk_widget_show (separator);
|
|
|
|
row += 1;
|
|
|
|
label = gtk_label_new (_("Program:"));
|
|
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
|
|
gtk_label_set_attributes (GTK_LABEL (label), thunar_pango_attr_list_bold ());
|
|
gtk_grid_attach (GTK_GRID (chooser->grid), label, 0, row, 1, 1);
|
|
gtk_widget_show (label);
|
|
|
|
chooser->program_button = gtk_check_button_new_with_mnemonic (_("Allow this file to _run as a program"));
|
|
g_object_bind_property (G_OBJECT (chooser->program_button), "visible",
|
|
G_OBJECT (label), "visible",
|
|
G_BINDING_SYNC_CREATE);
|
|
g_object_bind_property (G_OBJECT (chooser), "mutable",
|
|
G_OBJECT (chooser->program_button), "sensitive",
|
|
G_BINDING_SYNC_CREATE);
|
|
g_signal_connect_swapped (G_OBJECT (chooser->program_button), "toggled", G_CALLBACK (thunar_permissions_chooser_program_toggled), chooser);
|
|
gtk_grid_attach (GTK_GRID (chooser->grid), chooser->program_button, 1, row, 1, 1);
|
|
thunar_gtk_label_set_a11y_relation (GTK_LABEL (label), chooser->program_button);
|
|
gtk_widget_show (chooser->program_button);
|
|
|
|
row += 1;
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
|
g_object_bind_property (G_OBJECT (chooser), "mutable",
|
|
G_OBJECT (hbox), "sensitive",
|
|
G_BINDING_SYNC_CREATE);
|
|
g_object_bind_property (G_OBJECT (chooser->program_button), "visible",
|
|
G_OBJECT (hbox), "visible",
|
|
G_BINDING_SYNC_CREATE);
|
|
gtk_grid_attach (GTK_GRID (chooser->grid), hbox, 1, row, 1, 1);
|
|
gtk_widget_show (hbox);
|
|
|
|
image = gtk_image_new_from_icon_name ("dialog-warning", GTK_ICON_SIZE_LARGE_TOOLBAR);
|
|
gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
|
|
gtk_widget_show (image);
|
|
|
|
label = gtk_label_new (_("Allowing untrusted programs to run presents a security risk to your system."));
|
|
gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
|
|
gtk_label_set_attributes (GTK_LABEL (label), thunar_pango_attr_list_small_italic ());
|
|
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
|
|
gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
|
|
gtk_widget_show (label);
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
|
g_object_bind_property (G_OBJECT (chooser), "mutable",
|
|
G_OBJECT (hbox), "sensitive",
|
|
G_BINDING_SYNC_CREATE);
|
|
gtk_grid_attach (GTK_GRID (chooser->grid), hbox, 1, row, 1, 1);
|
|
gtk_widget_show (hbox);
|
|
|
|
image = gtk_image_new_from_icon_name ("dialog-warning", GTK_ICON_SIZE_LARGE_TOOLBAR);
|
|
gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
|
|
gtk_widget_show (image);
|
|
|
|
chooser->fixperm_label = gtk_label_new (_("The folder permissions are inconsistent, you may not be able to work with files in this folder."));
|
|
gtk_label_set_xalign (GTK_LABEL (chooser->fixperm_label), 0.0f);
|
|
gtk_label_set_attributes (GTK_LABEL (chooser->fixperm_label), thunar_pango_attr_list_small_italic ());
|
|
gtk_label_set_line_wrap (GTK_LABEL (chooser->fixperm_label), TRUE);
|
|
g_object_bind_property (G_OBJECT (chooser->fixperm_label), "visible",
|
|
G_OBJECT (hbox), "visible",
|
|
G_BINDING_SYNC_CREATE);
|
|
gtk_box_pack_start (GTK_BOX (hbox), chooser->fixperm_label, TRUE, TRUE, 0);
|
|
gtk_widget_show (chooser->fixperm_label);
|
|
|
|
row += 1;
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
|
gtk_grid_attach (GTK_GRID (chooser->grid), hbox, 1, row, 1, 1);
|
|
gtk_widget_show (hbox);
|
|
|
|
chooser->fixperm_button = gtk_button_new_with_mnemonic (_("Correct _folder permissions..."));
|
|
gtk_widget_set_tooltip_text (chooser->fixperm_button, _("Click here to automatically fix the folder permissions."));
|
|
g_signal_connect_swapped (G_OBJECT (chooser->fixperm_button), "clicked", G_CALLBACK (thunar_permissions_chooser_fixperm_clicked), chooser);
|
|
g_object_bind_property (G_OBJECT (chooser->fixperm_button), "visible",
|
|
G_OBJECT (hbox), "visible",
|
|
G_BINDING_SYNC_CREATE);
|
|
gtk_box_pack_end (GTK_BOX (hbox), chooser->fixperm_button, FALSE, FALSE, 0);
|
|
gtk_widget_show (chooser->fixperm_button);
|
|
|
|
/* the job control stuff */
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
|
gtk_box_pack_start (GTK_BOX (chooser), hbox, FALSE, FALSE, 0);
|
|
|
|
chooser->job_progress = gtk_progress_bar_new ();
|
|
gtk_progress_bar_set_text (GTK_PROGRESS_BAR (chooser->job_progress), _("Please wait..."));
|
|
g_object_bind_property (G_OBJECT (chooser->job_progress), "visible",
|
|
G_OBJECT (hbox), "visible",
|
|
G_BINDING_SYNC_CREATE);
|
|
gtk_box_pack_start (GTK_BOX (hbox), chooser->job_progress, TRUE, TRUE, 0);
|
|
|
|
button = gtk_button_new ();
|
|
gtk_widget_set_tooltip_text (button, _("Stop applying permissions recursively."));
|
|
g_signal_connect_swapped (G_OBJECT (button), "clicked", G_CALLBACK (thunar_permissions_chooser_job_cancel), chooser);
|
|
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
|
|
gtk_widget_show (button);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_permissions_chooser_finalize (GObject *object)
|
|
{
|
|
ThunarPermissionsChooser *chooser = THUNAR_PERMISSIONS_CHOOSER (object);
|
|
|
|
/* cancel any pending job */
|
|
if (G_UNLIKELY (chooser->job != NULL))
|
|
{
|
|
/* cancel the job (if not already done) */
|
|
exo_job_cancel (EXO_JOB (chooser->job));
|
|
|
|
/* disconnect from the job */
|
|
g_signal_handlers_disconnect_by_data (chooser->job, chooser);
|
|
g_object_unref (chooser->job);
|
|
chooser->job = NULL;
|
|
}
|
|
|
|
/* drop the reference on the file (if any) */
|
|
thunar_permissions_chooser_set_files (chooser, NULL);
|
|
|
|
(*G_OBJECT_CLASS (thunar_permissions_chooser_parent_class)->finalize) (object);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_permissions_chooser_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ThunarPermissionsChooser *chooser = THUNAR_PERMISSIONS_CHOOSER (object);
|
|
GList *lp;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_FILES:
|
|
g_value_set_boxed (value, thunar_permissions_chooser_get_files (chooser));
|
|
break;
|
|
|
|
case PROP_MUTABLE:
|
|
for (lp = chooser->files; lp != NULL; lp = lp->next)
|
|
if (!thunar_file_is_chmodable (THUNAR_FILE (lp->data)))
|
|
break;
|
|
|
|
g_value_set_boolean (value, (chooser->files != NULL) && (lp == NULL));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_permissions_chooser_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ThunarPermissionsChooser *chooser = THUNAR_PERMISSIONS_CHOOSER (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_FILES:
|
|
thunar_permissions_chooser_set_files (chooser, g_value_get_boxed (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_permissions_chooser_ask_recursive (ThunarPermissionsChooser *chooser)
|
|
{
|
|
ThunarRecursivePermissionsMode mode;
|
|
ThunarPreferences *preferences;
|
|
GtkWidget *toplevel;
|
|
GtkWidget *dialog;
|
|
GtkWidget *button;
|
|
GtkWidget *label;
|
|
GtkWidget *image;
|
|
GtkWidget *hbox;
|
|
GtkWidget *vbox;
|
|
gint response;
|
|
|
|
/* grab a reference on the preferences */
|
|
preferences = thunar_preferences_get ();
|
|
|
|
/* determine the current recursive permissions mode */
|
|
g_object_get (G_OBJECT (preferences), "misc-recursive-permissions", &mode, NULL);
|
|
|
|
/* check if we should ask the user first */
|
|
if (G_UNLIKELY (mode == THUNAR_RECURSIVE_PERMISSIONS_ASK))
|
|
{
|
|
/* determine the toplevel widget for the chooser */
|
|
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (chooser));
|
|
|
|
/* allocate the question dialog */
|
|
dialog = gtk_dialog_new_with_buttons (_("Question"), GTK_WINDOW (toplevel),
|
|
GTK_DIALOG_DESTROY_WITH_PARENT
|
|
| GTK_DIALOG_MODAL,
|
|
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
|
_("_No"), GTK_RESPONSE_NO,
|
|
_("_Yes"), GTK_RESPONSE_YES,
|
|
NULL);
|
|
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
|
gtk_container_set_border_width (GTK_CONTAINER (hbox), 8);
|
|
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), hbox, TRUE, TRUE, 0);
|
|
gtk_widget_show (hbox);
|
|
|
|
image = gtk_image_new_from_icon_name ("dialog-question", GTK_ICON_SIZE_DIALOG);
|
|
gtk_widget_set_halign (image, GTK_ALIGN_CENTER);
|
|
gtk_widget_set_valign (image, GTK_ALIGN_START);
|
|
gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
|
|
gtk_widget_show (image);
|
|
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
|
|
gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
|
|
gtk_widget_show (vbox);
|
|
|
|
label = gtk_label_new (_("Apply recursively?"));
|
|
gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
|
|
gtk_label_set_attributes (GTK_LABEL (label), thunar_pango_attr_list_big_bold ());
|
|
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
|
|
gtk_widget_show (label);
|
|
|
|
label = gtk_label_new (_("Do you want to apply your changes recursively to\nall files and subfolders below the selected folder?"));
|
|
gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
|
|
gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
|
|
gtk_widget_show (label);
|
|
|
|
button = gtk_check_button_new_with_mnemonic (_("Do _not ask me again"));
|
|
gtk_widget_set_tooltip_text (button, _("If you select this option your choice will be remembered and you won't be asked "
|
|
"again. You can use the preferences dialog to alter your choice afterwards."));
|
|
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
|
|
gtk_widget_show (button);
|
|
|
|
/* run the dialog and save the selected option (if requested) */
|
|
response = gtk_dialog_run (GTK_DIALOG (dialog));
|
|
switch (response)
|
|
{
|
|
case GTK_RESPONSE_YES:
|
|
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
|
|
g_object_set (G_OBJECT (preferences), "misc-recursive-permissions", THUNAR_RECURSIVE_PERMISSIONS_ALWAYS, NULL);
|
|
break;
|
|
|
|
case GTK_RESPONSE_NO:
|
|
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
|
|
g_object_set (G_OBJECT (preferences), "misc-recursive-permissions", THUNAR_RECURSIVE_PERMISSIONS_NEVER, NULL);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* destroy the dialog resources */
|
|
gtk_widget_destroy (dialog);
|
|
}
|
|
else if (mode == THUNAR_RECURSIVE_PERMISSIONS_ALWAYS)
|
|
{
|
|
response = GTK_RESPONSE_YES;
|
|
}
|
|
else
|
|
{
|
|
response = GTK_RESPONSE_NO;
|
|
}
|
|
|
|
/* release the reference on the preferences */
|
|
g_object_unref (G_OBJECT (preferences));
|
|
|
|
return response;
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_permissions_chooser_has_directory (ThunarPermissionsChooser *chooser)
|
|
{
|
|
GList *lp;
|
|
|
|
for (lp = chooser->files; lp != NULL; lp = lp->next)
|
|
if (thunar_file_is_directory (THUNAR_FILE (lp->data)))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_permissions_chooser_is_fixable_directory (ThunarFile *file)
|
|
{
|
|
ThunarFileMode mode;
|
|
|
|
_thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
|
|
|
|
if (!thunar_file_is_directory (file)
|
|
|| !thunar_file_is_chmodable (file))
|
|
return FALSE;
|
|
|
|
mode = thunar_file_get_mode (file);
|
|
|
|
return ((mode & 0111) != ((mode >> 2) & 0111));
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_permissions_chooser_has_fixable_directory (ThunarPermissionsChooser *chooser)
|
|
{
|
|
GList *lp;
|
|
|
|
for (lp = chooser->files; lp != NULL; lp = lp->next)
|
|
if (thunar_permissions_chooser_is_fixable_directory (THUNAR_FILE (lp->data)))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
/* free returned list with g_list_free_full (file_list, g_object_unref); */
|
|
static GList *
|
|
thunar_permissions_chooser_get_file_list (ThunarPermissionsChooser *chooser)
|
|
{
|
|
GList *file_list = NULL;
|
|
GList *lp;
|
|
GFile *gfile;
|
|
|
|
for (lp = chooser->files; lp != NULL; lp = lp->next)
|
|
{
|
|
gfile = thunar_file_get_file (THUNAR_FILE (lp->data));
|
|
_thunar_assert (G_IS_FILE (gfile));
|
|
file_list = g_list_prepend (file_list, g_object_ref (G_OBJECT (gfile)));
|
|
}
|
|
|
|
return file_list;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_permissions_chooser_change_group (ThunarPermissionsChooser *chooser,
|
|
guint32 gid)
|
|
{
|
|
ThunarJob *job;
|
|
gboolean recursive = FALSE;
|
|
gint response;
|
|
GList *file_list;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_PERMISSIONS_CHOOSER (chooser));
|
|
_thunar_return_if_fail (chooser->files != NULL);
|
|
|
|
/* check if we should operate recursively */
|
|
if (thunar_permissions_chooser_has_directory (chooser))
|
|
{
|
|
response = thunar_permissions_chooser_ask_recursive (chooser);
|
|
switch (response)
|
|
{
|
|
case GTK_RESPONSE_YES:
|
|
recursive = TRUE;
|
|
break;
|
|
|
|
case GTK_RESPONSE_NO:
|
|
recursive = FALSE;
|
|
break;
|
|
|
|
default: /* cancelled by the user */
|
|
thunar_permissions_chooser_file_changed (chooser);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* try to allocate the new job */
|
|
file_list = thunar_permissions_chooser_get_file_list (chooser);
|
|
job = thunar_io_jobs_change_group (file_list, gid, recursive);
|
|
exo_job_launch (EXO_JOB (job));
|
|
thunar_permissions_chooser_job_start (chooser, job, recursive);
|
|
g_list_free_full (file_list, g_object_unref);
|
|
g_object_unref (job);
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_permissions_chooser_change_mode (ThunarPermissionsChooser *chooser,
|
|
ThunarFileMode dir_mask,
|
|
ThunarFileMode dir_mode,
|
|
ThunarFileMode file_mask,
|
|
ThunarFileMode file_mode)
|
|
{
|
|
ThunarJob *job;
|
|
gboolean recursive = FALSE;
|
|
gint response;
|
|
GList *file_list;
|
|
|
|
_thunar_return_val_if_fail (THUNAR_IS_PERMISSIONS_CHOOSER (chooser), FALSE);
|
|
_thunar_return_val_if_fail (chooser->files != NULL, FALSE);
|
|
|
|
/* check if we should operate recursively */
|
|
if (thunar_permissions_chooser_has_directory (chooser))
|
|
{
|
|
response = thunar_permissions_chooser_ask_recursive (chooser);
|
|
switch (response)
|
|
{
|
|
case GTK_RESPONSE_YES:
|
|
recursive = TRUE;
|
|
break;
|
|
|
|
case GTK_RESPONSE_NO:
|
|
recursive = FALSE;
|
|
break;
|
|
|
|
default: /* cancelled by the user */
|
|
thunar_permissions_chooser_file_changed (chooser);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* try to allocate the new job */
|
|
file_list = thunar_permissions_chooser_get_file_list (chooser);
|
|
job = thunar_io_jobs_change_mode (file_list, dir_mask, dir_mode, file_mask, file_mode, recursive);
|
|
exo_job_launch (EXO_JOB (job));
|
|
thunar_permissions_chooser_job_start (chooser, job, recursive);
|
|
g_list_free_full (file_list, g_object_unref);
|
|
g_object_unref (job);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_permissions_chooser_access_changed (ThunarPermissionsChooser *chooser,
|
|
GtkWidget *combo)
|
|
{
|
|
ThunarFileMode file_mask;
|
|
ThunarFileMode file_mode;
|
|
ThunarFileMode dir_mask;
|
|
ThunarFileMode dir_mode;
|
|
guint n;
|
|
gint active_mode;
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_PERMISSIONS_CHOOSER (chooser));
|
|
_thunar_return_if_fail (GTK_IS_COMBO_BOX (combo));
|
|
|
|
/* leave if the active mode is varying */
|
|
active_mode = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
|
|
if (active_mode > 3)
|
|
return;
|
|
|
|
/* determine the new mode from the combo box */
|
|
for (n = 0; n < G_N_ELEMENTS (chooser->access_combos) && chooser->access_combos[n] != combo; ++n)
|
|
;
|
|
dir_mode = file_mode = (active_mode << 1) << (n * 3);
|
|
dir_mask = file_mask = 0006 << (n * 3);
|
|
|
|
/* keep exec bit in sync for folders */
|
|
if (thunar_permissions_chooser_has_directory (chooser))
|
|
{
|
|
/* if either read or write is, set exec as well, else unset exec as well */
|
|
if ((dir_mode & (0004 << (n * 3))) != 0)
|
|
dir_mode |= (0001 << (n * 3));
|
|
dir_mask = 0007 << (n * 3);
|
|
}
|
|
|
|
/* change the permissions */
|
|
if (thunar_permissions_chooser_change_mode (chooser, dir_mask, dir_mode, file_mask, file_mode))
|
|
{
|
|
/* for better feedback remove the varying item */
|
|
model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
|
|
if (gtk_tree_model_get_iter_from_string (model, &iter, "4"))
|
|
gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static gint
|
|
group_compare (gconstpointer group_a,
|
|
gconstpointer group_b,
|
|
gpointer group_primary)
|
|
{
|
|
guint32 group_primary_id;
|
|
guint32 group_a_id = thunar_group_get_id (THUNAR_GROUP (group_a));
|
|
guint32 group_b_id = thunar_group_get_id (THUNAR_GROUP (group_b));
|
|
|
|
/* check if the groups are equal */
|
|
if (group_a_id == group_b_id)
|
|
return 0;
|
|
|
|
/* the primary group is always sorted first */
|
|
if (group_primary != NULL)
|
|
{
|
|
group_primary_id = thunar_group_get_id (THUNAR_GROUP (group_primary));
|
|
if (group_a_id == group_primary_id)
|
|
return -1;
|
|
else if (group_b_id == group_primary_id)
|
|
return 1;
|
|
}
|
|
|
|
/* system groups (< 100) are always sorted last */
|
|
if (group_a_id < 100 && group_b_id >= 100)
|
|
return 1;
|
|
else if (group_b_id < 100 && group_a_id >= 100)
|
|
return -1;
|
|
|
|
/* otherwise just sort by name */
|
|
return g_ascii_strcasecmp (thunar_group_get_name (THUNAR_GROUP (group_a)),
|
|
thunar_group_get_name (THUNAR_GROUP (group_b)));
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_permissions_chooser_file_changed (ThunarPermissionsChooser *chooser)
|
|
{
|
|
ThunarFile *file;
|
|
ThunarUserManager *user_manager;
|
|
ThunarFileMode mode = 0;
|
|
ThunarGroup *group = NULL;
|
|
ThunarUser *user = NULL;
|
|
GtkListStore *store;
|
|
GtkTreeIter iter;
|
|
const gchar *user_name;
|
|
const gchar *real_name;
|
|
GList *groups = NULL;
|
|
GList *lp;
|
|
gchar buffer[1024];
|
|
guint n;
|
|
guint n_files = 0;
|
|
gint modes[3] = {
|
|
0,
|
|
};
|
|
gint file_modes[3];
|
|
GtkListStore *access_store;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_PERMISSIONS_CHOOSER (chooser));
|
|
|
|
/* compare multiple files */
|
|
for (lp = chooser->files; lp != NULL; lp = lp->next)
|
|
{
|
|
file = THUNAR_FILE (lp->data);
|
|
|
|
/* transform the file modes in r/w/r+w for each group */
|
|
mode = thunar_file_get_mode (file);
|
|
for (n = 0; n < 3; n++)
|
|
file_modes[n] = ((mode >> (n * 3)) & 0007) >> 1;
|
|
|
|
if (n_files == 0)
|
|
{
|
|
/* get information of the first file */
|
|
user = thunar_file_get_user (file);
|
|
group = thunar_file_get_group (file);
|
|
|
|
for (n = 0; n < 3; n++)
|
|
modes[n] = file_modes[n];
|
|
}
|
|
else
|
|
{
|
|
/* unset the file info if it is different from the other files */
|
|
if (user != NULL && user != thunar_file_get_user (file))
|
|
user = NULL;
|
|
|
|
if (group != NULL && group != thunar_file_get_group (file))
|
|
group = NULL;
|
|
|
|
for (n = 0; n < 3; n++)
|
|
if (file_modes[n] != modes[n])
|
|
modes[n] = 4;
|
|
}
|
|
|
|
n_files++;
|
|
}
|
|
|
|
file = THUNAR_FILE (chooser->files->data);
|
|
|
|
/* allocate a new store for the group combo box */
|
|
g_signal_handlers_block_by_func (G_OBJECT (chooser->group_combo), thunar_permissions_chooser_group_changed, chooser);
|
|
store = gtk_list_store_new (THUNAR_PERMISSIONS_STORE_N_COLUMNS, G_TYPE_STRING, G_TYPE_UINT);
|
|
gtk_combo_box_set_model (GTK_COMBO_BOX (chooser->group_combo), GTK_TREE_MODEL (store));
|
|
|
|
/* determine the owner of the new file */
|
|
if (G_LIKELY (user != NULL))
|
|
{
|
|
/* determine sane display name for the owner */
|
|
user_name = thunar_user_get_name (user);
|
|
real_name = thunar_user_get_real_name (user);
|
|
if (G_LIKELY (real_name != NULL))
|
|
g_snprintf (buffer, sizeof (buffer), "%s (%s)", real_name, user_name);
|
|
else
|
|
g_strlcpy (buffer, user_name, sizeof (buffer));
|
|
gtk_label_set_text (GTK_LABEL (chooser->user_label), buffer);
|
|
}
|
|
else
|
|
{
|
|
gtk_label_set_text (GTK_LABEL (chooser->user_label),
|
|
n_files > 1 ? _("Mixed file owners") :_("Unknown file owner"));
|
|
}
|
|
|
|
/* check if we have superuser privileges */
|
|
if (G_UNLIKELY (geteuid () == 0))
|
|
{
|
|
/* determine all groups in the system */
|
|
user_manager = thunar_user_manager_get_default ();
|
|
groups = thunar_user_manager_get_all_groups (user_manager);
|
|
g_object_unref (G_OBJECT (user_manager));
|
|
}
|
|
else
|
|
{
|
|
if (G_UNLIKELY (user == NULL && n_files > 1))
|
|
{
|
|
/* get groups of the active user */
|
|
user_manager = thunar_user_manager_get_default ();
|
|
user = thunar_user_manager_get_user_by_id (user_manager, geteuid ());
|
|
g_object_unref (G_OBJECT (user_manager));
|
|
}
|
|
|
|
/* determine the groups for the user and take a copy */
|
|
if (G_LIKELY (user != NULL))
|
|
{
|
|
groups = g_list_copy (thunar_user_get_groups (user));
|
|
g_list_foreach (groups, (GFunc) (void (*) (void)) g_object_ref, NULL);
|
|
}
|
|
}
|
|
|
|
/* make sure that the group list includes the file group */
|
|
if (G_UNLIKELY (group != NULL && g_list_find (groups, group) == NULL))
|
|
groups = g_list_prepend (groups, g_object_ref (G_OBJECT (group)));
|
|
|
|
/* sort the groups according to group_compare() */
|
|
groups = g_list_sort_with_data (groups, group_compare, group);
|
|
|
|
/* add the groups to the store */
|
|
for (lp = groups, n = 0; lp != NULL; lp = lp->next)
|
|
{
|
|
/* append a separator after the primary group and after the user-groups (not system groups) */
|
|
if (group != NULL
|
|
&& thunar_group_get_id (groups->data) == thunar_group_get_id (group)
|
|
&& lp != groups && n == 0)
|
|
{
|
|
gtk_list_store_append (store, &iter);
|
|
n += 1;
|
|
}
|
|
else if (lp != groups && thunar_group_get_id (lp->data) < 100 && n == 1)
|
|
{
|
|
gtk_list_store_append (store, &iter);
|
|
n += 1;
|
|
}
|
|
|
|
/* append a new item for the group */
|
|
gtk_list_store_append (store, &iter);
|
|
gtk_list_store_set (store, &iter,
|
|
THUNAR_PERMISSIONS_STORE_COLUMN_NAME, thunar_group_get_name (lp->data),
|
|
THUNAR_PERMISSIONS_STORE_COLUMN_GID, thunar_group_get_id (lp->data),
|
|
-1);
|
|
|
|
/* set the active iter for the combo box if this group is the primary group */
|
|
if (G_UNLIKELY (lp->data == group))
|
|
gtk_combo_box_set_active_iter (GTK_COMBO_BOX (chooser->group_combo), &iter);
|
|
}
|
|
|
|
/* cleanup */
|
|
if (G_LIKELY (user != NULL))
|
|
g_object_unref (G_OBJECT (user));
|
|
if (G_LIKELY (group != NULL))
|
|
g_object_unref (G_OBJECT (group));
|
|
|
|
g_list_free_full (groups, g_object_unref);
|
|
|
|
/* determine the file mode and update the combo boxes */
|
|
for (n = 0; n < G_N_ELEMENTS (chooser->access_combos); ++n)
|
|
{
|
|
g_signal_handlers_block_by_func (G_OBJECT (chooser->access_combos[n]), thunar_permissions_chooser_access_changed, chooser);
|
|
|
|
/* allocate the store for the permission combos */
|
|
access_store = gtk_list_store_new (1, G_TYPE_STRING);
|
|
gtk_list_store_insert_with_values (access_store, NULL, 0, 0, _("None"), -1); /* 0000 */
|
|
gtk_list_store_insert_with_values (access_store, NULL, 1, 0, _("Write only"), -1); /* 0002 */
|
|
gtk_list_store_insert_with_values (access_store, NULL, 2, 0, _("Read only"), -1); /* 0004 */
|
|
gtk_list_store_insert_with_values (access_store, NULL, 3, 0, _("Read & Write"), -1); /* 0006 */
|
|
if (modes[n] == 4)
|
|
gtk_list_store_insert_with_values (access_store, NULL, 4, 0, _("Varying (no change)"), -1);
|
|
|
|
gtk_combo_box_set_model (GTK_COMBO_BOX (chooser->access_combos[n]), GTK_TREE_MODEL (access_store));
|
|
gtk_combo_box_set_active (GTK_COMBO_BOX (chooser->access_combos[n]), modes[n]);
|
|
|
|
g_signal_handlers_unblock_by_func (G_OBJECT (chooser->access_combos[n]), thunar_permissions_chooser_access_changed, chooser);
|
|
|
|
g_object_unref (G_OBJECT (access_store));
|
|
}
|
|
|
|
/* update the program setting based on the mode (only visible for regular files) */
|
|
g_signal_handlers_block_by_func (G_OBJECT (chooser->program_button), thunar_permissions_chooser_program_toggled, chooser);
|
|
g_object_set (G_OBJECT (chooser->program_button), "visible", thunar_file_is_regular (file), NULL);
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (chooser->program_button), (mode & 0111) != 0);
|
|
g_signal_handlers_unblock_by_func (G_OBJECT (chooser->program_button), thunar_permissions_chooser_program_toggled, chooser);
|
|
|
|
/* update the "inconsistent folder permissions" warning and the "fix permissions" button based on the mode */
|
|
if (thunar_permissions_chooser_has_fixable_directory (chooser))
|
|
{
|
|
/* always display the warning even if we cannot fix it */
|
|
gtk_widget_show (chooser->fixperm_label);
|
|
gtk_widget_show (chooser->fixperm_button);
|
|
}
|
|
else
|
|
{
|
|
/* hide both the warning text and the fix button */
|
|
gtk_widget_hide (chooser->fixperm_button);
|
|
gtk_widget_hide (chooser->fixperm_label);
|
|
}
|
|
|
|
/* release our reference on the new combo store and unblock the combo */
|
|
g_signal_handlers_unblock_by_func (G_OBJECT (chooser->group_combo), thunar_permissions_chooser_group_changed, chooser);
|
|
g_object_unref (G_OBJECT (store));
|
|
|
|
/* emit notification on "mutable", so all widgets update their sensitivity */
|
|
g_object_notify (G_OBJECT (chooser), "mutable");
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_permissions_chooser_group_changed (ThunarPermissionsChooser *chooser,
|
|
GtkWidget *combo)
|
|
{
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
guint32 gid;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_PERMISSIONS_CHOOSER (chooser));
|
|
_thunar_return_if_fail (chooser->group_combo == combo);
|
|
_thunar_return_if_fail (GTK_IS_COMBO_BOX (combo));
|
|
|
|
/* verify that we have a valid file */
|
|
if (G_UNLIKELY (chooser->files == NULL))
|
|
return;
|
|
|
|
/* determine the tree model from the combo box */
|
|
model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
|
|
|
|
/* determine the iterator for the selected item */
|
|
if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter))
|
|
{
|
|
/* determine the group id for the selected item... */
|
|
gtk_tree_model_get (model, &iter, THUNAR_PERMISSIONS_STORE_COLUMN_GID, &gid, -1);
|
|
|
|
/* ...and try to change the group to the new gid */
|
|
thunar_permissions_chooser_change_group (chooser, gid);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_permissions_chooser_program_toggled (ThunarPermissionsChooser *chooser,
|
|
GtkWidget *button)
|
|
{
|
|
ThunarFileMode mode;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_PERMISSIONS_CHOOSER (chooser));
|
|
_thunar_return_if_fail (chooser->program_button == button);
|
|
_thunar_return_if_fail (GTK_IS_TOGGLE_BUTTON (button));
|
|
|
|
/* verify that we have a valid file */
|
|
if (G_UNLIKELY (chooser->files == NULL))
|
|
return;
|
|
|
|
/* determine the new mode based on the toggle state */
|
|
mode = (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) ? 0111 : 0000;
|
|
|
|
/* apply the new mode (only the executable bits for files) */
|
|
thunar_permissions_chooser_change_mode (chooser, 0000, 0000, 0111, mode);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_permissions_chooser_fixperm_clicked (ThunarPermissionsChooser *chooser,
|
|
GtkWidget *button)
|
|
{
|
|
ThunarFileMode mode;
|
|
GtkWidget *dialog;
|
|
GtkWidget *window;
|
|
ThunarJob *job;
|
|
gint response;
|
|
GList *lp;
|
|
GList file_list;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_PERMISSIONS_CHOOSER (chooser));
|
|
_thunar_return_if_fail (chooser->fixperm_button == button);
|
|
_thunar_return_if_fail (GTK_IS_BUTTON (button));
|
|
_thunar_return_if_fail (thunar_permissions_chooser_has_fixable_directory (chooser));
|
|
|
|
/* verify that we have a valid file */
|
|
if (G_UNLIKELY (chooser->files == NULL))
|
|
return;
|
|
|
|
/* determine the toplevel widget */
|
|
window = gtk_widget_get_toplevel (GTK_WIDGET (chooser));
|
|
if (G_UNLIKELY (window == NULL))
|
|
return;
|
|
|
|
/* popup a confirm dialog */
|
|
dialog = gtk_message_dialog_new (GTK_WINDOW (window),
|
|
GTK_DIALOG_DESTROY_WITH_PARENT
|
|
| GTK_DIALOG_MODAL,
|
|
GTK_MESSAGE_QUESTION,
|
|
GTK_BUTTONS_NONE,
|
|
_("Correct folder permissions automatically?"));
|
|
gtk_window_set_title (GTK_WINDOW (dialog), _("Folder permissions"));
|
|
gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Cancel"), GTK_RESPONSE_CANCEL);
|
|
gtk_dialog_add_button (GTK_DIALOG (dialog), _("Correct _folder permissions"), GTK_RESPONSE_OK);
|
|
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
|
|
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("The folder permissions will be reset to a consistent state. Only users "
|
|
"allowed to read the contents of this folder will be allowed to enter the "
|
|
"folder afterwards."));
|
|
response = gtk_dialog_run (GTK_DIALOG (dialog));
|
|
gtk_widget_destroy (dialog);
|
|
|
|
/* check if we should apply the changes */
|
|
if (response == GTK_RESPONSE_OK)
|
|
{
|
|
for (lp = chooser->files; lp != NULL; lp = lp->next)
|
|
{
|
|
/* skip files that are fine */
|
|
if (!thunar_permissions_chooser_is_fixable_directory (THUNAR_FILE (lp->data)))
|
|
continue;
|
|
|
|
/* determine the current mode */
|
|
mode = thunar_file_get_mode (THUNAR_FILE (lp->data));
|
|
|
|
/* determine the new mode (making sure the owner can read/enter the folder) */
|
|
mode = (THUNAR_FILE_MODE_USR_READ | THUNAR_FILE_MODE_USR_EXEC)
|
|
| (((mode & THUNAR_FILE_MODE_GRP_READ) != 0) ? THUNAR_FILE_MODE_GRP_EXEC : 0)
|
|
| (((mode & THUNAR_FILE_MODE_OTH_READ) != 0) ? THUNAR_FILE_MODE_OTH_EXEC : 0);
|
|
|
|
file_list.prev = NULL;
|
|
file_list.data = thunar_file_get_file (THUNAR_FILE (lp->data));
|
|
file_list.next = NULL;
|
|
|
|
/* try to allocate the new job */
|
|
job = thunar_io_jobs_change_mode (&file_list,
|
|
0511, mode, 0000, 0000, FALSE);
|
|
exo_job_launch (EXO_JOB (job));
|
|
|
|
/* handle the job */
|
|
thunar_permissions_chooser_job_start (chooser, job, FALSE);
|
|
g_object_unref (job);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static ThunarJobResponse
|
|
thunar_permissions_chooser_job_ask (ThunarPermissionsChooser *chooser,
|
|
const gchar *message,
|
|
ThunarJobResponse choices,
|
|
ThunarJob *job)
|
|
{
|
|
GtkWidget *toplevel;
|
|
|
|
_thunar_return_val_if_fail (THUNAR_IS_PERMISSIONS_CHOOSER (chooser), THUNAR_JOB_RESPONSE_CANCEL);
|
|
_thunar_return_val_if_fail (g_utf8_validate (message, -1, NULL), THUNAR_JOB_RESPONSE_CANCEL);
|
|
_thunar_return_val_if_fail (THUNAR_IS_JOB (job), THUNAR_JOB_RESPONSE_CANCEL);
|
|
_thunar_return_val_if_fail (chooser->job == job, THUNAR_JOB_RESPONSE_CANCEL);
|
|
|
|
/* be sure to display the progress bar prior to opening the question dialog */
|
|
gtk_widget_show_now (chooser->job_progress);
|
|
|
|
/* determine the toplevel window for the chooser */
|
|
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (chooser));
|
|
if (G_UNLIKELY (toplevel == NULL))
|
|
return THUNAR_JOB_RESPONSE_CANCEL;
|
|
|
|
/* display the question dialog */
|
|
return thunar_dialogs_show_job_ask (GTK_WINDOW (toplevel), message, choices);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_permissions_chooser_job_cancel (ThunarPermissionsChooser *chooser)
|
|
{
|
|
_thunar_return_if_fail (THUNAR_IS_PERMISSIONS_CHOOSER (chooser));
|
|
|
|
/* verify that we have a job to cancel */
|
|
if (G_UNLIKELY (chooser->job == NULL))
|
|
return;
|
|
|
|
/* cancel the job (if not already done) */
|
|
exo_job_cancel (EXO_JOB (chooser->job));
|
|
|
|
/* disconnect from the job */
|
|
g_signal_handlers_disconnect_by_data (chooser->job, chooser);
|
|
g_object_unref (G_OBJECT (chooser->job));
|
|
chooser->job = NULL;
|
|
|
|
/* hide the progress bar */
|
|
gtk_widget_hide (chooser->job_progress);
|
|
|
|
/* make the remaining widgets sensitive again */
|
|
gtk_widget_set_sensitive (chooser->grid, TRUE);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_permissions_chooser_job_error (ThunarPermissionsChooser *chooser,
|
|
GError *error,
|
|
ThunarJob *job)
|
|
{
|
|
GtkWidget *toplevel;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_PERMISSIONS_CHOOSER (chooser));
|
|
_thunar_return_if_fail (error != NULL && error->message != NULL);
|
|
_thunar_return_if_fail (THUNAR_IS_JOB (job));
|
|
_thunar_return_if_fail (chooser->job == job);
|
|
|
|
/* be sure to display the progress bar prior to opening the error dialog */
|
|
gtk_widget_show_now (chooser->job_progress);
|
|
|
|
/* determine the toplevel widget for the chooser */
|
|
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (chooser));
|
|
if (G_UNLIKELY (toplevel == NULL))
|
|
return;
|
|
|
|
/* popup the error message dialog */
|
|
xfce_dialog_show_error (GTK_WINDOW (toplevel), error, _("Error while changing file permissions"));
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_permissions_chooser_job_finished (ThunarPermissionsChooser *chooser,
|
|
ThunarJob *job)
|
|
{
|
|
_thunar_return_if_fail (THUNAR_IS_PERMISSIONS_CHOOSER (chooser));
|
|
_thunar_return_if_fail (THUNAR_IS_JOB (job));
|
|
_thunar_return_if_fail (chooser->job == job);
|
|
|
|
/* we can just use job_cancel(), since the job is already done */
|
|
thunar_permissions_chooser_job_cancel (chooser);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_permissions_chooser_job_percent (ThunarPermissionsChooser *chooser,
|
|
gdouble percent,
|
|
ThunarJob *job)
|
|
{
|
|
_thunar_return_if_fail (THUNAR_IS_PERMISSIONS_CHOOSER (chooser));
|
|
_thunar_return_if_fail (THUNAR_IS_JOB (job));
|
|
_thunar_return_if_fail (chooser->job == job);
|
|
|
|
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (chooser->job_progress), percent / 100.0);
|
|
gtk_widget_show (chooser->job_progress);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_permissions_chooser_job_start (ThunarPermissionsChooser *chooser,
|
|
ThunarJob *job,
|
|
gboolean recursive)
|
|
{
|
|
_thunar_return_if_fail (THUNAR_IS_PERMISSIONS_CHOOSER (chooser));
|
|
_thunar_return_if_fail (THUNAR_IS_JOB (job));
|
|
_thunar_return_if_fail (chooser->job == NULL);
|
|
|
|
/* take a reference to the job and connect signals */
|
|
chooser->job = g_object_ref (job);
|
|
g_signal_connect_swapped (job, "ask", G_CALLBACK (thunar_permissions_chooser_job_ask), chooser);
|
|
g_signal_connect_swapped (job, "error", G_CALLBACK (thunar_permissions_chooser_job_error), chooser);
|
|
g_signal_connect_swapped (job, "finished", G_CALLBACK (thunar_permissions_chooser_job_finished), chooser);
|
|
|
|
/* don't connect percent for single file operations */
|
|
if (G_UNLIKELY (recursive))
|
|
g_signal_connect_swapped (job, "percent", G_CALLBACK (thunar_permissions_chooser_job_percent), chooser);
|
|
|
|
/* setup the progress bar */
|
|
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (chooser->job_progress), 0.0);
|
|
|
|
/* make the majority of widgets insensitive if doing recursively */
|
|
gtk_widget_set_sensitive (chooser->grid, !recursive);
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_permissions_chooser_row_separator (GtkTreeModel *model,
|
|
GtkTreeIter *iter,
|
|
gpointer data)
|
|
{
|
|
gchar *name;
|
|
|
|
/* determine the value of the "name" column */
|
|
gtk_tree_model_get (model, iter, THUNAR_PERMISSIONS_STORE_COLUMN_NAME, &name, -1);
|
|
if (G_LIKELY (name != NULL))
|
|
{
|
|
g_free (name);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_permissions_chooser_get_files:
|
|
* @chooser : a #ThunarPermissionsChooser.
|
|
*
|
|
* Returns the #ThunarFile's associated with the specified @chooser.
|
|
*
|
|
* Return value: the file associated with @chooser.
|
|
**/
|
|
GList *
|
|
thunar_permissions_chooser_get_files (ThunarPermissionsChooser *chooser)
|
|
{
|
|
_thunar_return_val_if_fail (THUNAR_IS_PERMISSIONS_CHOOSER (chooser), NULL);
|
|
return chooser->files;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_permissions_chooser_set_files:
|
|
* @chooser : a #ThunarPermissionsChooser.
|
|
* @files : a list of #ThunarFiles or %NULL.
|
|
*
|
|
* Associates @chooser with the specified @file.
|
|
**/
|
|
void
|
|
thunar_permissions_chooser_set_files (ThunarPermissionsChooser *chooser,
|
|
GList *files)
|
|
{
|
|
GList *lp;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_PERMISSIONS_CHOOSER (chooser));
|
|
|
|
/* check if we already use that file */
|
|
if (G_UNLIKELY (chooser->files == files))
|
|
return;
|
|
|
|
/* disconnect from the previous files */
|
|
for (lp = chooser->files; lp != NULL; lp = lp->next)
|
|
{
|
|
_thunar_assert (THUNAR_IS_FILE (lp->data));
|
|
g_signal_handlers_disconnect_by_func (G_OBJECT (lp->data), thunar_permissions_chooser_file_changed, chooser);
|
|
g_object_unref (G_OBJECT (lp->data));
|
|
}
|
|
g_list_free (chooser->files);
|
|
|
|
/* activate the new file */
|
|
chooser->files = g_list_copy (files);
|
|
|
|
/* connect to the new files */
|
|
for (lp = chooser->files; lp != NULL; lp = lp->next)
|
|
{
|
|
/* take a reference on the file */
|
|
_thunar_assert (THUNAR_IS_FILE (lp->data));
|
|
g_object_ref (G_OBJECT (lp->data));
|
|
|
|
/* stay informed about changes */
|
|
g_signal_connect_swapped (G_OBJECT (lp->data), "changed", G_CALLBACK (thunar_permissions_chooser_file_changed), chooser);
|
|
}
|
|
|
|
/* update our state */
|
|
if (chooser->files != NULL)
|
|
thunar_permissions_chooser_file_changed (chooser);
|
|
|
|
/* notify listeners */
|
|
g_object_notify (G_OBJECT (chooser), "files");
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_permissions_chooser_new:
|
|
*
|
|
* Allocates a new #ThunarPermissionsChooser instance.
|
|
*
|
|
* Return value: the newly allocated #ThunarPermissionsChooser.
|
|
**/
|
|
GtkWidget *
|
|
thunar_permissions_chooser_new (void)
|
|
{
|
|
return g_object_new (THUNAR_TYPE_PERMISSIONS_CHOOSER, NULL);
|
|
}
|