nix_query_tree_viewer/ui/stack/
tree.rs

1mod columns;
2mod path;
3mod signals;
4mod store;
5
6use core::cmp::Ordering;
7use glib::clone;
8use glib::translate::ToGlibPtr;
9
10use super::super::super::ui;
11use super::super::prelude::*;
12
13fn clear(state: &ui::State) {
14    let tree_store = state.get_tree_store();
15    tree_store.clear();
16}
17
18pub fn disable(state: &ui::State) {
19    let tree_view: gtk::TreeView = state.get_tree_view();
20    tree_view.set_sensitive(false);
21}
22
23pub fn enable(state: &ui::State) {
24    let tree_view: gtk::TreeView = state.get_tree_view();
25    tree_view.set_sensitive(true);
26}
27
28fn render_nix_store_res(state: &ui::State) {
29    if let Some(res) = &*state.read_nix_store_res() {
30        let tree_store = state.get_tree_store();
31        store::insert(&tree_store, res);
32    }
33}
34
35pub fn setup(state: &ui::State) {
36    signals::connect(state);
37}
38
39/// Low-level (unsafe) function for setting the sorting function.
40#[allow(unsafe_code)]
41fn set_sort_func<O: IsA<gtk::TreeSortable>>(
42    tree_model_sort: &O,
43    sort_func: Box<
44        dyn Fn(gtk::TreeModel, gtk::TreeIter, gtk::TreeIter) -> Ordering
45            + 'static,
46    >,
47) {
48    let sort_func_data: Box<
49        Box<
50            dyn Fn(gtk::TreeModel, gtk::TreeIter, gtk::TreeIter) -> Ordering
51                + 'static,
52        >,
53    > = Box::new(sort_func);
54
55    unsafe extern "C" fn sort_func_func(
56        tree_model: *mut gtk_sys::GtkTreeModel,
57        tree_iter_a: *mut gtk_sys::GtkTreeIter,
58        tree_iter_b: *mut gtk_sys::GtkTreeIter,
59        user_data: glib_sys::gpointer,
60    ) -> i32 {
61        let tree_model: gtk::TreeModel =
62            glib::translate::from_glib_borrow(tree_model);
63        let tree_iter_a: gtk::TreeIter =
64            glib::translate::from_glib_borrow(tree_iter_a);
65        let tree_iter_b: gtk::TreeIter =
66            glib::translate::from_glib_borrow(tree_iter_b);
67        let callback: &Box<
68            dyn Fn(gtk::TreeModel, gtk::TreeIter, gtk::TreeIter) -> Ordering
69                + 'static,
70        > = &*(user_data as *mut _);
71
72        let res = callback(tree_model, tree_iter_a, tree_iter_b);
73
74        match res {
75            Ordering::Less => -1,
76            Ordering::Equal => 0,
77            Ordering::Greater => 1,
78        }
79    }
80
81    let tree_sortable: &gtk::TreeSortable = tree_model_sort.as_ref();
82    let gtk_tree_sortable: *mut gtk_sys::GtkTreeSortable =
83        tree_sortable.to_glib_none().0;
84
85    unsafe extern "C" fn destroy_func(data: glib_sys::gpointer) {
86        let _callback: Box<
87            Box<
88                dyn Fn(gtk::TreeModel, gtk::TreeIter, gtk::TreeIter) -> Ordering
89                    + 'static,
90            >,
91        > = Box::from_raw(data as *mut _);
92    }
93
94    unsafe {
95        gtk_sys::gtk_tree_sortable_set_sort_func(
96            gtk_tree_sortable,
97            0,
98            Some(sort_func_func as _),
99            Box::into_raw(sort_func_data) as *mut std::ffi::c_void,
100            Some(
101                destroy_func as unsafe extern "C" fn(_: *mut std::ffi::c_void),
102            ),
103        );
104    }
105}
106
107pub fn change_sort_order(state: &ui::State) {
108    let tree_model_sort = state.get_tree_model_sort();
109
110    match *state.read_sort_order() {
111        ui::SortOrder::NixStoreOrigOutput => {
112            tree_model_sort.set_sort_column_id(
113                gtk::SortColumn::Default,
114                gtk::SortType::Ascending,
115            );
116        }
117        ui::SortOrder::AlphabeticalHash => {
118            set_sort_function(state);
119            tree_model_sort.set_sort_column_id(
120                gtk::SortColumn::Index(0),
121                gtk::SortType::Ascending,
122            );
123        }
124        ui::SortOrder::AlphabeticalDrvName => {
125            set_sort_function(state);
126            tree_model_sort.set_sort_column_id(
127                gtk::SortColumn::Index(0),
128                gtk::SortType::Ascending,
129            );
130        }
131    }
132}
133
134pub fn change_view_style(state: &ui::State) {
135    columns::change_view_style(state);
136}
137
138fn set_sort_func_callback(
139    state: &ui::State,
140    tree_model: gtk::TreeModel,
141    tree_model_sort_iter_a: gtk::TreeIter,
142    tree_model_sort_iter_b: gtk::TreeIter,
143) -> Ordering {
144    let sort_order = *state.read_sort_order();
145    if let Some(nix_store_res) = &*state.read_nix_store_res() {
146        let tree_store: &gtk::TreeStore = tree_model
147            .downcast_ref()
148            .expect("tree_model is not a tree_store");
149
150        let child_iter_a = path::GtkChildTreeIter::new(tree_model_sort_iter_a);
151        let child_iter_b = path::GtkChildTreeIter::new(tree_model_sort_iter_b);
152
153        let option_nix_query_entry_a: Option<
154            &crate::nix_query_tree::NixQueryEntry,
155        > = child_iter_a.nix_store_res_lookup(tree_store, &nix_store_res);
156        let option_nix_query_entry_b: Option<
157            &crate::nix_query_tree::NixQueryEntry,
158        > = child_iter_b.nix_store_res_lookup(tree_store, &nix_store_res);
159
160        match (option_nix_query_entry_a, option_nix_query_entry_b) {
161            (Some(nix_query_entry_a), Some(nix_query_entry_b)) => {
162                match sort_order {
163                    ui::SortOrder::NixStoreOrigOutput => {
164                        println!("The sort function should never be called when the sort order is NixStoreOrigOutput!!!");
165                        Ordering::Equal
166                    }
167                    ui::SortOrder::AlphabeticalHash => {
168                        nix_query_entry_a.cmp_hash(&nix_query_entry_b)
169                    }
170                    ui::SortOrder::AlphabeticalDrvName => {
171                        nix_query_entry_a.cmp_drv_name(&nix_query_entry_b)
172                    }
173                }
174            }
175            _ => panic!("Not able to get an ordering for one of the nix_query_entries.  This should never happen."),
176        }
177    } else {
178        panic!("The nix_store_res in state hasn't been set yet.  This should never happen.");
179    }
180}
181
182pub fn set_sort_function(state: &ui::State) {
183    let tree_model_sort = state.get_tree_model_sort();
184
185    let sort_callback = clone!(@strong state => move|tree_model, tree_model_sort_iter_a, tree_model_sort_iter_b| {
186        set_sort_func_callback(&state, tree_model, tree_model_sort_iter_a, tree_model_sort_iter_b)
187    });
188
189    set_sort_func(&tree_model_sort, Box::new(sort_callback));
190}
191
192pub fn redisplay_data(state: &ui::State) {
193    clear(state);
194    enable(state);
195
196    render_nix_store_res(state);
197
198    // expand the first row of the tree view
199    state
200        .get_tree_view()
201        .expand_row(&gtk::TreePath::new_first(), false);
202}