[geary/wip/730682-refine-convo-list] Implement single-conversation actions.
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/730682-refine-convo-list] Implement single-conversation actions.
- Date: Wed, 3 Jan 2018 09:03:51 +0000 (UTC)
commit d6abef8b2a297376ac1df0ce0d2d2afa40cd2063
Author: Michael James Gratton <mike vee net>
Date: Wed Jan 3 19:57:29 2018 +1100
Implement single-conversation actions.
This enables both context menus and per-conversatio flagging in the
conversation list.
* src/client/components/main-window.vala (MainWindow): Split conversation
actions up into single-conversation actions and possibly-multiple
conversation highlighted actions, update existing uses of highlighted
action names. Add implementation for all single-conversations
actions. Hook up to new ConversationList context menu signal, query to
determine policy and show an appropriately updated context menu when
the query comes back.
* src/client/conversation-list/conversation-list-item.vala
(ConversationListItem): Update star/unstar buttons actio target based
on the item's conversation.
* src/client/conversation-list/conversation-list.vala (ConversationList):
Load the default context menu from resource, look for context-menu
clicks and fire signal so get it shown as appropriate.
po/POTFILES.in | 1 +
src/client/components/main-window.vala | 381 +++++++++++++++++---
.../conversation-list/conversation-list-item.vala | 21 ++
.../conversation-list/conversation-list.vala | 32 ++
src/engine/util/util-collection.vala | 8 +
ui/CMakeLists.txt | 1 +
ui/conversation-action-bar.ui | 18 +-
ui/conversation-list-menus.ui | 46 +++
8 files changed, 454 insertions(+), 54 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 366745e..c2a8947 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -410,6 +410,7 @@ ui/conversation-email.ui
ui/conversation-email-attachment-view.ui
ui/conversation-email-menus.ui
ui/conversation-list-item.ui
+ui/conversation-list-menus.ui
ui/conversation-message-menus.ui
ui/conversation-message.ui
ui/conversation-viewer.ui
diff --git a/src/client/components/main-window.vala b/src/client/components/main-window.vala
index 121b33a..745ad5a 100644
--- a/src/client/components/main-window.vala
+++ b/src/client/components/main-window.vala
@@ -10,17 +10,27 @@
public class MainWindow : Gtk.ApplicationWindow {
- public const string ACTION_ARCHIVE = "conversation-archive";
- public const string ACTION_COPY = "conversation-copy";
- public const string ACTION_DELETE = "conversation-delete";
- public const string ACTION_JUNK = "conversation-junk";
- public const string ACTION_MARK_READ = "conversation-mark-read";
- public const string ACTION_MARK_STARRED = "conversation-mark-starred";
- public const string ACTION_MARK_UNREAD = "conversation-mark-unread";
- public const string ACTION_MARK_UNSTARRED = "conversation-mark-unstarred";
- public const string ACTION_MOVE = "conversation-move";
- public const string ACTION_RESTORE = "conversation-restore";
- public const string ACTION_TRASH = "conversation-trash";
+ public const string ACTION_CONVERSATION_ARCHIVE = "conversation-archive";
+ public const string ACTION_CONVERSATION_DELETE = "conversation-delete";
+ public const string ACTION_CONVERSATION_JUNK = "conversation-junk";
+ public const string ACTION_CONVERSATION_MARK_READ = "conversation-mark-read";
+ public const string ACTION_CONVERSATION_MARK_STARRED = "conversation-mark-starred";
+ public const string ACTION_CONVERSATION_MARK_UNREAD = "conversation-mark-unread";
+ public const string ACTION_CONVERSATION_MARK_UNSTARRED = "conversation-mark-unstarred";
+ public const string ACTION_CONVERSATION_RESTORE = "conversation-restore";
+ public const string ACTION_CONVERSATION_TRASH = "conversation-trash";
+
+ public const string ACTION_HIGHLIGHTED_ARCHIVE = "highlighted-archive";
+ public const string ACTION_HIGHLIGHTED_COPY = "highlighted-copy";
+ public const string ACTION_HIGHLIGHTED_DELETE = "highlighted-delete";
+ public const string ACTION_HIGHLIGHTED_JUNK = "highlighted-junk";
+ public const string ACTION_HIGHLIGHTED_MARK_READ = "highlighted-mark-read";
+ public const string ACTION_HIGHLIGHTED_MARK_STARRED = "highlighted-mark-starred";
+ public const string ACTION_HIGHLIGHTED_MARK_UNREAD = "highlighted-mark-unread";
+ public const string ACTION_HIGHLIGHTED_MARK_UNSTARRED = "highlighted-mark-unstarred";
+ public const string ACTION_HIGHLIGHTED_MOVE = "highlighted-move";
+ public const string ACTION_HIGHLIGHTED_RESTORE = "highlighted-restore";
+ public const string ACTION_HIGHLIGHTED_TRASH = "highlighted-trash";
public const string ACTION_SHOW_COPY = "show-copy";
public const string ACTION_SHOW_MOVE = "show-move";
@@ -33,17 +43,33 @@ public class MainWindow : Gtk.ApplicationWindow {
private const int STATUS_BAR_HEIGHT = 18;
private const ActionEntry[] action_entries = {
- { ACTION_ARCHIVE, on_conversation_archive },
- { ACTION_COPY, on_conversation_copy, "as" },
- { ACTION_DELETE, on_conversation_delete },
- { ACTION_JUNK, on_conversation_junk },
- { ACTION_MARK_READ, on_conversation_mark_read },
- { ACTION_MARK_STARRED, on_conversation_mark_starred },
- { ACTION_MARK_UNREAD, on_conversation_mark_unread },
- { ACTION_MARK_UNSTARRED, on_conversation_mark_unstarred },
- { ACTION_MOVE, on_conversation_move, "as" },
- { ACTION_RESTORE, on_conversation_restore },
- { ACTION_TRASH, on_conversation_trash },
+ // XXX Using "(yxx)" as the param type here is sketch since we
+ // don't know in advance what type is used to serialise
+ // ConversationListItem.id, since its type (EmailIdentifier)
+ // is abstract
+ { ACTION_CONVERSATION_ARCHIVE, on_conversation_archive, "(yxx)" },
+ { ACTION_CONVERSATION_MARK_READ, on_conversation_mark_read, "(yxx)" },
+ { ACTION_CONVERSATION_MARK_STARRED, on_conversation_mark_starred, "(yxx)" },
+ { ACTION_CONVERSATION_MARK_UNSTARRED, on_conversation_mark_unstarred, "(yxx)" },
+ { ACTION_CONVERSATION_MARK_UNREAD, on_conversation_mark_unread, "(yxx)" },
+ { ACTION_CONVERSATION_DELETE, on_conversation_delete, "(yxx)" },
+ { ACTION_CONVERSATION_JUNK, on_conversation_junk, "(yxx)" },
+ { ACTION_CONVERSATION_RESTORE, on_conversation_restore, "(yxx)" },
+ { ACTION_CONVERSATION_TRASH, on_conversation_trash, "(yxx)" },
+
+ { ACTION_HIGHLIGHTED_ARCHIVE, on_highlighted_archive },
+ { ACTION_HIGHLIGHTED_COPY, on_highlighted_copy,
+ Geary.FolderPath.VARIANT_TYPE },
+ { ACTION_HIGHLIGHTED_DELETE, on_highlighted_delete },
+ { ACTION_HIGHLIGHTED_JUNK, on_highlighted_junk },
+ { ACTION_HIGHLIGHTED_MARK_READ, on_highlighted_mark_read },
+ { ACTION_HIGHLIGHTED_MARK_STARRED, on_highlighted_mark_starred },
+ { ACTION_HIGHLIGHTED_MARK_UNREAD, on_highlighted_mark_unread },
+ { ACTION_HIGHLIGHTED_MARK_UNSTARRED, on_highlighted_mark_unstarred },
+ { ACTION_HIGHLIGHTED_MOVE, on_highlighted_move,
+ Geary.FolderPath.VARIANT_TYPE },
+ { ACTION_HIGHLIGHTED_RESTORE, on_highlighted_restore },
+ { ACTION_HIGHLIGHTED_TRASH, on_highlighted_trash },
{ ACTION_SHOW_COPY },
{ ACTION_SHOW_MOVE },
@@ -187,6 +213,7 @@ public class MainWindow : Gtk.ApplicationWindow {
Object(application: application);
this.conversation_list = new ConversationList(application.config);
+ this.conversation_list.context_menu_requested.connect(on_context_menu_requested);
this.conversation_list.conversation_selection_changed.connect(on_conversation_selection_changed);
this.conversation_list.conversation_activated.connect(on_conversation_activated);
this.conversation_list.items_marked.connect(on_conversation_items_marked);
@@ -416,6 +443,23 @@ public class MainWindow : Gtk.ApplicationWindow {
}
/**
+ * Returns the conversation from an email id action param, if valid.
+ */
+ private Geary.App.Conversation? variant_to_conversation(Variant? param) {
+ Geary.App.Conversation? target = null;
+ if (param != null) {
+ try {
+ Geary.EmailIdentifier id =
+ this.current_folder.account.to_email_identifier(param);
+ target = this.current_conversations.get_conversation_for_email(id);
+ } catch (Geary.EngineError err) {
+ debug("Error getting action email id parameter: %s", err.message);
+ }
+ }
+ return target;
+ }
+
+ /**
* Returns email ids from all highlighted conversations, if any.
*/
private Gee.List<Geary.EmailIdentifier> get_highlighted_email() {
@@ -524,40 +568,40 @@ public class MainWindow : Gtk.ApplicationWindow {
FolderActionPolicy policy = this.highlighted_policy ?? this.folder_policy;
bool has_highlighted = this.conversation_list.has_highlighted_conversations;
- get_action(ACTION_ARCHIVE).set_enabled(
+ get_action(ACTION_HIGHLIGHTED_ARCHIVE).set_enabled(
has_highlighted && policy.can_archive
);
- get_action(ACTION_DELETE).set_enabled(
+ get_action(ACTION_HIGHLIGHTED_DELETE).set_enabled(
has_highlighted && policy.can_delete
);
- get_action(ACTION_JUNK).set_enabled(
+ get_action(ACTION_HIGHLIGHTED_JUNK).set_enabled(
has_highlighted && policy.can_junk
);
- get_action(ACTION_RESTORE).set_enabled(
+ get_action(ACTION_HIGHLIGHTED_RESTORE).set_enabled(
has_highlighted && policy.can_restore
);
- get_action(ACTION_TRASH).set_enabled(
+ get_action(ACTION_HIGHLIGHTED_TRASH).set_enabled(
has_highlighted && policy.can_trash
);
- get_action(ACTION_COPY).set_enabled(
+ get_action(ACTION_HIGHLIGHTED_COPY).set_enabled(
has_highlighted && policy.can_copy
);
get_action(ACTION_SHOW_COPY).set_enabled(
has_highlighted && policy.can_copy
);
- get_action(ACTION_MOVE).set_enabled(
+ get_action(ACTION_HIGHLIGHTED_MOVE).set_enabled(
has_highlighted && policy.can_move
);
get_action(ACTION_SHOW_MOVE).set_enabled(
has_highlighted && policy.can_move
);
- SimpleAction mark_read = get_action(ACTION_MARK_READ);
- SimpleAction mark_unread = get_action(ACTION_MARK_UNREAD);
- SimpleAction mark_starred = get_action(ACTION_MARK_STARRED);
- SimpleAction mark_unstarred = get_action(ACTION_MARK_UNSTARRED);
+ SimpleAction mark_read = get_action(ACTION_HIGHLIGHTED_MARK_READ);
+ SimpleAction mark_unread = get_action(ACTION_HIGHLIGHTED_MARK_UNREAD);
+ SimpleAction mark_starred = get_action(ACTION_HIGHLIGHTED_MARK_STARRED);
+ SimpleAction mark_unstarred = get_action(ACTION_HIGHLIGHTED_MARK_UNSTARRED);
if (has_highlighted && policy.can_mark) {
bool has_read = false;
bool has_unread = false;
@@ -588,6 +632,75 @@ public class MainWindow : Gtk.ApplicationWindow {
}
}
+ private void show_conversation_context_menu(Menu menu,
+ ConversationListItem target,
+ FolderActionPolicy policy) {
+
+ Geary.App.Conversation conversation = target.conversation;
+ Variant action_target = target.id.to_variant();
+ Menu target_menu = new Menu();
+ for (int i = 0; i < menu.get_n_items(); i++) {
+ Menu? existing = (Menu) menu.get_item_link(i, Menu.LINK_SECTION);
+ if (existing != null) {
+ Menu updated = (Menu) new Menu();
+ GtkUtil.menu_foreach(
+ existing, (label, action_name, target) => {
+ bool enabled = false;
+ // Remove "win." prefix before checking
+ switch (action_name.substring(4, action_name.length - 4)) {
+ case ACTION_CONVERSATION_ARCHIVE:
+ enabled = policy.can_archive;
+ break;
+
+ case ACTION_CONVERSATION_DELETE:
+ enabled = policy.can_delete;
+ break;
+
+ case ACTION_CONVERSATION_JUNK:
+ enabled = policy.can_junk;
+ break;
+
+ case ACTION_CONVERSATION_MARK_READ:
+ enabled = policy.can_mark && conversation.is_unread();
+ break;
+
+ case ACTION_CONVERSATION_MARK_STARRED:
+ enabled = policy.can_mark && !conversation.is_flagged();
+ break;
+
+ case ACTION_CONVERSATION_MARK_UNREAD:
+ enabled = policy.can_mark && !conversation.is_unread();
+ break;
+
+ case ACTION_CONVERSATION_MARK_UNSTARRED:
+ enabled = policy.can_mark && conversation.is_flagged();
+ break;
+
+ case ACTION_CONVERSATION_RESTORE:
+ enabled = policy.can_restore;
+ break;
+
+ case ACTION_CONVERSATION_TRASH:
+ enabled = policy.can_trash;
+ break;
+ }
+
+ if (enabled) {
+ MenuItem item = new MenuItem(label, null);
+ item.set_action_and_target_value(
+ action_name, action_target
+ );
+ updated.append_item(item);
+ }
+ });
+ target_menu.append_section(null, updated);
+ }
+ }
+
+ Gtk.Widget popover = new Gtk.Popover.from_model(target, target_menu);
+ popover.show();
+ }
+
private inline void check_shift_event(Gdk.EventKey event) {
// FIXME: it's possible the user will press two shift keys. We want
// the shift key to report as released when they release ALL of them.
@@ -786,6 +899,25 @@ public class MainWindow : Gtk.ApplicationWindow {
this.folder_paned, "position");
}
+ private void on_context_menu_requested(Menu menu, ConversationListItem target) {
+ this.application.controller.query_supported_operations.begin(
+ target.conversation.get_email_ids(),
+ this.load_cancellable,
+ (obj, res) => {
+ Gee.Set<Type>? supported = null;
+ try {
+ supported = this.application.controller.query_supported_operations.end(res);
+ } catch (Error err) {
+ debug("Error querying supported actions: %s", err.message);
+ }
+ show_conversation_context_menu(
+ menu,
+ target,
+ new FolderActionPolicy(this.current_folder, supported)
+ );
+ });
+ }
+
private void on_conversation_selection_changed(Geary.App.Conversation? selection) {
show_conversation(selection);
query_supported_actions();
@@ -911,6 +1043,165 @@ public class MainWindow : Gtk.ApplicationWindow {
}
private void on_conversation_archive(Action action, Variant? param) {
+ Geary.App.Conversation? target = variant_to_conversation(param);
+ if (target != null) {
+ this.application.controller.move_conversations_special.begin(
+ Geary.Collection.new_unary_linked_list(target),
+ Geary.SpecialFolderType.ARCHIVE,
+ (obj, ret) => {
+ try {
+ this.application.controller.move_conversations_special.end(ret);
+ } catch (Error err) {
+ report_problem(action, param, err);
+ }
+ }
+ );
+ }
+ }
+
+ private void on_conversation_delete(Action action, Variant? param) {
+ Geary.App.Conversation? target = variant_to_conversation(param);
+ if (target != null) {
+ this.application.controller.delete_conversations.begin(
+ Geary.Collection.new_unary_linked_list(target),
+ (obj, ret) => {
+ try {
+ this.application.controller.delete_conversations.end(ret);
+ } catch (Error err) {
+ report_problem(action, param, err);
+ }
+ }
+ );
+ }
+ }
+
+ private void on_conversation_junk(Action action, Variant? param) {
+ Geary.App.Conversation? target = variant_to_conversation(param);
+ if (target != null) {
+ this.application.controller.move_conversations_special.begin(
+ Geary.Collection.new_unary_linked_list(target),
+ Geary.SpecialFolderType.SPAM,
+ (obj, ret) => {
+ try {
+ this.application.controller.move_conversations_special.end(ret);
+ } catch (Error err) {
+ report_problem(action, param, err);
+ }
+ }
+ );
+ }
+ }
+
+ private void on_conversation_mark_read(Action action, Variant? param) {
+ Geary.EmailFlags flags = new Geary.EmailFlags();
+ flags.add(Geary.EmailFlags.UNREAD);
+
+ Geary.App.Conversation? target = variant_to_conversation(param);
+ if (target != null) {
+ this.application.controller.mark_email.begin(
+ target.get_email_ids(), null, flags,
+ (obj, ret) => {
+ try {
+ this.application.controller.mark_email.end(ret);
+ } catch (Error err) {
+ report_problem(action, param, err);
+ }
+ }
+ );
+ }
+ }
+
+ private void on_conversation_mark_starred(Action action, Variant? param) {
+ Geary.EmailFlags flags = new Geary.EmailFlags();
+ flags.add(Geary.EmailFlags.FLAGGED);
+
+ Geary.App.Conversation? target = variant_to_conversation(param);
+ if (target != null) {
+ this.application.controller.mark_email.begin(
+ target.get_email_ids(), flags, null,
+ (obj, ret) => {
+ try {
+ this.application.controller.mark_email.end(ret);
+ } catch (Error err) {
+ report_problem(action, param, err);
+ }
+ }
+ );
+ }
+ }
+
+ private void on_conversation_mark_unread(Action action, Variant? param) {
+ Geary.EmailFlags flags = new Geary.EmailFlags();
+ flags.add(Geary.EmailFlags.UNREAD);
+
+ Geary.App.Conversation? target = variant_to_conversation(param);
+ if (target != null) {
+ this.application.controller.mark_email.begin(
+ target.get_email_ids(), flags, null,
+ (obj, ret) => {
+ try {
+ this.application.controller.mark_email.end(ret);
+ } catch (Error err) {
+ report_problem(action, param, err);
+ }
+ }
+ );
+ }
+ }
+
+ private void on_conversation_mark_unstarred(Action action, Variant? param) {
+ Geary.EmailFlags flags = new Geary.EmailFlags();
+ flags.add(Geary.EmailFlags.FLAGGED);
+
+ Geary.App.Conversation? target = variant_to_conversation(param);
+ if (target != null) {
+ this.application.controller.mark_email.begin(
+ target.get_email_ids(), null, flags,
+ (obj, ret) => {
+ try {
+ this.application.controller.mark_email.end(ret);
+ } catch (Error err) {
+ report_problem(action, param, err);
+ }
+ }
+ );
+ }
+ }
+
+ private void on_conversation_restore(Action action, Variant? param) {
+ Geary.App.Conversation? target = variant_to_conversation(param);
+ if (target != null) {
+ this.application.controller.restore_conversations.begin(
+ Geary.Collection.new_unary_linked_list(target),
+ (obj, ret) => {
+ try {
+ this.application.controller.restore_conversations.end(ret);
+ } catch (Error err) {
+ report_problem(action, param, err);
+ }
+ }
+ );
+ }
+ }
+
+ private void on_conversation_trash(Action action, Variant? param) {
+ Geary.App.Conversation? target = variant_to_conversation(param);
+ if (target != null) {
+ this.application.controller.move_conversations_special.begin(
+ Geary.Collection.new_unary_linked_list(target),
+ Geary.SpecialFolderType.TRASH,
+ (obj, ret) => {
+ try {
+ this.application.controller.move_conversations_special.end(ret);
+ } catch (Error err) {
+ report_problem(action, param, err);
+ }
+ }
+ );
+ }
+ }
+
+ private void on_highlighted_archive(Action action, Variant? param) {
this.application.controller.move_conversations_special.begin(
this.conversation_list.get_highlighted_conversations(),
Geary.SpecialFolderType.ARCHIVE,
@@ -924,7 +1215,7 @@ public class MainWindow : Gtk.ApplicationWindow {
);
}
- private void on_conversation_copy(Action action, Variant? param) {
+ private void on_highlighted_copy(Action action, Variant? param) {
Geary.FolderPath? destination = null;
if (param != null) {
try {
@@ -950,7 +1241,7 @@ public class MainWindow : Gtk.ApplicationWindow {
}
}
- private void on_conversation_delete(Action action, Variant? param) {
+ private void on_highlighted_delete(Action action, Variant? param) {
if (confirm_delete()) {
this.application.controller.delete_conversations.begin(
this.conversation_list.get_highlighted_conversations(),
@@ -965,7 +1256,7 @@ public class MainWindow : Gtk.ApplicationWindow {
}
}
- private void on_conversation_junk(Action action, Variant? param) {
+ private void on_highlighted_junk(Action action, Variant? param) {
this.application.controller.move_conversations_special.begin(
this.conversation_list.get_highlighted_conversations(),
Geary.SpecialFolderType.SPAM,
@@ -979,7 +1270,7 @@ public class MainWindow : Gtk.ApplicationWindow {
);
}
- private void on_conversation_mark_read(Action action, Variant? param) {
+ private void on_highlighted_mark_read(Action action, Variant? param) {
Geary.EmailFlags flags = new Geary.EmailFlags();
flags.add(Geary.EmailFlags.UNREAD);
@@ -1009,7 +1300,7 @@ public class MainWindow : Gtk.ApplicationWindow {
}
}
- private void on_conversation_mark_unread(Action action, Variant? param) {
+ private void on_highlighted_mark_unread(Action action, Variant? param) {
Geary.EmailFlags flags = new Geary.EmailFlags();
flags.add(Geary.EmailFlags.UNREAD);
@@ -1039,7 +1330,7 @@ public class MainWindow : Gtk.ApplicationWindow {
}
}
- private void on_conversation_mark_starred(Action action, Variant? param) {
+ private void on_highlighted_mark_starred(Action action, Variant? param) {
Geary.EmailFlags flags = new Geary.EmailFlags();
flags.add(Geary.EmailFlags.FLAGGED);
this.application.controller.mark_email.begin(
@@ -1054,7 +1345,7 @@ public class MainWindow : Gtk.ApplicationWindow {
);
}
- private void on_conversation_mark_unstarred(Action action, Variant? param) {
+ private void on_highlighted_mark_unstarred(Action action, Variant? param) {
Geary.EmailFlags flags = new Geary.EmailFlags();
flags.add(Geary.EmailFlags.FLAGGED);
this.application.controller.mark_email.begin(
@@ -1069,7 +1360,7 @@ public class MainWindow : Gtk.ApplicationWindow {
);
}
- private void on_conversation_move(Action action, Variant? param) {
+ private void on_highlighted_move(Action action, Variant? param) {
Geary.FolderPath? destination = null;
if (param != null) {
try {
@@ -1095,7 +1386,7 @@ public class MainWindow : Gtk.ApplicationWindow {
}
}
- private void on_conversation_restore(Action action, Variant? param) {
+ private void on_highlighted_restore(Action action, Variant? param) {
this.application.controller.restore_conversations.begin(
this.conversation_list.get_highlighted_conversations(),
(obj, ret) => {
@@ -1108,7 +1399,7 @@ public class MainWindow : Gtk.ApplicationWindow {
);
}
- private void on_conversation_trash(Action action, Variant? param) {
+ private void on_highlighted_trash(Action action, Variant? param) {
this.application.controller.move_conversations_special.begin(
this.conversation_list.get_highlighted_conversations(),
Geary.SpecialFolderType.TRASH,
@@ -1123,11 +1414,11 @@ public class MainWindow : Gtk.ApplicationWindow {
}
public void on_copy_folder(Geary.Folder target) {
- get_action(ACTION_COPY).activate(target.path.to_variant());
+ get_action(ACTION_HIGHLIGHTED_COPY).activate(target.path.to_variant());
}
public void on_move_folder(Geary.Folder target) {
- get_action(ACTION_MOVE).activate(target.path.to_variant());
+ get_action(ACTION_HIGHLIGHTED_MOVE).activate(target.path.to_variant());
}
private void on_selection_mode_enabled() {
diff --git a/src/client/conversation-list/conversation-list-item.vala
b/src/client/conversation-list/conversation-list-item.vala
index 52de137..e9c855f 100644
--- a/src/client/conversation-list/conversation-list-item.vala
+++ b/src/client/conversation-list/conversation-list-item.vala
@@ -79,6 +79,16 @@ public class ConversationListItem : Gtk.ListBoxRow {
/** Determines if this row is marked for selection mode */
public bool is_marked { get; private set; default = false; }
+ /*
+ * An email id that can be used to look up this conversation.
+ *
+ * This is only guaranteed to remain stable while the conversation
+ * remains trimmed, after that it may have changed. Thus it should
+ * only be used for transient values such as context menu action
+ * targets.
+ */
+ public Geary.EmailIdentifier id { get; private set; }
+
[GtkChild]
private Gtk.Button star_button;
@@ -256,6 +266,17 @@ public class ConversationListItem : Gtk.ListBoxRow {
if (count <= 1) {
this.count.hide();
}
+
+ // This must be done every time the conversation is trimmed
+ Geary.Email? email = this.conversation.get_earliest_recv_email(
+ Geary.App.Conversation.Location.ANYWHERE
+ );
+ if (email != null) {
+ Variant target = email.id.to_variant();
+ this.star_button.set_action_target_value(target);
+ this.unstar_button.set_action_target_value(target);
+ this.id = email.id;
+ }
}
private string get_participants_markup() {
diff --git a/src/client/conversation-list/conversation-list.vala
b/src/client/conversation-list/conversation-list.vala
index c84923c..d9178ab 100644
--- a/src/client/conversation-list/conversation-list.vala
+++ b/src/client/conversation-list/conversation-list.vala
@@ -46,6 +46,7 @@ public class ConversationList : Gtk.ListBox {
}
private Configuration config;
+ private Menu context_menu;
private int selected_index = -1;
private bool selection_frozen = false;
private Gee.Map<Geary.App.Conversation,ConversationListItem> marked =
@@ -74,6 +75,15 @@ public class ConversationList : Gtk.ListBox {
}
/**
+ * Fired when the user requested a context menu for an item.
+ *
+ * The application should set targets for the given menu model and
+ * selectively hide unwanted actions before displaying the popup
+ * on the item.
+ */
+ public signal void context_menu_requested(Menu menu, ConversationListItem target);
+
+ /**
* Fired when a list item was targeted with a selection gesture.
*
* Selection gestures include Ctrl-click or Shift-click on the
@@ -104,6 +114,11 @@ public class ConversationList : Gtk.ListBox {
selection_changed();
});
this.show.connect(on_show);
+
+ Gtk.Builder builder = new Gtk.Builder.from_resource(
+ "/org/gnome/Geary/conversation-list-menus.ui"
+ );
+ this.context_menu = (Menu) builder.get_object("context_menu");
}
/**
@@ -203,6 +218,23 @@ public class ConversationList : Gtk.ListBox {
public override bool button_press_event(Gdk.EventButton event) {
bool ret = Gdk.EVENT_PROPAGATE;
+ if (event.button == 3) {
+ ConversationListItem? clicked =
+ get_row_at_y((int) event.y) as ConversationListItem;
+ if (clicked != null) {
+ context_menu_requested(this.context_menu, clicked);
+ ret = Gdk.EVENT_STOP;
+ }
+ }
+
+ if (ret == Gdk.EVENT_PROPAGATE) {
+ ret = base.button_press_event(event);
+ }
+ return ret;
+ }
+
+ public override bool button_release_event(Gdk.EventButton event) {
+ bool ret = Gdk.EVENT_PROPAGATE;
if (event.button == 1) {
ConversationListItem? clicked =
get_row_at_y((int) event.y) as ConversationListItem;
diff --git a/src/engine/util/util-collection.vala b/src/engine/util/util-collection.vala
index 6859569..7fae553 100644
--- a/src/engine/util/util-collection.vala
+++ b/src/engine/util/util-collection.vala
@@ -12,6 +12,14 @@ public inline bool is_empty(Gee.Collection? c) {
return c == null || c.size == 0;
}
+/** Returns a linked list containing one element. */
+public Gee.LinkedList<G> new_unary_linked_list<G>(G element,
+ owned Gee.EqualDataFunc<G>? equal_func = null) {
+ Gee.LinkedList<G> list = new Gee.LinkedList<G>(equal_func);
+ list.add(element);
+ return list;
+}
+
// A substitute for ArrayList<G>.wrap() for compatibility with older versions of Gee.
public Gee.ArrayList<G> array_list_wrap<G>(G[] a, owned Gee.EqualDataFunc<G>? equal_func = null) {
Gee.ArrayList<G> list = new Gee.ArrayList<G>(equal_func);
diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt
index 5164978..b2d9645 100644
--- a/ui/CMakeLists.txt
+++ b/ui/CMakeLists.txt
@@ -17,6 +17,7 @@ set(RESOURCE_LIST
STRIPBLANKS "conversation-email-attachment-view.ui"
STRIPBLANKS "conversation-email-menus.ui"
STRIPBLANKS "conversation-list-item.ui"
+ STRIPBLANKS "conversation-list-menus.ui"
STRIPBLANKS "conversation-message.ui"
STRIPBLANKS "conversation-message-menus.ui"
STRIPBLANKS "conversation-viewer.ui"
diff --git a/ui/conversation-action-bar.ui b/ui/conversation-action-bar.ui
index 0cf111d..9ba0a79 100644
--- a/ui/conversation-action-bar.ui
+++ b/ui/conversation-action-bar.ui
@@ -15,7 +15,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="action_name">win.conversation-mark-read</property>
+ <property name="action_name">win.highlighted-mark-read</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
@@ -33,7 +33,7 @@
<object class="GtkButton" id="mark_unread_action">
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="action_name">win.conversation-mark-unread</property>
+ <property name="action_name">win.highlighted-mark-unread</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
@@ -52,7 +52,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="action_name">win.conversation-mark-starred</property>
+ <property name="action_name">win.highlighted-mark-starred</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
@@ -70,7 +70,7 @@
<object class="GtkButton" id="mark_unstarred_action">
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="action_name">win.conversation-mark-unstarred</property>
+ <property name="action_name">win.highlighted-mark-unstarred</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
@@ -102,7 +102,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="action_name">win.conversation-archive</property>
+ <property name="action_name">win.highlighted-archive</property>
</object>
<packing>
<property name="left_attach">0</property>
@@ -115,7 +115,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="action_name">win.conversation-restore</property>
+ <property name="action_name">win.highlighted-restore</property>
</object>
<packing>
<property name="left_attach">1</property>
@@ -179,7 +179,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="action_name">win.conversation-junk</property>
+ <property name="action_name">win.highlighted-junk</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
@@ -198,7 +198,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="action_name">win.conversation-trash</property>
+ <property name="action_name">win.highlighted-trash</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
@@ -219,7 +219,7 @@
<property name="receives_default">True</property>
<property name="halign">end</property>
<property name="hexpand">True</property>
- <property name="action_name">win.conversation-delete</property>
+ <property name="action_name">win.highlighted-delete</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
diff --git a/ui/conversation-list-menus.ui b/ui/conversation-list-menus.ui
new file mode 100644
index 0000000..ffdc6f3
--- /dev/null
+++ b/ui/conversation-list-menus.ui
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<interface>
+ <menu id="context_menu">
+ <section id="context_menu_mark">
+ <item>
+ <attribute name="label" translatable="yes">Mark _Read</attribute>
+ <attribute name="action">win.conversation-mark-read</attribute>
+ </item>
+ <item>
+ <attribute name="label" translatable="yes">Un-mark _Read</attribute>
+ <attribute name="action">win.conversation-mark-unread</attribute>
+ </item>
+ <item>
+ <attribute name="label" translatable="yes">Mark _Starred</attribute>
+ <attribute name="action">win.conversation-mark-starred</attribute>
+ </item>
+ <item>
+ <attribute name="label" translatable="yes">Un-mark _Starred</attribute>
+ <attribute name="action">win.conversation-mark-unstarred</attribute>
+ </item>
+ </section>
+ <section id="context_menu_actions">
+ <item>
+ <attribute name="label" translatable="yes">_Archive</attribute>
+ <attribute name="action">win.conversation-archive</attribute>
+ </item>
+ <item>
+ <attribute name="label" translatable="yes">R_estore</attribute>
+ <attribute name="action">win.conversation-restore</attribute>
+ </item>
+ <item>
+ <attribute name="label" translatable="yes">_Junk</attribute>
+ <attribute name="action">win.conversation-junk</attribute>
+ </item>
+ <item>
+ <attribute name="label" translatable="yes">_Trash</attribute>
+ <attribute name="action">win.conversation-trash</attribute>
+ </item>
+ <item>
+ <attribute name="label" translatable="yes">_Delete</attribute>
+ <attribute name="action">win.conversation-delete</attribute>
+ </item>
+ </section>
+ </menu>
+</interface>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]