gtk4/text_buffer.rs
1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{boxed::Box as Box_, mem::transmute, slice, str};
4
5use glib::{
6 signal::{SignalHandlerId, connect_raw},
7 translate::*,
8};
9use libc::{c_char, c_int};
10
11use crate::{TextBuffer, TextIter, TextTag, ffi, prelude::*};
12
13#[cfg(feature = "v4_16")]
14use crate::TextBufferNotifyFlags;
15
16// rustdoc-stripper-ignore-next
17/// Trait containing manually implemented methods of
18/// [`TextBuffer`](crate::TextBuffer).
19pub trait TextBufferExtManual: IsA<TextBuffer> + 'static {
20 // rustdoc-stripper-ignore-next
21 /// # Panics
22 ///
23 /// If the properties don't exists or are not writable.
24 // rustdoc-stripper-ignore-next-stop
25 /// s tag table, so the ref count will be equal to one.
26 ///
27 /// If @tag_name is [`None`], the tag is anonymous.
28 ///
29 /// If @tag_name is non-[`None`], a tag called @tag_name must not already
30 /// exist in the tag table for this buffer.
31 ///
32 /// The @first_property_name argument and subsequent arguments are a list
33 /// of properties to set on the tag, as with g_object_set().
34 /// ## `tag_name`
35 /// name of the new tag
36 /// ## `first_property_name`
37 /// name of first property to set
38 ///
39 /// # Returns
40 ///
41 /// a new tag
42 #[doc(alias = "gtk_text_buffer_create_tag")]
43 fn create_tag(
44 &self,
45 tag_name: Option<&str>,
46 properties: &[(&str, &dyn ToValue)],
47 ) -> Option<TextTag> {
48 let tag = TextTag::new(tag_name);
49 tag.set_properties(properties);
50 if self.as_ref().tag_table().add(&tag) {
51 Some(tag)
52 } else {
53 None
54 }
55 }
56
57 /// Inserts @text into @self at @iter, applying the list of tags to
58 /// the newly-inserted text.
59 ///
60 /// The last tag specified must be [`None`] to terminate the list.
61 /// Equivalent to calling [`TextBufferExt::insert()`][crate::prelude::TextBufferExt::insert()],
62 /// then [`TextBufferExt::apply_tag()`][crate::prelude::TextBufferExt::apply_tag()] on the inserted text;
63 /// this is just a convenience function.
64 /// ## `iter`
65 /// an iterator in @self
66 /// ## `text`
67 /// UTF-8 text
68 /// ## `len`
69 /// length of @text, or -1
70 /// ## `first_tag`
71 /// first tag to apply to @text
72 #[doc(alias = "gtk_text_buffer_insert_with_tags")]
73 fn insert_with_tags(&self, iter: &mut TextIter, text: &str, tags: &[&TextTag]) {
74 let start_offset = iter.offset();
75 self.as_ref().insert(iter, text);
76 let start_iter = self.as_ref().iter_at_offset(start_offset);
77 tags.iter().for_each(|tag| {
78 self.as_ref().apply_tag(&(*tag).clone(), &start_iter, iter);
79 });
80 }
81
82 /// Inserts @text into @self at @iter, applying the list of tags to
83 /// the newly-inserted text.
84 ///
85 /// Same as [`insert_with_tags()`][Self::insert_with_tags()], but allows you
86 /// to pass in tag names instead of tag objects.
87 /// ## `iter`
88 /// position in @self
89 /// ## `text`
90 /// UTF-8 text
91 /// ## `len`
92 /// length of @text, or -1
93 /// ## `first_tag_name`
94 /// name of a tag to apply to @text
95 #[doc(alias = "gtk_text_buffer_insert_with_tags_by_name")]
96 fn insert_with_tags_by_name(&self, iter: &mut TextIter, text: &str, tags_names: &[&str]) {
97 let start_offset = iter.offset();
98 self.as_ref().insert(iter, text);
99 let start_iter = self.as_ref().iter_at_offset(start_offset);
100 let tag_table = self.as_ref().tag_table();
101 tags_names
102 .iter()
103 .for_each(|tag_name| match tag_table.lookup(tag_name) {
104 Some(tag) => {
105 self.as_ref().apply_tag(&tag, &start_iter, iter);
106 }
107 _ => {
108 glib::g_warning!("TextBuffer", "No tag with name {}!", tag_name);
109 }
110 });
111 }
112
113 /// Emitted to insert text in a [`TextBuffer`][crate::TextBuffer].
114 ///
115 /// Insertion actually occurs in the default handler.
116 ///
117 /// Note that if your handler runs before the default handler
118 /// it must not invalidate the @location iter (or has to
119 /// revalidate it). The default signal handler revalidates
120 /// it to point to the end of the inserted text.
121 ///
122 /// See also: [`TextBufferExt::insert()`][crate::prelude::TextBufferExt::insert()],
123 /// [`TextBufferExt::insert_range()`][crate::prelude::TextBufferExt::insert_range()].
124 /// ## `location`
125 /// position to insert @text in @textbuffer
126 /// ## `text`
127 /// the UTF-8 text to be inserted
128 /// ## `len`
129 /// length of the inserted text in bytes
130 fn connect_insert_text<F: Fn(&Self, &mut TextIter, &str) + 'static>(
131 &self,
132 f: F,
133 ) -> SignalHandlerId {
134 unsafe {
135 unsafe extern "C" fn insert_text_trampoline<
136 T,
137 F: Fn(&T, &mut TextIter, &str) + 'static,
138 >(
139 this: *mut ffi::GtkTextBuffer,
140 location: *mut ffi::GtkTextIter,
141 text: *mut c_char,
142 len: c_int,
143 f: glib::ffi::gpointer,
144 ) where
145 T: IsA<TextBuffer>,
146 {
147 unsafe {
148 let mut location_copy = from_glib_none(location);
149 let f: &F = &*(f as *const F);
150 let text = if len <= 0 {
151 &[]
152 } else {
153 slice::from_raw_parts(text as *const u8, len as usize)
154 };
155
156 f(
157 TextBuffer::from_glib_borrow(this).unsafe_cast_ref(),
158 &mut location_copy,
159 str::from_utf8(text).unwrap(),
160 )
161 }
162 }
163 let f: Box_<F> = Box_::new(f);
164 connect_raw(
165 self.to_glib_none().0 as *mut _,
166 c"insert-text".as_ptr() as *mut _,
167 Some(transmute::<*const (), unsafe extern "C" fn()>(
168 insert_text_trampoline::<Self, F> as *const (),
169 )),
170 Box_::into_raw(f),
171 )
172 }
173 }
174
175 /// Adds a `callback::Gtk::TextBufferCommitNotify to be called when a change
176 /// is to be made to the [type@Gtk.TextBuffer].
177 ///
178 /// Functions are explicitly forbidden from making changes to the
179 /// [type@Gtk.TextBuffer] from this callback. It is intended for tracking
180 /// changes to the buffer only.
181 ///
182 /// It may be advantageous to use `callback::Gtk::TextBufferCommitNotify over
183 /// connecting to the [`insert-text`][struct@crate::TextBuffer#insert-text] or
184 /// [`delete-range`][struct@crate::TextBuffer#delete-range] signals to avoid ordering issues with
185 /// other signal handlers which may further modify the [type@Gtk.TextBuffer].
186 /// ## `flags`
187 /// which notifications should be dispatched to @callback
188 /// ## `commit_notify`
189 /// a
190 /// `callback::Gtk::TextBufferCommitNotify to call for commit notifications
191 ///
192 /// # Returns
193 ///
194 /// a handler id which may be used to remove the commit notify
195 /// callback using [`TextBufferExt::remove_commit_notify()`][crate::prelude::TextBufferExt::remove_commit_notify()].
196 #[cfg(feature = "v4_16")]
197 #[cfg_attr(docsrs, doc(cfg(feature = "v4_16")))]
198 #[doc(alias = "gtk_text_buffer_add_commit_notify")]
199 fn add_commit_notify<P: Fn(&TextBuffer, TextBufferNotifyFlags, u32, u32) + 'static>(
200 &self,
201 flags: TextBufferNotifyFlags,
202 commit_notify: P,
203 ) -> u32 {
204 let commit_notify_data: Box_<P> = Box_::new(commit_notify);
205 unsafe extern "C" fn commit_notify_func<
206 P: Fn(&TextBuffer, TextBufferNotifyFlags, u32, u32) + 'static,
207 >(
208 buffer: *mut ffi::GtkTextBuffer,
209 flags: ffi::GtkTextBufferNotifyFlags,
210 position: std::ffi::c_uint,
211 length: std::ffi::c_uint,
212 user_data: glib::ffi::gpointer,
213 ) {
214 unsafe {
215 let buffer = from_glib_borrow(buffer);
216 let flags = from_glib(flags);
217 let callback = &*(user_data as *mut P);
218 (*callback)(&buffer, flags, position, length)
219 }
220 }
221 let commit_notify = Some(commit_notify_func::<P> as _);
222 unsafe extern "C" fn destroy_func<
223 P: Fn(&TextBuffer, TextBufferNotifyFlags, u32, u32) + 'static,
224 >(
225 data: glib::ffi::gpointer,
226 ) {
227 unsafe {
228 let _callback = Box_::from_raw(data as *mut P);
229 }
230 }
231 let destroy_call4 = Some(destroy_func::<P> as _);
232 let super_callback0: Box_<P> = commit_notify_data;
233 unsafe {
234 ffi::gtk_text_buffer_add_commit_notify(
235 self.as_ref().to_glib_none().0,
236 flags.into_glib(),
237 commit_notify,
238 Box_::into_raw(super_callback0) as *mut _,
239 destroy_call4,
240 )
241 }
242 }
243}
244
245impl<O: IsA<TextBuffer>> TextBufferExtManual for O {}
246
247impl std::fmt::Write for TextBuffer {
248 fn write_str(&mut self, s: &str) -> std::fmt::Result {
249 let mut iter = self.end_iter();
250 self.insert(&mut iter, s);
251 Ok(())
252 }
253}