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// A branch that holds all the folders for a particular account. 8public class FolderList.AccountBranch : Sidebar.Branch { 9 10 // Defines the ordering that special-use folders appear in the 11 // folder list 12 private const Geary.Folder.SpecialUse[] SPECIAL_USE_ORDERING = { 13 INBOX, 14 SEARCH, 15 FLAGGED, 16 IMPORTANT, 17 DRAFTS, 18 CUSTOM, 19 NONE, 20 OUTBOX, 21 SENT, 22 ARCHIVE, 23 ALL_MAIL, 24 TRASH, 25 JUNK 26 }; 27 28 29 public Geary.Account account { get; private set; } 30 public SpecialGrouping user_folder_group { get; private set; } 31 public Gee.HashMap<Geary.FolderPath, FolderEntry> folder_entries { get; private set; } 32 33 private string display_name = ""; 34 35 public AccountBranch(Geary.Account account) { 36 base(new Sidebar.Header(account.information.display_name), 37 STARTUP_OPEN_GROUPING | STARTUP_EXPAND_TO_FIRST_CHILD, 38 normal_folder_comparator, 39 special_folder_comparator 40 ); 41 42 this.account = account; 43 // Translators: The name of the folder group containing 44 // folders created by people (as opposed to special-use 45 // folders) 46 user_folder_group = new SpecialGrouping(2, _("Labels"), "tag-symbolic"); 47 folder_entries = new Gee.HashMap<Geary.FolderPath, FolderEntry>(); 48 49 this.display_name = account.information.display_name; 50 account.information.changed.connect(on_information_changed); 51 52 entry_removed.connect(on_entry_removed); 53 entry_moved.connect(check_user_folders); 54 } 55 56 ~AccountBranch() { 57 account.information.changed.disconnect(on_information_changed); 58 entry_removed.disconnect(on_entry_removed); 59 entry_moved.disconnect(check_user_folders); 60 } 61 62 private void on_information_changed() { 63 if (this.display_name != this.account.information.display_name) { 64 this.display_name = account.information.display_name; 65 ((Sidebar.Grouping) get_root()).rename(this.display_name); 66 } 67 } 68 69 private static int special_grouping_comparator(Sidebar.Entry a, Sidebar.Entry b) { 70 SpecialGrouping? grouping_a = a as SpecialGrouping; 71 SpecialGrouping? grouping_b = b as SpecialGrouping; 72 73 assert(grouping_a != null || grouping_b != null); 74 75 int position_a = (grouping_a != null ? grouping_a.position : 0); 76 int position_b = (grouping_b != null ? grouping_b.position : 0); 77 78 return position_a - position_b; 79 } 80 81 private static int special_folder_comparator(Sidebar.Entry a, Sidebar.Entry b) { 82 if (a is Sidebar.Grouping || b is Sidebar.Grouping) 83 return special_grouping_comparator(a, b); 84 85 FolderEntry entry_a = (FolderEntry) a; 86 FolderEntry entry_b = (FolderEntry) b; 87 Geary.Folder.SpecialUse type_a = entry_a.folder.used_as; 88 Geary.Folder.SpecialUse type_b = entry_b.folder.used_as; 89 90 if (type_a == type_b) return 0; 91 if (type_a == INBOX) return -1; 92 if (type_b == INBOX) return 1; 93 94 int ordering_a = 0; 95 for (; ordering_a < SPECIAL_USE_ORDERING.length; ordering_a++) { 96 if (type_a == SPECIAL_USE_ORDERING[ordering_a]) { 97 break; 98 } 99 } 100 int ordering_b = 0; 101 for (; ordering_b < SPECIAL_USE_ORDERING.length; ordering_b++) { 102 if (type_b == SPECIAL_USE_ORDERING[ordering_b]) { 103 break; 104 } 105 } 106 107 if (ordering_a == ordering_b) return normal_folder_comparator(a, b); 108 return ordering_a - ordering_b; 109 } 110 111 private static int normal_folder_comparator(Sidebar.Entry a, Sidebar.Entry b) { 112 // Non-special folders are compared based on name. 113 return a.get_sidebar_name().collate(b.get_sidebar_name()); 114 } 115 116 public FolderEntry? get_entry_for_path(Geary.FolderPath folder_path) { 117 return folder_entries.get(folder_path); 118 } 119 120 public void add_folder(Application.FolderContext context) { 121 Sidebar.Entry? graft_point = null; 122 FolderEntry folder_entry = new FolderEntry(context); 123 Geary.Folder.SpecialUse used_as = context.folder.used_as; 124 if (used_as != NONE) { 125 if (used_as == SEARCH) 126 return; // Don't show search folder under the account. 127 128 // Special folders go in the root of the account. 129 graft_point = get_root(); 130 } else if (context.folder.path.is_top_level) { 131 // Top-level folders get put in our special user folders group. 132 graft_point = user_folder_group; 133 134 if (!has_entry(user_folder_group)) { 135 graft(get_root(), user_folder_group); 136 } 137 } else { 138 var entry = folder_entries.get(context.folder.path.parent); 139 if (entry != null) 140 graft_point = entry; 141 } 142 143 // Due to how we enumerate folders on the server, it's unfortunately 144 // possible now to have two folders that we'd put in the same place in 145 // our tree. In that case, we just ignore the second folder for now. 146 // See #6616. 147 if (graft_point != null) { 148 Sidebar.Entry? twin = find_first_child(graft_point, (e) => { 149 return e.get_sidebar_name() == folder_entry.get_sidebar_name(); 150 }); 151 if (twin != null) 152 graft_point = null; 153 } 154 155 if (graft_point != null) { 156 graft(graft_point, folder_entry); 157 folder_entries.set(context.folder.path, folder_entry); 158 } else { 159 debug( 160 "Could not add folder %s of type %s to folder list", 161 context.folder.to_string(), 162 used_as.to_string() 163 ); 164 } 165 } 166 167 public void remove_folder(Geary.FolderPath path) { 168 Sidebar.Entry? entry = this.folder_entries.get(path); 169 if (entry == null) { 170 debug("Could not remove folder %s", path.to_string()); 171 return; 172 } 173 174 prune(entry); 175 this.folder_entries.unset(path); 176 } 177 178 private void on_entry_removed(Sidebar.Entry entry) { 179 FolderEntry? folder_entry = entry as FolderEntry; 180 if (folder_entry != null && folder_entries.has_key(folder_entry.folder.path)) 181 folder_entries.unset(folder_entry.folder.path); 182 183 check_user_folders(entry); 184 } 185 186 private void check_user_folders(Sidebar.Entry entry) { 187 if (entry != user_folder_group) { 188 // remove "Labels" entry if there are no more user entries 189 if (has_entry(user_folder_group) && (get_child_count(user_folder_group) == 0)) { 190 prune(user_folder_group); 191 } 192 } 193 } 194} 195