Fix messages being selected after move/etc and autoselect is disabled.

Bug 773054

* src/client/conversation-list/conversation-list-view.vala: Disable all
  selection when the user is causing the selection will change. This
  prevents the GtkTreeView impl from selecting the next row after rows
  are removed as a result of user action, but not for some other reason,
  i.e. the server removing a message.

* src/client/application/geary-controller.vala: Call new
  ConversationListView::set_changing_selection as needed when moving
  messages.

* src/client/conversation-list/conversation-list-store,
  src/engine/app/app-conversation-monitor.vala: Rename
  conversation_removed signal to conversations_removed and change its
  param to be a collection of conversations, so we can re-enable
  ConversationListView selection only after all conversations have been
  removed. Update call sites.
This commit is contained in:
Michael James Gratton 2016-10-20 16:27:43 +11:00
parent 0e5df2c9be
commit 310daa8ad0
4 changed files with 68 additions and 31 deletions

View file

@ -1442,7 +1442,7 @@ public class GearyController : Geary.BaseObject {
current_conversations.seed_completed.connect(on_conversation_count_changed);
current_conversations.scan_completed.connect(on_conversation_count_changed);
current_conversations.conversations_added.connect(on_conversation_count_changed);
current_conversations.conversation_removed.connect(on_conversation_count_changed);
current_conversations.conversations_removed.connect(on_conversation_count_changed);
if (!current_conversations.is_monitoring)
yield current_conversations.start_monitoring_async(conversation_cancellable);
@ -1960,12 +1960,19 @@ public class GearyController : Geary.BaseObject {
Gee.List<Geary.EmailIdentifier> ids = get_selected_email_ids(false);
if (ids.size == 0)
return;
this.main_window.conversation_list_view.set_changing_selection(true);
Geary.FolderSupport.Move? supports_move = current_folder as Geary.FolderSupport.Move;
if (supports_move != null)
move_conversation_async.begin(supports_move, ids, destination.path, cancellable_folder);
move_conversation_async.begin(
supports_move, ids, destination.path, cancellable_folder,
(obj, ret) => {
move_conversation_async.end(ret);
this.main_window.conversation_list_view.set_changing_selection(false);
});
}
private async void move_conversation_async(Geary.FolderSupport.Move source_folder,
Gee.List<Geary.EmailIdentifier> ids, Geary.FolderPath destination, Cancellable? cancellable) {
try {
@ -2572,9 +2579,12 @@ public class GearyController : Geary.BaseObject {
last_deleted_conversation = selected_conversations.size > 0
? Geary.traverse<Geary.App.Conversation>(selected_conversations).first() : null;
// Return focus to the conversation list from the clicked toolbar button.
main_window.conversation_list_view.grab_focus();
// Return focus to the conversation list from the clicked
// toolbar button.
this.main_window.conversation_list_view.grab_focus();
this.main_window.conversation_list_view.set_changing_selection(true);
Gee.List<Geary.EmailIdentifier> ids = get_selected_email_ids(false);
if (archive) {
debug("Archiving selected messages");
@ -2629,6 +2639,7 @@ public class GearyController : Geary.BaseObject {
} catch (Error e) {
debug("Unable to archive/trash/delete messages: %s", e.message);
}
this.main_window.conversation_list_view.set_changing_selection(false);
}
private void save_revokable(Geary.Revokable? new_revokable, string? description) {

View file

@ -92,8 +92,8 @@ public class ConversationListStore : Gtk.ListStore {
private uint update_id = 0;
public signal void conversations_added_began();
public signal void conversations_added_finished();
public signal void conversations_removed(bool start);
public ConversationListStore(Geary.App.ConversationMonitor conversations) {
set_column_types(Column.get_types());
@ -112,7 +112,7 @@ public class ConversationListStore : Gtk.ListStore {
conversations.scan_completed.connect(on_scan_completed);
conversations.conversations_added.connect(on_conversations_added);
conversations.conversation_removed.connect(on_conversation_removed);
conversations.conversations_removed.connect(on_conversations_removed);
conversations.conversation_appended.connect(on_conversation_appended);
conversations.conversation_trimmed.connect(on_conversation_trimmed);
conversations.email_flags_changed.connect(on_email_flags_changed);
@ -428,10 +428,13 @@ public class ConversationListStore : Gtk.ListStore {
conversations_added_finished();
}
private void on_conversation_removed(Geary.App.Conversation conversation) {
remove_conversation(conversation);
private void on_conversations_removed(Gee.Collection<Geary.App.Conversation> conversations) {
conversations_removed(true);
foreach (Geary.App.Conversation removed in conversations)
remove_conversation(removed);
conversations_removed(false);
}
private void on_conversation_appended(Geary.App.Conversation conversation) {
if (has_conversation(conversation)) {
refresh_conversation(conversation);

View file

@ -19,6 +19,7 @@ public class ConversationListView : Gtk.TreeView {
private Gtk.Menu? context_menu = null;
private Gee.Set<Geary.App.Conversation> selected = new Gee.HashSet<Geary.App.Conversation>();
private uint selection_changed_id = 0;
private bool suppress_selection = false;
public signal void conversations_selected(Gee.Set<Geary.App.Conversation> selected);
@ -83,6 +84,7 @@ public class ConversationListView : Gtk.TreeView {
old_store.conversations_added_began.disconnect(
on_conversations_added_began
);
old_store.conversations_removed.disconnect(on_conversations_removed);
old_store.row_inserted.disconnect(on_rows_changed);
old_store.rows_reordered.disconnect(on_rows_changed);
old_store.row_changed.disconnect(on_rows_changed);
@ -102,6 +104,7 @@ public class ConversationListView : Gtk.TreeView {
new_store.conversations_added_finished.connect(
on_conversations_added_finished
);
new_store.conversations_removed.connect(on_conversations_removed);
}
// Disconnect the selection handler since we don't want to
@ -113,12 +116,27 @@ public class ConversationListView : Gtk.TreeView {
selection.changed.connect(on_selection_changed);
}
/**
* Specifies an action is currently changing the view's selection.
*/
public void set_changing_selection(bool is_changing) {
// Make sure that when not autoselecting, and if the user is
// causing selected rows to be removed, the next row is not
// automatically selected by GtkTreeView
if (is_changing) {
this.suppress_selection =
!GearyApplication.instance.config.autoselect;
} else {
// If no longer changing, always re-enable selection
get_selection().set_mode(Gtk.SelectionMode.MULTIPLE);
}
}
private void on_conversation_monitor_changed() {
if (conversation_monitor != null) {
conversation_monitor.scan_started.disconnect(on_scan_started);
conversation_monitor.scan_completed.disconnect(on_scan_completed);
conversation_monitor.seed_completed.disconnect(on_seed_completed);
conversation_monitor.conversation_removed.disconnect(on_conversation_removed);
}
conversation_monitor = GearyApplication.instance.controller.current_conversations;
@ -127,7 +145,6 @@ public class ConversationListView : Gtk.TreeView {
conversation_monitor.scan_started.connect(on_scan_started);
conversation_monitor.scan_completed.connect(on_scan_completed);
conversation_monitor.seed_completed.connect(on_seed_completed);
conversation_monitor.conversation_removed.connect(on_conversation_removed);
}
}
@ -157,11 +174,6 @@ public class ConversationListView : Gtk.TreeView {
}
}
private void on_conversation_removed(Geary.App.Conversation conversation) {
if (!GearyApplication.instance.config.autoselect)
get_selection().unselect_all();
}
private void on_conversations_added_began() {
Gtk.Adjustment? adjustment = get_adjustment();
// If we were at the top, we want to stay there after conversations are added.
@ -184,7 +196,19 @@ public class ConversationListView : Gtk.TreeView {
adjustment.set_value(0);
}
private void on_conversations_removed(bool start) {
if (!GearyApplication.instance.config.autoselect) {
Gtk.SelectionMode mode = start
// Stop GtkTreeView from automatically selecting the
// next row after the removed rows
? Gtk.SelectionMode.NONE
// Allow the user to make selections again
: Gtk.SelectionMode.MULTIPLE;
get_selection().set_mode(mode);
}
}
private Gtk.Adjustment? get_adjustment() {
Gtk.ScrolledWindow? parent = get_parent() as Gtk.ScrolledWindow;
if (parent == null) {

View file

@ -112,15 +112,15 @@ public class Geary.App.ConversationMonitor : BaseObject {
/**
* "conversations-removed" is fired when all the email in a Conversation has been removed.
* It's possible this will be called without a signal alerting that it's emails have been
* removed, i.e. a "conversation-removed" signal may fire with no accompanying
* removed, i.e. a "conversations-removed" signal may fire with no accompanying
* "conversation-trimmed".
*
* Note that this can only occur when monitoring is enabled. There is (currently) no
* user call to manually remove email from Conversations.
*/
public virtual signal void conversation_removed(Conversation conversation) {
Logging.debug(Logging.Flag.CONVERSATIONS, "[%s] ConversationMonitor::conversation_removed",
folder.to_string());
public virtual signal void conversations_removed(Gee.Collection<Conversation> conversations) {
Logging.debug(Logging.Flag.CONVERSATIONS, "[%s] ConversationMonitor::conversations_removed %d",
folder.to_string(), conversations.size);
}
/**
@ -216,8 +216,8 @@ public class Geary.App.ConversationMonitor : BaseObject {
conversations_added(conversations);
}
protected virtual void notify_conversation_removed(Conversation conversation) {
conversation_removed(conversation);
protected virtual void notify_conversations_removed(Gee.Collection<Conversation> conversations) {
conversations_removed(conversations);
}
protected virtual void notify_conversation_appended(Conversation conversation,
@ -583,9 +583,8 @@ public class Geary.App.ConversationMonitor : BaseObject {
// fall-through
}
if (removed_due_to_merge != null) {
foreach (Conversation conversation in removed_due_to_merge)
notify_conversation_removed(conversation);
if (removed_due_to_merge != null && removed_due_to_merge.size > 0) {
notify_conversations_removed(removed_due_to_merge);
}
if (added != null && added.size > 0)
@ -637,8 +636,8 @@ public class Geary.App.ConversationMonitor : BaseObject {
foreach (Conversation conversation in trimmed.get_keys())
notify_conversation_trimmed(conversation, trimmed.get(conversation));
foreach (Conversation conversation in removed)
notify_conversation_removed(conversation);
if (removed.size > 0)
notify_conversations_removed(removed);
// For any still-existing conversations that we've trimmed messages
// from, do a search for any messages that should still be there due to