1/* Copyright 2016 Software Freedom Conservancy Inc. 2 * 3 * This software is licensed under the GNU Lesser General Public License 4 * (version 2.1 or later). See the COPYING file in this distribution. 5 */ 6 7[GtkTemplate (ui = "/org/gnome/Geary/folder-popover.ui")] 8public class FolderPopover : Gtk.Popover { 9 10 [GtkChild] private unowned Gtk.SearchEntry search_entry; 11 [GtkChild] private unowned Gtk.ListBox list_box; 12 13 private int filtered_folder_count = 0; 14 15 public signal void folder_selected(Geary.Folder folder); 16 17 public FolderPopover() { 18 list_box.set_filter_func(row_filter); 19 list_box.set_sort_func(row_sort); 20 this.show.connect(() => search_entry.grab_focus()); 21 this.hide.connect(() => { 22 search_entry.set_text(""); 23 invalidate_filter(); 24 }); 25 } 26 27 public bool has_folder(Geary.Folder folder) { 28 return get_row_with_folder(folder) != null; 29 } 30 31 public void add_folder(Geary.Folder folder) { 32 // don't allow multiples and don't allow folders that can't be opened (that means they 33 // support almost no operations and have no content) 34 if (has_folder(folder) || folder.properties.is_openable.is_impossible()) 35 return; 36 37 // also don't allow local-only or virtual folders, which also have a limited set of 38 // operations 39 if (folder.properties.is_local_only || folder.properties.is_virtual) 40 return; 41 42 list_box.add(build_row(folder)); 43 list_box.invalidate_sort(); 44 } 45 46 public void enable_disable_folder(Geary.Folder folder, bool sensitive) { 47 Gtk.ListBoxRow row = get_row_with_folder(folder); 48 if (row != null) 49 row.sensitive = sensitive; 50 } 51 52 public void remove_folder(Geary.Folder folder) { 53 Gtk.ListBoxRow row = get_row_with_folder(folder); 54 if (row != null) 55 list_box.remove(row); 56 } 57 58 public Gtk.ListBoxRow? get_row_with_folder(Geary.Folder folder) { 59 Gtk.ListBoxRow result = null; 60 list_box.foreach((row) => { 61 if (row.get_data<Geary.Folder>("folder") == folder) 62 result = row as Gtk.ListBoxRow; 63 }); 64 return result; 65 } 66 67 public void clear() { 68 list_box.foreach((row) => list_box.remove(row)); 69 } 70 71 private Gtk.ListBoxRow build_row(Geary.Folder folder) { 72 Gtk.ListBoxRow row = new Gtk.ListBoxRow(); 73 row.get_style_context().add_class("geary-folder-popover-list-row"); 74 row.set_data("folder", folder); 75 76 Gtk.Label label = new Gtk.Label(folder.path.to_string()); 77 label.set_halign(Gtk.Align.START); 78 row.add(label); 79 80 row.show_all(); 81 82 return row; 83 } 84 85 [GtkCallback] 86 private void on_row_activated(Gtk.ListBoxRow? row) { 87 if (row != null) { 88 Geary.Folder folder = row.get_data<Geary.Folder>("folder"); 89 folder_selected(folder); 90 } 91 92 this.hide(); 93 } 94 95 [GtkCallback] 96 private void on_search_entry_activate() { 97 if (filtered_folder_count == 1) { 98 // Don't use get_row_at_index(0), or you will get the first row of the unfiltered list. 99 Gtk.ListBoxRow? row = list_box.get_row_at_y(0); 100 if (row != null) 101 on_row_activated(row); 102 } else if (filtered_folder_count > 0) { 103 list_box.get_row_at_y(0).grab_focus(); 104 } 105 } 106 107 [GtkCallback] 108 private void on_search_entry_search_changed() { 109 invalidate_filter(); 110 if (this.search_entry.get_text() != "") { 111 this.list_box.unselect_all(); 112 } 113 } 114 115 private void invalidate_filter() { 116 filtered_folder_count = 0; 117 list_box.invalidate_filter(); 118 } 119 120 private bool row_filter(Gtk.ListBoxRow row) { 121 Gtk.Label label = row.get_child() as Gtk.Label; 122 if (label.label.down().contains(search_entry.text.down())) { 123 filtered_folder_count++; 124 return true; 125 } 126 return false; 127 } 128 129 private int row_sort(Gtk.ListBoxRow row1, Gtk.ListBoxRow row2) { 130 Geary.Folder folder1 = row1.get_data<Geary.Folder>("folder"); 131 Geary.Folder folder2 = row2.get_data<Geary.Folder>("folder"); 132 return folder1.path.compare_to(folder2.path); 133 } 134} 135