summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Eisenmann <p3732@getgoogleoff.me>2021-11-28 16:29:38 +0100
committerAntónio Fernandes <antoniof@gnome.org>2022-01-02 19:22:17 +0000
commitfd9ae59217b41bd0c4d0f786aef2b5b70c909ded (patch)
treefba0e9ef565cf59c878656b8d510929374eb6124
parent8f9cffa4af372e603bdf21cc6e8989123132f948 (diff)
downloadnautilus-fd9ae59217b41bd0c4d0f786aef2b5b70c909ded.tar.gz
properties-window: use AdwComboRow for owner
Replace owner GtkComboBox with AdwComboRow. The underlying model only stores strings returned by nautilus_get_user_names, instead of structured data. On owner changes, the user name is extracted ad-hoc. The implementaiton uses multiple helper functions, meant to be reused for the group ownership adaption. Part of #1326
-rw-r--r--src/nautilus-properties-window.c363
-rw-r--r--src/resources/ui/nautilus-properties-window.ui54
2 files changed, 190 insertions, 227 deletions
diff --git a/src/nautilus-properties-window.c b/src/nautilus-properties-window.c
index 4717c2fa9..79f056211 100644
--- a/src/nautilus-properties-window.c
+++ b/src/nautilus-properties-window.c
@@ -151,8 +151,7 @@ struct _NautilusPropertiesWindow
GtkWidget *bottom_prompt_seperator;
GtkWidget *not_the_owner_label;
- GtkWidget *owner_value_stack;
-
+ AdwComboRow *owner_row;
AdwComboRow *owner_access_row;
AdwComboRow *owner_folder_access_row;
AdwComboRow *owner_file_access_row;
@@ -270,6 +269,7 @@ typedef struct
gboolean has_folders;
gboolean can_set_all_folder_permission;
gboolean can_set_all_file_permission;
+ gboolean is_multi_file_window;
} TargetPermissions;
/* NautilusPermissionEntry - helper struct for permission AdwComboRow */
@@ -494,6 +494,8 @@ static void is_directory_ready_callback (NautilusFile *file,
gpointer data);
static void cancel_group_change_callback (GroupChange *change);
static void cancel_owner_change_callback (OwnerChange *change);
+static void update_owner_row (AdwComboRow *row,
+ TargetPermissions *target_perm);
static void select_image_button_callback (GtkWidget *widget,
NautilusPropertiesWindow *self);
static void set_icon (const char *icon_path,
@@ -1136,6 +1138,8 @@ get_target_permissions (NautilusPropertiesWindow *self)
}
}
+ p->is_multi_file_window = is_multi_file_window (self);
+
return p;
}
@@ -1194,6 +1198,7 @@ properties_window_update (NautilusPropertiesWindow *self,
if (dirty_target)
{
TargetPermissions *target_perm = get_target_permissions (self);
+ update_owner_row (self->owner_row, target_perm);
g_list_foreach (self->permission_buttons,
(GFunc) permission_button_update,
self);
@@ -1402,6 +1407,116 @@ hash_string_list (GList *list)
return hash_value;
}
+static const gchar *
+get_g_list_model_string (GListModel *list,
+ guint position)
+{
+ return gtk_string_object_get_string (GTK_STRING_OBJECT (g_list_model_get_item (list, position)));
+}
+
+static gsize
+get_first_word_length (const gchar *str)
+{
+ const gchar *space_pos = g_strstr_len (str, -1, " ");
+ return space_pos ? space_pos - str : strlen (str);
+}
+
+static void
+update_combo_row_dropdown (AdwComboRow *row,
+ GList *entries)
+{
+ /* check if dropdown already exist and is up to date by comparing with stored hash. */
+ guint current_hash = hash_string_list (entries);
+ guint stored_hash = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (row), "dropdown-hash"));
+
+ if (stored_hash != current_hash)
+ {
+ /* Recreate the drop down. */
+ GListModel *list = adw_combo_row_get_model (row);
+
+ g_list_store_remove_all (G_LIST_STORE (list));
+
+ for (GList *node = entries; node != NULL; node = node->next)
+ {
+ const char *entry = (const char *) node->data;
+ g_autoptr (GtkStringObject) obj = gtk_string_object_new (entry);
+ g_list_store_append (G_LIST_STORE (list), obj);
+ }
+
+ g_object_set_data (G_OBJECT (row), "dropdown-hash", GUINT_TO_POINTER (current_hash));
+ }
+}
+
+typedef gboolean CompareOwnershipRowFunc (GListModel *list,
+ guint position,
+ const char *str);
+
+static void
+select_ownership_row_entry (AdwComboRow *row,
+ const char *entry,
+ CompareOwnershipRowFunc compare_func)
+{
+ GListModel *list = adw_combo_row_get_model (row);
+ gint index_to_select = -1;
+ guint n_entries;
+
+ /* check if entry is already selected */
+ gint selected_pos = adw_combo_row_get_selected (row);
+
+ if (selected_pos >= 0
+ && compare_func (list, selected_pos, entry))
+ {
+ /* entry already selected */
+ return;
+ }
+
+ /* check if entry exists in model list */
+ n_entries = g_list_model_get_n_items (list);
+
+ for (guint position = 0; position < n_entries; position += 1)
+ {
+ if (compare_func (list, position, entry))
+ {
+ /* found entry in list, select it */
+ index_to_select = position;
+ break;
+ }
+ }
+
+ if (index_to_select == -1)
+ {
+ /* entry not in list, add */
+ g_autoptr (GtkStringObject) obj = gtk_string_object_new (entry);
+
+ g_list_store_append (G_LIST_STORE (list), obj);
+ index_to_select = n_entries;
+ }
+
+ adw_combo_row_set_selected (row, index_to_select);
+}
+
+static void
+ownership_row_set_single_entry (AdwComboRow *row,
+ const char *entry,
+ CompareOwnershipRowFunc compare_func)
+{
+ GListModel *list = adw_combo_row_get_model (row);
+ gint selected_pos = adw_combo_row_get_selected (row);
+
+ /* check entry not already displayed */
+ if (selected_pos < 0
+ || g_list_model_get_n_items (list) > 1
+ || !compare_func (list, selected_pos, entry))
+ {
+ /* set current entry as only entry */
+ g_autoptr (GtkStringObject) obj = gtk_string_object_new (entry);
+
+ g_list_store_remove_all (G_LIST_STORE (list));
+ g_list_store_append (G_LIST_STORE (list), obj);
+ adw_combo_row_set_selected (row, 0);
+ }
+}
+
static void
group_change_free (GroupChange *change)
{
@@ -1547,30 +1662,6 @@ changed_group_callback (GtkComboBox *combo_box,
}
}
-static char *
-combo_box_get_active_entry (GtkComboBox *combo_box,
- unsigned int column)
-{
- GtkTreeModel *model;
- GtkTreeIter iter;
- char *val;
-
- g_assert (GTK_IS_COMBO_BOX (combo_box));
-
- if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter))
- {
- model = gtk_combo_box_get_model (combo_box);
- g_assert (GTK_IS_TREE_MODEL (model));
-
- gtk_tree_model_get (model, &iter,
- column, &val,
- -1);
- return val;
- }
-
- return NULL;
-}
-
/* returns the index of the given entry in the the given column
* at the first level of model. Returns -1 if entry can't be found
* or entry is NULL.
@@ -1853,168 +1944,98 @@ unschedule_or_cancel_owner_change (NautilusPropertiesWindow *self)
}
static void
-changed_owner_callback (GtkComboBox *combo_box,
- NautilusFile *file)
+changed_owner_callback (AdwComboRow *row,
+ GParamSpec *pspec,
+ NautilusPropertiesWindow *self)
{
- NautilusPropertiesWindow *self;
- g_autofree char *new_owner = NULL;
- g_autofree char *cur_owner = NULL;
-
- g_assert (GTK_IS_COMBO_BOX (combo_box));
- g_assert (NAUTILUS_IS_FILE (file));
+ gint selected_pos = adw_combo_row_get_selected (row);
+ g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (self));
- new_owner = combo_box_get_active_entry (combo_box, 2);
- if (!new_owner)
+ if (selected_pos >= 0)
{
- return;
- }
- cur_owner = nautilus_file_get_owner_name (file);
+ NautilusFile *file = get_target_file (self);
- if (strcmp (new_owner, cur_owner) != 0)
- {
- /* Try to change file owner. If this fails, complain to user. */
- self = NAUTILUS_PROPERTIES_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (combo_box), GTK_TYPE_WINDOW));
+ GListModel *list = adw_combo_row_get_model (row);
+ const gchar *selected_owner_str = get_g_list_model_string (list, selected_pos);
+ gsize owner_name_length = get_first_word_length (selected_owner_str);
+ g_autofree gchar *new_owner_name = g_strndup (selected_owner_str, owner_name_length);
+ g_autofree char *current_owner_name = nautilus_file_get_owner_name (file);
+
+ g_assert (NAUTILUS_IS_FILE (file));
- unschedule_or_cancel_owner_change (self);
- schedule_owner_change (self, file, new_owner);
+ if (strcmp (new_owner_name, current_owner_name) != 0)
+ {
+ /* Try to change file owner. If this fails, complain to user. */
+ unschedule_or_cancel_owner_change (self);
+ schedule_owner_change (self, file, new_owner_name);
+ }
}
}
-static void
-synch_user_menu (GtkComboBox *combo_box,
- NautilusFile *file)
+static gboolean
+adw_value_list_entry_starts_with_str (GListModel *list,
+ guint position,
+ const char *str)
{
- GList *users = NULL;
- GList *node;
- GtkTreeModel *model;
- GtkListStore *store;
- GtkTreeIter iter;
- g_autofree char *owner_name = NULL;
- g_autofree char *nice_owner_name = NULL;
- int user_index;
- int owner_index;
- guint current_user_hash, stored_user_hash;
+ const gchar *entry_str = get_g_list_model_string (list, position);
- g_assert (GTK_IS_COMBO_BOX (combo_box));
- g_assert (NAUTILUS_IS_FILE (file));
-
- if (nautilus_file_is_gone (file))
- {
- return;
- }
-
- users = nautilus_get_user_names ();
+ return g_str_has_prefix (entry_str, str)
+ && strlen (str) == get_first_word_length (entry_str);
+}
- current_user_hash = hash_string_list (users);
- stored_user_hash = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (combo_box), "user-hash"));
+/* Select correct owner if file permissions have changed. */
+static void
+update_owner_row (AdwComboRow *row,
+ TargetPermissions *target_perm)
+{
+ NautilusPropertiesWindow *self = target_perm->window;
+ gboolean provide_dropdown = (!target_perm->is_multi_file_window
+ && nautilus_file_can_set_owner (get_target_file (self)));
+ gboolean had_dropdown = gtk_widget_is_sensitive (GTK_WIDGET (row));
- model = gtk_combo_box_get_model (combo_box);
- store = GTK_LIST_STORE (model);
- g_assert (GTK_IS_LIST_STORE (model));
+ gtk_widget_set_sensitive (GTK_WIDGET (row), provide_dropdown);
- if (stored_user_hash != current_user_hash)
+ /* check if should provide dropdown */
+ if (provide_dropdown)
{
- /* Clear the contents of ComboBox in a wacky way because there
- * is no function to clear all items and also no function to obtain
- * the number of items in a combobox.
- */
- gtk_list_store_clear (store);
+ NautilusFile *file = get_target_file (self);
+ g_autofree char *owner_name = nautilus_file_get_owner_name (file);
+ GList *users = nautilus_get_user_names ();
- for (node = users, user_index = 0; node != NULL; node = node->next, ++user_index)
- {
- char *combo_text = (char *) node->data;
- char *separator_pos = g_strstr_len (combo_text, -1, " – ");
- char *user_name = combo_text;
+ update_combo_row_dropdown (row, users);
- if (separator_pos != NULL)
- {
- /* Has display name, extract user name */
- guint user_name_length = separator_pos - combo_text;
- user_name = g_strndup (combo_text, user_name_length);
- }
+ /* display current owner */
+ select_ownership_row_entry (row, owner_name, adw_value_list_entry_starts_with_str);
- gtk_list_store_append (store, &iter);
- gtk_list_store_set (store, &iter,
- 0, combo_text,
- 1, user_name,
- 2, user_name,
- -1);
- if (separator_pos != NULL)
- {
- // only free if copied
- g_free (user_name);
- }
+ if (!had_dropdown)
+ {
+ /* Update file when selection changes. */
+ g_signal_connect (row, "notify::selected-index",
+ G_CALLBACK (changed_owner_callback),
+ self);
}
-
- g_object_set_data (G_OBJECT (combo_box), "user-hash", GUINT_TO_POINTER (current_user_hash));
}
-
- owner_name = nautilus_file_get_owner_name (file);
- owner_index = tree_model_get_entry_index (model, 2, owner_name);
- nice_owner_name = nautilus_file_get_string_attribute (file, "owner");
-
- /* If owner wasn't in list, we prepend it (with a separator).
- * This can happen if the owner is an id with no matching
- * identifier in the passwords file.
- */
- if (owner_index < 0 && owner_name != NULL)
+ else
{
- if (users != NULL)
- {
- /* add separator */
- gtk_list_store_prepend (store, &iter);
- gtk_list_store_set (store, &iter,
- 0, "-",
- 1, NULL,
- 2, NULL,
- -1);
- }
+ g_autofree char *owner_name = file_list_get_string_attribute (self->target_files,
+ "owner",
+ _("Multiple"));
- owner_index = 0;
+ g_signal_handlers_disconnect_by_func (row, G_CALLBACK (changed_owner_callback), self);
- gtk_list_store_prepend (store, &iter);
- gtk_list_store_set (store, &iter,
- 0, nice_owner_name,
- 1, owner_name,
- 2, owner_name,
- -1);
+ ownership_row_set_single_entry (row, owner_name, adw_value_list_entry_starts_with_str);
}
-
- gtk_combo_box_set_active (combo_box, owner_index);
-
- g_list_free_full (users, g_free);
}
static void
-setup_owner_combo_box (GtkWidget *combo_box,
- NautilusFile *file)
+setup_ownership_row (NautilusPropertiesWindow *self,
+ AdwComboRow *row)
{
- g_autoptr (GtkTreeModel) model = NULL;
- GtkCellRenderer *renderer;
-
- model = GTK_TREE_MODEL (gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING));
- gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box), model);
-
- renderer = gtk_cell_renderer_text_new ();
- gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE);
- gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box), renderer,
- "text", 0);
-
- gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo_box),
- combo_box_row_separator_func,
- NULL,
- NULL);
+ GListStore *list_store = g_list_store_new (GTK_TYPE_STRING_OBJECT);
- synch_user_menu (GTK_COMBO_BOX (combo_box), file);
+ adw_combo_row_set_model (row, G_LIST_MODEL (list_store));
- /* Connect to signal to update menu when file changes. */
- g_signal_connect_object (file, "changed",
- G_CALLBACK (synch_user_menu),
- combo_box, G_CONNECT_SWAPPED);
- g_signal_connect_data (combo_box, "changed",
- G_CALLBACK (changed_owner_callback),
- nautilus_file_ref (file),
- (GClosureNotify) nautilus_file_unref, 0);
+ /* Intial setup of list model is handled via update function, called via properties_window_update. */
}
static gboolean
@@ -3475,34 +3496,14 @@ create_permissions_row (NautilusPropertiesWindow *self,
static void
create_simple_permissions (NautilusPropertiesWindow *self)
{
- GtkWidget *owner_combo_box;
- GtkWidget *owner_value_label;
GtkWidget *group_combo_box;
GtkWidget *group_value_label;
FilterType filter_type = files_get_filter_type (self);
g_assert (filter_type != NO_FILES_OR_FOLDERS);
- if (!is_multi_file_window (self) && nautilus_file_can_set_owner (get_target_file (self)))
- {
- /* Combo box in this case. */
- owner_combo_box = gtk_stack_get_child_by_name (GTK_STACK (self->owner_value_stack), "combo_box");
- gtk_stack_set_visible_child (GTK_STACK (self->owner_value_stack), owner_combo_box);
- setup_owner_combo_box (owner_combo_box, get_target_file (self));
- }
- else
- {
- /* Static text in this case. */
- owner_value_label = gtk_stack_get_child_by_name (GTK_STACK (self->owner_value_stack), "label");
- gtk_stack_set_visible_child (GTK_STACK (self->owner_value_stack), owner_value_label);
+ setup_ownership_row (self, self->owner_row);
- /* Stash a copy of the file attribute name in this field for the callback's sake. */
- g_object_set_data_full (G_OBJECT (owner_value_label), "file_attribute",
- g_strdup ("owner"), g_free);
-
- self->value_fields = g_list_prepend (self->value_fields,
- owner_value_label);
- }
if (!is_multi_file_window (self) && nautilus_file_can_set_group (get_target_file (self)))
{
/* Combo box in this case. */
@@ -5046,7 +5047,7 @@ nautilus_properties_window_class_init (NautilusPropertiesWindowClass *klass)
gtk_widget_class_bind_template_child (widget_class, NautilusPropertiesWindow, bottom_prompt_seperator);
gtk_widget_class_bind_template_child (widget_class, NautilusPropertiesWindow, not_the_owner_label);
gtk_widget_class_bind_template_child (widget_class, NautilusPropertiesWindow, unknown_permissions_page);
- gtk_widget_class_bind_template_child (widget_class, NautilusPropertiesWindow, owner_value_stack);
+ gtk_widget_class_bind_template_child (widget_class, NautilusPropertiesWindow, owner_row);
gtk_widget_class_bind_template_child (widget_class, NautilusPropertiesWindow, owner_access_row);
gtk_widget_class_bind_template_child (widget_class, NautilusPropertiesWindow, owner_folder_access_row);
gtk_widget_class_bind_template_child (widget_class, NautilusPropertiesWindow, owner_file_access_row);
diff --git a/src/resources/ui/nautilus-properties-window.ui b/src/resources/ui/nautilus-properties-window.ui
index 431a6e76a..e0135789b 100644
--- a/src/resources/ui/nautilus-properties-window.ui
+++ b/src/resources/ui/nautilus-properties-window.ui
@@ -666,52 +666,6 @@
<property name="row_spacing">6</property>
<property name="column_spacing">12</property>
<child>
- <object class="GtkLabel" id="owner_label">
- <property name="label" translatable="yes">_Owner</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">owner_combo_box</property>
- <property name="xalign">1</property>
- <style>
- <class name="dim-label"/>
- </style>
- <layout>
- <property name="column">0</property>
- <property name="row">0</property>
- </layout>
- </object>
- </child>
- <child>
- <object class="GtkStack" id="owner_value_stack">
- <child>
- <object class="GtkStackPage">
- <property name="name">combo_box</property>
- <property name="title">page0</property>
- <property name="child">
- <object class="GtkComboBox" id="owner_combo_box">
- <property name="halign">start</property>
- </object>
- </property>
- </object>
- </child>
- <child>
- <object class="GtkStackPage">
- <property name="name">label</property>
- <property name="title">page0</property>
- <property name="child">
- <object class="GtkLabel" id="owner_value_label">
- <property name="selectable">True</property>
- <property name="xalign">0</property>
- </object>
- </property>
- </object>
- </child>
- <layout>
- <property name="column">1</property>
- <property name="row">0</property>
- </layout>
- </object>
- </child>
- <child>
<object class="GtkLabel" id="group_label">
<property name="margin_top">12</property>
<property name="label" translatable="yes">_Group</property>
@@ -778,6 +732,14 @@
<object class="GtkListBox" id="owner_list_box">
<property name="selection-mode">none</property>
<child>
+ <object class="AdwComboRow" id="owner_row">
+ <property name="sensitive">False</property>
+ <property name="activatable">False</property>
+ <property name="title" translatable="yes">_Owner</property>
+ <property name="use-underline">True</property>
+ </object>
+ </child>
+ <child>
<object class="AdwComboRow" id="owner_access_row">
<property name="visible">False</property>
<property name="activatable">False</property>