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