diff --git a/src/MessageList/FolderPopover/FolderPopover.vala b/src/MessageList/FolderPopover/FolderPopover.vala new file mode 100644 index 000000000..6d3c6029e --- /dev/null +++ b/src/MessageList/FolderPopover/FolderPopover.vala @@ -0,0 +1,102 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2017-2023 elementary, Inc. (https://elementary.io) + * + * Authored by: Leonhard Kargl + */ + +public class Mail.FolderPopover : Gtk.Popover { + private Gtk.SearchEntry search_entry; + private Gtk.ListBox list_box; + + construct { + search_entry = new Gtk.SearchEntry () { + margin_top = 3, + margin_bottom = 3, + margin_start = 3, + margin_end = 3 + }; + + list_box = new Gtk.ListBox () { + activate_on_single_click = true + }; + list_box.set_sort_func (sort_func); + list_box.set_filter_func (filter_func); + + var scrolled_window = new Gtk.ScrolledWindow (null, null) { + child = list_box, + hexpand = true, + vexpand = true, + margin_bottom = 3, + hscrollbar_policy = NEVER + }; + + var box = new Gtk.Box (VERTICAL, 0); + box.add (search_entry); + box.add (new Gtk.Separator (HORIZONTAL)); + box.add (scrolled_window); + box.show_all (); + + width_request = 250; + height_request = 350; + child = box; + + search_entry.search_changed.connect (() => list_box.invalidate_filter ()); + + list_box.row_activated.connect ((row) => { + if (row is FolderRow) { + var folder_row = (FolderRow)row; + + popdown (); + ((MainWindow)get_toplevel ()).activate_action (MainWindow.ACTION_MOVE, row.folder_info.full_name); + } + }); + } + + public void set_store (Camel.Store store) { + foreach (var child in list_box.get_children ()) { + child.destroy (); + } + + store.get_folder_info.begin (null, Camel.StoreGetFolderInfoFlags.RECURSIVE, GLib.Priority.DEFAULT, null, (obj, res) => { + try { + var folder_info = store.get_folder_info.end (res); + update (folder_info, 0, store); + } catch (Error e) { + critical (e.message); + } + }); + } + + private void update (Camel.FolderInfo top, int depth, Camel.Store store) { + var folder_info = top; + while (folder_info != null) { + list_box.add (new FolderRow (depth, folder_info, store)); + + if (folder_info.child != null) { + update (folder_info.child, depth + 1, store); + } + folder_info = folder_info.next; + } + } + + private int sort_func (Gtk.ListBoxRow row1, Gtk.ListBoxRow row2) { + if (row1 is FolderRow && row2 is FolderRow) { + var folder_row1 = (FolderRow) row1; + var folder_row2 = (FolderRow) row2; + + return folder_row1.pos - folder_row2.pos; + } + + return 0; + } + + private bool filter_func (Gtk.ListBoxRow row) { + if (row is FolderRow) { + var folder_row = (FolderRow)row; + return search_entry.text.down ().strip () in folder_row.folder_info.display_name.down (); + } + + return true; + } +} diff --git a/src/MessageList/FolderPopover/FolderRow.vala b/src/MessageList/FolderPopover/FolderRow.vala new file mode 100644 index 000000000..f73ac0aa0 --- /dev/null +++ b/src/MessageList/FolderPopover/FolderRow.vala @@ -0,0 +1,72 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2017-2023 elementary, Inc. (https://elementary.io) + * + * Authored by: Leonhard Kargl + */ + +public class Mail.FolderRow : Gtk.ListBoxRow { + public int depth { get; construct; } //Currently not used + public Camel.FolderInfo folder_info { get; construct; } + public Camel.Store store { get; construct; } + public int pos { get; construct; } + + public FolderRow (int depth, Camel.FolderInfo folder_info, Camel.Store store) { + Object (depth: depth, folder_info: folder_info, store: store); + } + + construct { + var icon = new Gtk.Image.from_icon_name ("folder", MENU) { + margin_end = 3 + }; + + var full_folder_info_flags = Utils.get_full_folder_info_flags (store, folder_info); + switch (full_folder_info_flags & Camel.FOLDER_TYPE_MASK) { + case Camel.FolderInfoFlags.TYPE_INBOX: + icon.set_from_icon_name ("mail-inbox", MENU); + pos = 1; + break; + case Camel.FolderInfoFlags.TYPE_DRAFTS: + icon.set_from_icon_name ("mail-drafts", MENU); + pos = 2; + break; + case Camel.FolderInfoFlags.TYPE_OUTBOX: + icon.set_from_icon_name ("mail-outbox", MENU); + pos = 3; + break; + case Camel.FolderInfoFlags.TYPE_SENT: + icon.set_from_icon_name ("mail-sent", MENU); + pos = 4; + break; + case Camel.FolderInfoFlags.TYPE_ARCHIVE: + icon.set_from_icon_name ("mail-archive", MENU); + pos = 5; + break; + case Camel.FolderInfoFlags.TYPE_TRASH: + icon.set_from_icon_name (folder_info.total == 0 ? "user-trash" : "user-trash-full", MENU); + pos = 6; + break; + case Camel.FolderInfoFlags.TYPE_JUNK: + icon.set_from_icon_name ("edit-flag", MENU); + pos = 7; + break; + default: + icon.set_from_icon_name ("folder", MENU); + pos = 8; + break; + } + + var box = new Gtk.Box (HORIZONTAL, 0) { + margin_top = 3, + margin_bottom = 3, + margin_start = 12, + margin_end = 12 + }; + + box.add (icon); + box.add (new Gtk.Label (folder_info.display_name)); + + child = box; + show_all (); + } +} diff --git a/src/MessageList/MessageList.vala b/src/MessageList/MessageList.vala index 0a2b71866..f942ddd92 100644 --- a/src/MessageList/MessageList.vala +++ b/src/MessageList/MessageList.vala @@ -9,8 +9,7 @@ public class Mail.MessageList : Gtk.Box { public signal void hovering_over_link (string? label, string? uri); public Hdy.HeaderBar headerbar { get; private set; } - private Menu move_menu; - private Gtk.MenuButton move_button; + private FolderPopover folder_popover; private Gtk.ListBox list_box; private Gtk.ScrolledWindow scrolled_window; private Gee.HashMap messages; @@ -85,14 +84,13 @@ public class Mail.MessageList : Gtk.Box { tooltip_text = _("Mark Conversation") }; - move_menu = new Menu (); + folder_popover = new FolderPopover (); - move_button = new Gtk.MenuButton () { + var move_button = new Gtk.MenuButton () { action_name = MainWindow.ACTION_PREFIX + MainWindow.ACTION_MODIFY, image = new Gtk.Image.from_icon_name ("folder", Gtk.IconSize.LARGE_TOOLBAR), tooltip_text = _("Move Conversation to…"), - menu_model = move_menu, - use_popover = false + popover = folder_popover }; var archive_button = new Gtk.Button.from_icon_name ("mail-archive", Gtk.IconSize.LARGE_TOOLBAR) { @@ -157,27 +155,6 @@ public class Mail.MessageList : Gtk.Box { add (scrolled_window); } - public void update_move_menu (Camel.FolderInfo top, int depth) { - /* Hackish way of indenting subfolders */ - var builder = new StringBuilder (); - for (int i = 0; i < depth; i++) { - builder.append (" "); - } - - var folder_info = top; - while (folder_info != null) { - move_menu.append ( - builder.str + folder_info.display_name, - Action.print_detailed_name (MainWindow.ACTION_PREFIX + MainWindow.ACTION_MOVE, folder_info.full_name) - ); - - if (folder_info.child != null) { - update_move_menu (folder_info.child, depth + 1); - } - folder_info = folder_info.next; - } - } - public void set_conversation (Camel.FolderThreadNode? node) { /* * Prevent the user from interacting with the message thread while it @@ -203,15 +180,7 @@ public class Mail.MessageList : Gtk.Box { can_move_thread (true); var store = node.message.summary.folder.parent_store; - store.get_folder_info.begin (null, Camel.StoreGetFolderInfoFlags.RECURSIVE, GLib.Priority.DEFAULT, null, (obj, res) => { - try { - var folder_info = store.get_folder_info.end (res); - move_menu.remove_all (); - update_move_menu (folder_info, 0); - } catch (Error e) { - critical (e.message); - } - }); + folder_popover.set_store (store); var item = new MessageListItem (node.message); list_box.add (item); diff --git a/src/meson.build b/src/meson.build index 7c7aa2ee7..fe3d4ce19 100644 --- a/src/meson.build +++ b/src/meson.build @@ -23,6 +23,8 @@ vala_files = files( 'FoldersView/GroupedFolderSourceItem.vala', 'FoldersView/SessionSourceItem.vala', 'MessageList/AttachmentButton.vala', + 'MessageList/FolderPopover/FolderPopover.vala', + 'MessageList/FolderPopover/FolderRow.vala', 'MessageList/MessageList.vala', 'MessageList/MessageListItem.vala', 'SourceList/CellRendererBadge.vala',