1use std::sync::OnceLock;
6
7use glib::{translate::*, GString, Quark};
8use libc::{c_char, c_int};
9
10use crate::{ffi, prelude::*, subclass::prelude::*, Editable};
11
12pub trait EditableImpl: WidgetImpl + ObjectSubclass<Type: IsA<Editable>> {
13 fn insert_text(&self, text: &str, length: i32, position: &mut i32) {
14 self.parent_insert_text(text, length, position);
15 }
16
17 fn delete_text(&self, start_position: i32, end_position: i32) {
18 self.parent_delete_text(start_position, end_position)
19 }
20
21 fn changed(&self) {
22 self.parent_changed()
23 }
24
25 #[doc(alias = "get_text")]
26 fn text(&self) -> GString {
27 self.parent_text()
28 }
29
30 #[doc(alias = "get_delegate")]
31 fn delegate(&self) -> Option<Editable> {
32 self.parent_delegate()
33 }
34
35 fn do_insert_text(&self, text: &str, length: i32, position: &mut i32) {
36 self.parent_do_insert_text(text, length, position)
37 }
38
39 fn do_delete_text(&self, start_position: i32, end_position: i32) {
40 self.parent_do_delete_text(start_position, end_position)
41 }
42
43 #[doc(alias = "get_selection_bounds")]
44 fn selection_bounds(&self) -> Option<(i32, i32)> {
45 self.parent_selection_bounds()
46 }
47
48 fn set_selection_bounds(&self, start_position: i32, end_position: i32) {
49 self.parent_set_selection_bounds(start_position, end_position)
50 }
51}
52
53pub trait EditableImplExt: EditableImpl {
54 #[doc(alias = "gtk_editable_delegate_get_property")]
55 fn delegate_get_property(
56 &self,
57 prop_id: usize,
58 pspec: &glib::ParamSpec,
59 ) -> Option<glib::Value> {
60 unsafe {
61 let mut value = glib::Value::from_type(pspec.value_type());
62
63 if from_glib(ffi::gtk_editable_delegate_get_property(
64 self.obj()
65 .unsafe_cast_ref::<glib::Object>()
66 .to_glib_none()
67 .0,
68 prop_id as u32,
69 value.to_glib_none_mut().0,
70 pspec.to_glib_none().0,
71 )) {
72 Some(value)
73 } else {
74 None
75 }
76 }
77 }
78
79 #[doc(alias = "gtk_editable_delegate_set_property")]
80 fn delegate_set_property(
81 &self,
82 prop_id: usize,
83 value: &glib::Value,
84 pspec: &glib::ParamSpec,
85 ) -> bool {
86 unsafe {
87 from_glib(ffi::gtk_editable_delegate_set_property(
88 self.obj()
89 .unsafe_cast_ref::<glib::Object>()
90 .to_glib_none()
91 .0,
92 prop_id as u32,
93 value.to_glib_none().0,
94 pspec.to_glib_none().0,
95 ))
96 }
97 }
98
99 fn parent_insert_text(&self, text: &str, length: i32, position: &mut i32) {
100 unsafe {
101 let type_data = Self::type_data();
102 let parent_iface = type_data.as_ref().parent_interface::<Editable>()
103 as *const ffi::GtkEditableInterface;
104
105 if let Some(func) = (*parent_iface).insert_text {
106 func(
107 self.obj().unsafe_cast_ref::<Editable>().to_glib_none().0,
108 text.to_glib_none().0,
109 length,
110 position,
111 );
112 }
113 }
114 }
115
116 fn parent_delete_text(&self, start_position: i32, end_position: i32) {
117 unsafe {
118 let type_data = Self::type_data();
119 let parent_iface = type_data.as_ref().parent_interface::<Editable>()
120 as *const ffi::GtkEditableInterface;
121
122 if let Some(func) = (*parent_iface).delete_text {
123 func(
124 self.obj().unsafe_cast_ref::<Editable>().to_glib_none().0,
125 start_position,
126 end_position,
127 );
128 }
129 }
130 }
131
132 fn parent_text(&self) -> GString {
133 unsafe {
134 let type_data = Self::type_data();
135 let parent_iface = type_data.as_ref().parent_interface::<Editable>()
136 as *const ffi::GtkEditableInterface;
137 let func = (*parent_iface)
138 .get_text
139 .expect("no parent \"get_text\" implementation");
140
141 from_glib_none(func(
142 self.obj().unsafe_cast_ref::<Editable>().to_glib_none().0,
143 ))
144 }
145 }
146
147 fn parent_delegate(&self) -> Option<Editable> {
148 unsafe {
149 let type_data = Self::type_data();
150 let parent_iface = type_data.as_ref().parent_interface::<Editable>()
151 as *const ffi::GtkEditableInterface;
152 let func = (*parent_iface)
153 .get_delegate
154 .expect("no parent \"get_delegate\" implementation");
155 from_glib_none(func(
156 self.obj().unsafe_cast_ref::<Editable>().to_glib_none().0,
157 ))
158 }
159 }
160
161 fn parent_changed(&self) {
162 unsafe {
163 let type_data = Self::type_data();
164 let parent_iface = type_data.as_ref().parent_interface::<Editable>()
165 as *const ffi::GtkEditableInterface;
166
167 if let Some(func) = (*parent_iface).changed {
168 func(self.obj().unsafe_cast_ref::<Editable>().to_glib_none().0);
169 }
170 }
171 }
172
173 fn parent_do_insert_text(&self, text: &str, length: i32, position: &mut i32) {
174 unsafe {
175 let type_data = Self::type_data();
176 let parent_iface = type_data.as_ref().parent_interface::<Editable>()
177 as *const ffi::GtkEditableInterface;
178
179 if let Some(func) = (*parent_iface).do_insert_text {
180 func(
181 self.obj().unsafe_cast_ref::<Editable>().to_glib_none().0,
182 text.to_glib_none().0,
183 length,
184 position,
185 );
186 }
187 }
188 }
189
190 fn parent_do_delete_text(&self, start_position: i32, end_position: i32) {
191 unsafe {
192 let type_data = Self::type_data();
193 let parent_iface = type_data.as_ref().parent_interface::<Editable>()
194 as *const ffi::GtkEditableInterface;
195
196 if let Some(func) = (*parent_iface).do_delete_text {
197 func(
198 self.obj().unsafe_cast_ref::<Editable>().to_glib_none().0,
199 start_position,
200 end_position,
201 );
202 }
203 }
204 }
205
206 fn parent_selection_bounds(&self) -> Option<(i32, i32)> {
207 unsafe {
208 let type_data = Self::type_data();
209 let parent_iface = type_data.as_ref().parent_interface::<Editable>()
210 as *const ffi::GtkEditableInterface;
211
212 if let Some(func) = (*parent_iface).get_selection_bounds {
213 let mut start_position = std::mem::MaybeUninit::uninit();
214 let mut end_position = std::mem::MaybeUninit::uninit();
215 if from_glib(func(
216 self.obj().unsafe_cast_ref::<Editable>().to_glib_none().0,
217 start_position.as_mut_ptr(),
218 end_position.as_mut_ptr(),
219 )) {
220 return Some((start_position.assume_init(), end_position.assume_init()));
221 }
222 }
223 None
224 }
225 }
226
227 fn parent_set_selection_bounds(&self, start_position: i32, end_position: i32) {
228 unsafe {
229 let type_data = Self::type_data();
230 let parent_iface = type_data.as_ref().parent_interface::<Editable>()
231 as *const ffi::GtkEditableInterface;
232
233 if let Some(func) = (*parent_iface).set_selection_bounds {
234 func(
235 self.obj().unsafe_cast_ref::<Editable>().to_glib_none().0,
236 start_position,
237 end_position,
238 );
239 }
240 }
241 }
242}
243
244impl<T: EditableImpl> EditableImplExt for T {}
245
246unsafe impl<T: EditableImpl + ObjectSubclass> IsImplementable<T> for Editable {
247 fn interface_init(iface: &mut glib::Interface<Self>) {
248 let instance_type = iface.instance_type();
249 let iface = iface.as_mut();
250
251 iface.insert_text = Some(editable_insert_text::<T>);
252 iface.delete_text = Some(editable_delete_text::<T>);
253 iface.changed = Some(editable_changed::<T>);
254 iface.get_text = Some(editable_get_text::<T>);
255 iface.get_delegate = Some(editable_get_delegate::<T>);
256 iface.do_insert_text = Some(editable_do_insert_text::<T>);
257 iface.do_delete_text = Some(editable_do_delete_text::<T>);
258 iface.get_selection_bounds = Some(editable_get_selection_bounds::<T>);
259 iface.set_selection_bounds = Some(editable_set_selection_bounds::<T>);
260
261 unsafe {
262 let class_ref = glib::object::Class::<glib::Object>::from_type(instance_type).unwrap();
263 let object_class =
264 class_ref.as_ref() as *const _ as *mut glib::gobject_ffi::GObjectClass;
265
266 let mut first_prop = std::mem::MaybeUninit::uninit();
267 let properties = glib::gobject_ffi::g_object_class_list_properties(
268 object_class,
269 first_prop.as_mut_ptr(),
270 );
271 glib::ffi::g_free(properties as *mut _);
272 let first_prop = first_prop.assume_init();
273 ffi::gtk_editable_install_properties(object_class, first_prop);
274 }
275 }
276}
277
278unsafe extern "C" fn editable_insert_text<T: EditableImpl>(
279 editable: *mut ffi::GtkEditable,
280 text: *const c_char,
281 length: c_int,
282 position: *mut c_int,
283) {
284 let instance = &*(editable as *mut T::Instance);
285 let imp = instance.imp();
286
287 imp.insert_text(&GString::from_glib_borrow(text), length, &mut *position)
288}
289
290unsafe extern "C" fn editable_delete_text<T: EditableImpl>(
291 editable: *mut ffi::GtkEditable,
292 start_position: c_int,
293 end_position: c_int,
294) {
295 let instance = &*(editable as *mut T::Instance);
296 let imp = instance.imp();
297
298 imp.delete_text(start_position, end_position)
299}
300
301unsafe extern "C" fn editable_changed<T: EditableImpl>(editable: *mut ffi::GtkEditable) {
302 let instance = &*(editable as *mut T::Instance);
303 let imp = instance.imp();
304
305 imp.changed()
306}
307
308unsafe extern "C" fn editable_get_text<T: EditableImpl>(
309 editable: *mut ffi::GtkEditable,
310) -> *const c_char {
311 let instance = &*(editable as *mut T::Instance);
312 let imp = instance.imp();
313
314 imp.text().into_glib_ptr()
315}
316
317unsafe extern "C" fn editable_get_delegate<T: EditableImpl>(
318 editable: *mut ffi::GtkEditable,
319) -> *mut ffi::GtkEditable {
320 let instance = &*(editable as *mut T::Instance);
321 let imp = instance.imp();
322
323 let delegate = imp.delegate();
324
325 static QUARK: OnceLock<Quark> = OnceLock::new();
326 let quark = *QUARK.get_or_init(|| Quark::from_str("gtk-rs-subclass-editable-get-delegate"));
327
328 match imp.obj().qdata::<Option<Editable>>(quark) {
329 Some(delegate_data) => {
330 assert_eq!(
331 delegate_data.as_ref(),
332 &delegate,
333 "The Editable delegate must not change"
334 );
335 }
336 None => {
337 imp.obj().set_qdata(quark, delegate.clone());
338 }
339 };
340 delegate.to_glib_none().0
341}
342
343unsafe extern "C" fn editable_do_insert_text<T: EditableImpl>(
344 editable: *mut ffi::GtkEditable,
345 text: *const c_char,
346 length: i32,
347 position: *mut i32,
348) {
349 let instance = &*(editable as *mut T::Instance);
350 let imp = instance.imp();
351
352 imp.do_insert_text(&GString::from_glib_borrow(text), length, &mut *position)
353}
354
355unsafe extern "C" fn editable_do_delete_text<T: EditableImpl>(
356 editable: *mut ffi::GtkEditable,
357 start_position: i32,
358 end_position: i32,
359) {
360 let instance = &*(editable as *mut T::Instance);
361 let imp = instance.imp();
362
363 imp.do_delete_text(start_position, end_position)
364}
365
366unsafe extern "C" fn editable_get_selection_bounds<T: EditableImpl>(
367 editable: *mut ffi::GtkEditable,
368 start_position: *mut i32,
369 end_position: *mut i32,
370) -> glib::ffi::gboolean {
371 let instance = &*(editable as *mut T::Instance);
372 let imp = instance.imp();
373
374 if let Some((start_pos, end_pos)) = imp.selection_bounds() {
375 if !start_position.is_null() {
376 *start_position = start_pos;
377 }
378
379 if !end_position.is_null() {
380 *end_position = end_pos;
381 }
382 true.into_glib()
383 } else {
384 *start_position = 0;
385 *end_position = 0;
386 false.into_glib()
387 }
388}
389
390unsafe extern "C" fn editable_set_selection_bounds<T: EditableImpl>(
391 editable: *mut ffi::GtkEditable,
392 start_position: i32,
393 end_position: i32,
394) {
395 let instance = &*(editable as *mut T::Instance);
396 let imp = instance.imp();
397
398 imp.set_selection_bounds(start_position, end_position)
399}