Skip to main content

gtk/
list_store.rs

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