From 0b743ab0d396cbbaad5aebaebed235ecd0f1a564 Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Tue, 6 Oct 2020 13:34:30 +0200
Subject: [PATCH 059/124] conversation-list: use shift+activate to open
 conversation in new window

Open on shift+double click and shift+space/enter the selected
conversation in a new window.
---
 .../application/application-main-window.vala  | 14 ++---
 .../conversation-list-view.vala               | 61 ++++++++++++++++---
 2 files changed, 58 insertions(+), 17 deletions(-)

diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index c2b0954b..01b7b9c6 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -2143,17 +2143,16 @@ public class Application.MainWindow :
             focus_next_pane();
     }
 
-    private void on_conversation_activated(Geary.App.Conversation activated) {
-        if (main_leaflet.folded) {
-            focus_next_pane();
-        }
-        /* TODO: find correct UX for opening the conversation in a new window
-        if (this.selected_folder != null) {
+    private void on_conversation_activated(Geary.App.Conversation activated, bool single) {
+        if (single) {
+            if (main_leaflet.folded)
+                focus_next_pane();
+        } else if (this.selected_folder != null) {
             if (this.selected_folder.used_as != DRAFTS) {
                 this.application.new_window.begin(
                     this.selected_folder,
                     this.conversation_list_view.copy_selected()
-                );
+                    );
             } else {
                 // TODO: Determine how to map between conversations
                 // and drafts correctly.
@@ -2166,7 +2165,6 @@ public class Application.MainWindow :
                 );
             }
         }
-        */
     }
 
     private void on_find_in_conversation_action() {
diff --git a/src/client/conversation-list/conversation-list-view.vala b/src/client/conversation-list/conversation-list-view.vala
index dcced90d..0baa66b4 100644
--- a/src/client/conversation-list/conversation-list-view.vala
+++ b/src/client/conversation-list/conversation-list-view.vala
@@ -17,6 +17,7 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
     private Geary.Scheduler.Scheduled? scheduled_update_visible_conversations = null;
     private Gee.Set<Geary.App.Conversation> selected = new Gee.HashSet<Geary.App.Conversation>();
     private Geary.IdleManager selection_update;
+    private Gtk.GestureMultiPress gesture;
 
     // Determines if the next folder scan should avoid selecting a
     // conversation when autoselect is enabled
@@ -26,7 +27,7 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
     public signal void conversations_selected(Gee.Set<Geary.App.Conversation> selected);
 
     // Signal for when a conversation has been double-clicked, or selected and enter is pressed.
-    public signal void conversation_activated(Geary.App.Conversation activated);
+    public signal void conversation_activated(Geary.App.Conversation activated, bool single = false);
 
     public virtual signal void load_more() {
         enable_load_more = false;
@@ -42,7 +43,6 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
         base_ref();
         set_show_expanders(false);
         set_headers_visible(false);
-        set_activate_on_single_click(true);
 
         this.config = config;
 
@@ -53,11 +53,13 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
         Gtk.TreeSelection selection = get_selection();
         selection.set_mode(Gtk.SelectionMode.MULTIPLE);
         style_updated.connect(on_style_changed);
-        row_activated.connect(on_row_activated);
 
         notify["vadjustment"].connect(on_vadjustment_changed);
 
+        key_press_event.connect(on_key_press);
         button_press_event.connect(on_button_press);
+        gesture = new Gtk.GestureMultiPress(this);
+        gesture.pressed.connect(on_gesture_pressed);
 
         // Set up drag and drop.
         Gtk.drag_source_set(this, Gdk.ModifierType.BUTTON1_MASK, FolderList.Tree.TARGET_ENTRY_LIST,
@@ -270,6 +272,53 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
         return parent.get_vadjustment();
     }
 
+    private void on_gesture_pressed(int n_press, double x, double y) {
+        if (gesture.get_current_button() != Gdk.BUTTON_PRIMARY)
+            return;
+
+        Gtk.TreePath? path;
+        get_path_at_pos((int) x, (int) y, out path, null, null, null);
+
+        // If the user clicked in an empty area, do nothing.
+        if (path == null)
+            return;
+
+        Geary.App.Conversation? c = get_model().get_conversation_at_path(path);
+        if (c == null)
+            return;
+
+        Gdk.Event event = gesture.get_last_event(gesture.get_current_sequence());
+        Gdk.ModifierType modifiers = Gtk.accelerator_get_default_mod_mask();
+
+        Gdk.ModifierType state_mask;
+        event.get_state(out state_mask);
+
+        if ((state_mask & modifiers) == 0 && n_press == 1) {
+            conversation_activated(c, true);
+        } else if ((state_mask & modifiers) == Gdk.ModifierType.SHIFT_MASK && n_press == 2) {
+            conversation_activated(c);
+        }
+    }
+
+    private bool on_key_press(Gdk.EventKey event) {
+        if (this.selected.size != 1)
+            return false;
+
+        Geary.App.Conversation? c = this.selected.to_array()[0];
+        if (c == null)
+            return false;
+
+        Gdk.ModifierType modifiers = Gtk.accelerator_get_default_mod_mask();
+
+        if (event.keyval == Gdk.Key.Return ||
+            event.keyval == Gdk.Key.ISO_Enter ||
+            event.keyval == Gdk.Key.KP_Enter ||
+            event.keyval == Gdk.Key.space ||
+            event.keyval == Gdk.Key.KP_Space)
+            conversation_activated(c, !((event.state & modifiers) == Gdk.ModifierType.SHIFT_MASK));
+        return false;
+    }
+
     private bool on_button_press(Gdk.EventButton event) {
         // Get the coordinates on the cell as well as the clicked path.
         int cell_x;
@@ -576,12 +625,6 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
         return false;
     }
 
-    private void on_row_activated(Gtk.TreePath path) {
-        Geary.App.Conversation? c = get_model().get_conversation_at_path(path);
-        if (c != null)
-            conversation_activated(c);
-    }
-
     // Enable/disable hover effect on all selected cells.
     private void set_hover_selected(bool hover) {
         ConversationListCellRenderer.set_hover_selected(hover);
-- 
2.29.2

