Skip to main content

gtk/
tree_store.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use crate::TreeModel;
4use crate::TreeStore;
5use crate::{TreeIter, ffi};
6use glib::object::{Cast, IsA};
7use glib::translate::*;
8use glib::{Type, Value, value::ToValue};
9use libc::c_int;
10
11impl TreeStore {
12    /// Creates a new tree store as with `n_columns` columns each of the types passed
13    /// in. Note that only types derived from standard GObject fundamental types
14    /// are supported.
15    ///
16    /// As an example, `gtk_tree_store_new (3, G_TYPE_INT, G_TYPE_STRING,
17    /// GDK_TYPE_PIXBUF);` will create a new [`TreeStore`][crate::TreeStore] with three columns, of type
18    /// `gint`, `gchararray`, and [`gdk_pixbuf::Pixbuf`][crate::gdk_pixbuf::Pixbuf] respectively.
19    /// ## `n_columns`
20    /// number of columns in the tree store
21    ///
22    /// # Returns
23    ///
24    /// a new [`TreeStore`][crate::TreeStore]
25    #[doc(alias = "gtk_tree_store_newv")]
26    pub fn new(column_types: &[Type]) -> TreeStore {
27        assert_initialized_main_thread!();
28        unsafe {
29            let mut column_types = column_types
30                .iter()
31                .map(|t| t.into_glib())
32                .collect::<Vec<_>>();
33            from_glib_full(ffi::gtk_tree_store_newv(
34                column_types.len() as c_int,
35                column_types.as_mut_ptr(),
36            ))
37        }
38    }
39}
40
41pub trait TreeStoreExtManual: IsA<TreeStore> + 'static {
42    #[doc(alias = "gtk_tree_store_insert_with_valuesv")]
43    fn insert_with_values(
44        &self,
45        parent: Option<&TreeIter>,
46        position: Option<u32>,
47        columns_and_values: &[(u32, &dyn ToValue)],
48    ) -> TreeIter {
49        unsafe {
50            assert!(
51                position.unwrap_or(0) <= i32::MAX as u32,
52                "can't have more than {} rows",
53                i32::MAX
54            );
55            let n_columns = ffi::gtk_tree_model_get_n_columns(
56                self.as_ref().upcast_ref::<TreeModel>().to_glib_none().0,
57            ) as u32;
58            assert!(
59                columns_and_values.len() <= n_columns as usize,
60                "got values for {} columns but only {n_columns} columns exist",
61                columns_and_values.len(),
62            );
63            for (column, value) in columns_and_values {
64                assert!(
65                    *column < n_columns,
66                    "got column {} which is higher than the number of columns {n_columns}",
67                    *column,
68                );
69                let type_ = from_glib(ffi::gtk_tree_model_get_column_type(
70                    self.as_ref().upcast_ref::<TreeModel>().to_glib_none().0,
71                    *column as c_int,
72                ));
73                assert!(
74                    Value::type_transformable(value.value_type(), type_),
75                    "column {} is of type {type_} but found value of type {}",
76                    *column,
77                    value.value_type()
78                );
79            }
80
81            let columns = columns_and_values
82                .iter()
83                .map(|(c, _)| *c)
84                .collect::<Vec<_>>();
85            let values = columns_and_values
86                .iter()
87                .map(|(_, v)| v.to_value())
88                .collect::<Vec<_>>();
89
90            let mut iter = TreeIter::uninitialized();
91            ffi::gtk_tree_store_insert_with_valuesv(
92                self.as_ref().to_glib_none().0,
93                iter.to_glib_none_mut().0,
94                mut_override(parent.to_glib_none().0),
95                position.map_or(-1, |n| n as c_int),
96                mut_override(columns.as_ptr() as *const c_int),
97                mut_override(values.as_ptr() as *const glib::gobject_ffi::GValue),
98                columns.len() as c_int,
99            );
100            iter
101        }
102    }
103
104    /// Reorders the children of `parent` in `self` to follow the order
105    /// indicated by `new_order`. Note that this function only works with
106    /// unsorted stores.
107    /// ## `parent`
108    /// A [`TreeIter`][crate::TreeIter], or [`None`]
109    /// ## `new_order`
110    /// an array of integers mapping the new position of each child
111    ///  to its old position before the re-ordering,
112    ///  i.e. `new_order``[newpos] = oldpos`.
113    #[doc(alias = "gtk_tree_store_reorder")]
114    fn reorder(&self, parent: &TreeIter, new_order: &[u32]) {
115        unsafe {
116            let count = ffi::gtk_tree_model_iter_n_children(
117                self.as_ref().upcast_ref::<TreeModel>().to_glib_none().0,
118                mut_override(parent.to_glib_none().0),
119            );
120            let safe_count = count as usize == new_order.len();
121            debug_assert!(
122                safe_count,
123                "Incorrect `new_order` slice length. Expected `{count}`, found `{}`.",
124                new_order.len()
125            );
126            let safe_values = new_order.iter().max().is_none_or(|&max| {
127                let max = max as i32;
128                max >= 0 && max < count
129            });
130            debug_assert!(
131                safe_values,
132                "Some `new_order` slice values are out of range. Maximum safe value: \
133                 `{}`. The slice contents: `{new_order:?}`",
134                count - 1,
135            );
136            if safe_count && safe_values {
137                ffi::gtk_tree_store_reorder(
138                    self.as_ref().to_glib_none().0,
139                    mut_override(parent.to_glib_none().0),
140                    mut_override(new_order.as_ptr() as *const c_int),
141                );
142            }
143        }
144    }
145
146    /// Sets the value of one or more cells in the row referenced by `iter`.
147    /// The variable argument list should contain integer column numbers,
148    /// each column number followed by the value to be set.
149    /// The list is terminated by a -1. For example, to set column 0 with type
150    /// `G_TYPE_STRING` to “Foo”, you would write
151    /// `gtk_tree_store_set (store, iter, 0, "Foo", -1)`.
152    ///
153    /// The value will be referenced by the store if it is a `G_TYPE_OBJECT`, and it
154    /// will be copied if it is a `G_TYPE_STRING` or `G_TYPE_BOXED`.
155    /// ## `iter`
156    /// A valid [`TreeIter`][crate::TreeIter] for the row being modified
157    #[doc(alias = "gtk_tree_store_set")]
158    #[doc(alias = "gtk_tree_store_set_valuesv")]
159    fn set(&self, iter: &TreeIter, columns_and_values: &[(u32, &dyn ToValue)]) {
160        unsafe {
161            let n_columns = ffi::gtk_tree_model_get_n_columns(
162                self.as_ref().upcast_ref::<TreeModel>().to_glib_none().0,
163            ) as u32;
164            assert!(
165                columns_and_values.len() <= n_columns as usize,
166                "got values for {} columns but only {} columns exist",
167                columns_and_values.len(),
168                n_columns
169            );
170            for (column, value) in columns_and_values {
171                assert!(
172                    *column < n_columns,
173                    "got column {} which is higher than the number of columns {n_columns}",
174                    *column,
175                );
176                let type_ = from_glib(ffi::gtk_tree_model_get_column_type(
177                    self.as_ref().upcast_ref::<TreeModel>().to_glib_none().0,
178                    *column as c_int,
179                ));
180                assert!(
181                    Value::type_transformable(value.value_type(), type_),
182                    "column {} is of type {type_} but found value of type {}",
183                    *column,
184                    value.value_type()
185                );
186            }
187
188            let columns = columns_and_values
189                .iter()
190                .map(|(c, _)| *c)
191                .collect::<Vec<_>>();
192            let values = columns_and_values
193                .iter()
194                .map(|(_, v)| v.to_value())
195                .collect::<Vec<_>>();
196
197            ffi::gtk_tree_store_set_valuesv(
198                self.as_ref().to_glib_none().0,
199                mut_override(iter.to_glib_none().0),
200                mut_override(columns.as_ptr() as *const c_int),
201                mut_override(values.as_ptr() as *const glib::gobject_ffi::GValue),
202                columns.len() as c_int,
203            );
204        }
205    }
206
207    /// Sets the data in the cell specified by `iter` and `column`.
208    /// The type of `value` must be convertible to the type of the
209    /// column.
210    /// ## `iter`
211    /// A valid [`TreeIter`][crate::TreeIter] for the row being modified
212    /// ## `column`
213    /// column number to modify
214    /// ## `value`
215    /// new value for the cell
216    #[doc(alias = "gtk_tree_store_set_value")]
217    fn set_value(&self, iter: &TreeIter, column: u32, value: &Value) {
218        unsafe {
219            let columns = ffi::gtk_tree_model_get_n_columns(
220                self.as_ref().upcast_ref::<TreeModel>().to_glib_none().0,
221            ) as u32;
222            assert!(
223                column < columns,
224                "got column {column} which is higher than the number of columns {columns}",
225            );
226
227            let type_ = from_glib(ffi::gtk_tree_model_get_column_type(
228                self.as_ref().upcast_ref::<TreeModel>().to_glib_none().0,
229                column as c_int,
230            ));
231            assert!(
232                Value::type_transformable(value.type_(), type_),
233                "column {column} is of type {type_} but found value of type {}",
234                value.type_()
235            );
236
237            ffi::gtk_tree_store_set_value(
238                self.as_ref().to_glib_none().0,
239                mut_override(iter.to_glib_none().0),
240                column as c_int,
241                mut_override(value.to_glib_none().0),
242            );
243        }
244    }
245}
246
247impl<O: IsA<TreeStore>> TreeStoreExtManual for O {}