From: PBS Date: Fri, 22 Nov 2013 01:11:41 +0100 Subject: [PATCH] Fix bugginess and crashing with owner/group changing Prevent owner_change_callback from setting owner change fields to NULL and causing assertion failure in schedule_owner_change_timeout when they race with each other (same for changing group). This usually occurs because synch_user_menu triggers an owner change which is not recognized as trivial if the owner's fullname is different to their realname, but can also happen if the user perfectly times an owner change. Correctly find the combobox entry for the current owner, using the owner's real name. Also prevents above change from causing owner changes every 200ms for users with a non-empty 'full-name' field. Don't change the active entry in user/group combo box if changes are pending, unless it just has been refreshed in which case it is necessary, otherwise there would be no active entry. Unschedule pending owner changes if the current owner is reselected. https://bugzilla.gnome.org/show_bug.cgi?id=700492 https://bugzilla.gnome.org/show_bug.cgi?id=707987 diff --git a/src/nautilus-properties-window.c b/src/nautilus-properties-window.c --- a/src/nautilus-properties-window.c +++ b/src/nautilus-properties-window.c @@ -1371,6 +1371,12 @@ group_change_callback (NautilusFile *fil char *group; g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window)); + + if ((window->details->group_change_timeout != 0) || (window->details->group_change_file == NULL)) { + /* The callback should have been cancelled so behave as if it was. */ + return; + } + g_assert (window->details->group_change_file == file); group = window->details->group_change_group; @@ -1430,11 +1436,11 @@ schedule_group_change_timeout (NautilusP _("Cancel Group Change?"), GTK_WINDOW (window)); + window->details->group_change_timeout = 0; nautilus_file_set_group (file, group, (NautilusFileOperationCallback) group_change_callback, window); - window->details->group_change_timeout = 0; return FALSE; } @@ -1508,13 +1514,14 @@ changed_group_callback (GtkComboBox *com group = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (combo_box)); cur_group = nautilus_file_get_group_name (file); + /* Try to change file group. If this fails, complain to user. If the group is changed + * back to its current value before the change is applied, unschedule the pending change. */ + window = NAUTILUS_PROPERTIES_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (combo_box), GTK_TYPE_WINDOW)); + unschedule_or_cancel_group_change (window); if (group != NULL && strcmp (group, cur_group) != 0) { - /* Try to change file group. If this fails, complain to user. */ - window = NAUTILUS_PROPERTIES_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (combo_box), GTK_TYPE_WINDOW)); - - unschedule_or_cancel_group_change (window); schedule_group_change (window, file, group); } + g_free (group); g_free (cur_group); } @@ -1633,6 +1640,7 @@ synch_groups_combo_box (GtkComboBox *com GList *node; GtkTreeModel *model; GtkListStore *store; + gboolean refresh_grouplist; const char *group_name; char *current_group_name; int group_index; @@ -1651,7 +1659,8 @@ synch_groups_combo_box (GtkComboBox *com store = GTK_LIST_STORE (model); g_assert (GTK_IS_LIST_STORE (model)); - if (!tree_model_entries_equal (model, 0, groups)) { + refresh_grouplist = !tree_model_entries_equal (model, 0, groups); + if (refresh_grouplist) { /* 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. @@ -1680,7 +1689,13 @@ synch_groups_combo_box (GtkComboBox *com gtk_combo_box_text_prepend_text (GTK_COMBO_BOX_TEXT (combo_box), current_group_name); current_group_index = 0; } - gtk_combo_box_set_active (combo_box, current_group_index); + + /* Don't change the active entry if the user is scrolling because that means their changes will be unscheduled. */ + NautilusPropertiesWindow *window; + window = NAUTILUS_PROPERTIES_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (combo_box), GTK_TYPE_WINDOW)); + if (refresh_grouplist || ((window->details->group_change_timeout == 0) && (window->details->group_change_group == NULL))) { + gtk_combo_box_set_active (combo_box, current_group_index); + } g_free (current_group_name); g_list_free_full (groups, g_free); @@ -1786,6 +1801,12 @@ owner_change_callback (NautilusFile *fil char *owner; g_assert (NAUTILUS_IS_PROPERTIES_WINDOW (window)); + + if ((window->details->owner_change_timeout != 0) || (window->details->owner_change_file == NULL)) { + /* The callback should have been cancelled so behave as if it was. */ + return; + } + g_assert (window->details->owner_change_file == file); owner = window->details->owner_change_owner; @@ -1845,11 +1866,11 @@ schedule_owner_change_timeout (NautilusP _("Cancel Owner Change?"), GTK_WINDOW (window)); + window->details->owner_change_timeout = 0; nautilus_file_set_owner (file, owner, (NautilusFileOperationCallback) owner_change_callback, window); - window->details->owner_change_timeout = 0; return FALSE; } @@ -1930,13 +1951,14 @@ changed_owner_callback (GtkComboBox *com g_free (owner_text); cur_owner = nautilus_file_get_owner_name (file); + /* Try to change file owner. If this fails, complain to user. If the owner is changed + * back to its current value before the change is applied, unschedule the pending change. */ + window = NAUTILUS_PROPERTIES_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (combo_box), GTK_TYPE_WINDOW)); + unschedule_or_cancel_owner_change (window); if (strcmp (new_owner, cur_owner) != 0) { - /* Try to change file owner. If this fails, complain to user. */ - window = NAUTILUS_PROPERTIES_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (combo_box), GTK_TYPE_WINDOW)); - - unschedule_or_cancel_owner_change (window); schedule_owner_change (window, file, new_owner); } + g_strfreev (name_array); g_free (cur_owner); } @@ -1949,6 +1971,7 @@ synch_user_menu (GtkComboBox *combo_box, GtkTreeModel *model; GtkListStore *store; GtkTreeIter iter; + gboolean refresh_userlist; char *user_name; char *owner_name; int user_index; @@ -1969,36 +1992,42 @@ synch_user_menu (GtkComboBox *combo_box, store = GTK_LIST_STORE (model); g_assert (GTK_IS_LIST_STORE (model)); - if (!tree_model_entries_equal (model, 1, users)) { - /* 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. - */ + /* Recreate the ComboBox contents, while also searching for the current owner. */ + refresh_userlist = !tree_model_entries_equal (model, 1, users); + owner_name = nautilus_file_get_owner_name (file); + owner_index = -1; + + if (refresh_userlist) { gtk_list_store_clear (store); + } - for (node = users, user_index = 0; node != NULL; node = node->next, ++user_index) { - user_name = (char *)node->data; + for (node = users, user_index = 0; node != NULL; node = node->next, ++user_index) { + user_name = (char *)node->data; - name_array = g_strsplit (user_name, "\n", 2); - if (name_array[1] != NULL) { - combo_text = g_strdup_printf ("%s - %s", name_array[0], name_array[1]); - } else { - combo_text = g_strdup (name_array[0]); - } + name_array = g_strsplit (user_name, "\n", 2); + if (name_array[1] != NULL) { + combo_text = g_strdup_printf ("%s - %s", name_array[0], name_array[1]); + } else { + combo_text = g_strdup (name_array[0]); + } + + if (strcmp(owner_name, name_array[0]) == 0) { + owner_index = user_index; + } + if (refresh_userlist) { gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, - 0, combo_text, - 1, user_name, - -1); - - g_strfreev (name_array); - g_free (combo_text); + 0, combo_text, + 1, user_name, + -1); } - } - owner_name = nautilus_file_get_string_attribute (file, "owner"); - owner_index = tree_model_get_entry_index (model, 0, owner_name); + g_strfreev (name_array); + g_free (combo_text); + + if (owner_index != -1 && !refresh_userlist) break; + } /* If owner wasn't in list, we prepend it (with a separator). * This can happen if the owner is an id with no matching @@ -2032,7 +2061,12 @@ synch_user_menu (GtkComboBox *combo_box, g_strfreev (name_array); } - gtk_combo_box_set_active (combo_box, owner_index); + /* Don't change the active entry if the user is scrolling because that means their changes will be unscheduled. */ + NautilusPropertiesWindow *window; + window = NAUTILUS_PROPERTIES_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (combo_box), GTK_TYPE_WINDOW)); + if (refresh_userlist || ((window->details->owner_change_timeout == 0) && (window->details->owner_change_owner == NULL))) { + gtk_combo_box_set_active (combo_box, owner_index); + } g_free (owner_name); g_list_free_full (users, g_free);