fltk/
text.rs

1#![allow(deprecated)]
2
3use crate::enums::{Color, Font, Key};
4use crate::prelude::*;
5use crate::utils::FlString;
6use fltk_sys::text::*;
7use std::{
8    ffi::{CStr, CString},
9    os::raw,
10};
11
12type ModifyCbPtr =
13    *mut Box<dyn for<'r> FnMut(i32, i32, i32, i32, Option<&'r str>)>;
14
15#[allow(clippy::type_complexity)]
16unsafe extern "C" fn text_modify_shim(
17    pos: raw::c_int,
18    inserted: raw::c_int,
19    deleted: raw::c_int,
20    restyled: raw::c_int,
21    deleted_text: *const raw::c_char,
22    data: *mut raw::c_void,
23) {
24    unsafe {
25        let temp = if deleted_text.is_null() {
26            None
27        } else {
28            CStr::from_ptr(deleted_text).to_str().ok()
29        };
30        let a = data as *mut Box<dyn for<'r> FnMut(i32, i32, i32, i32, Option<&'r str>)>;
31        let f: &mut dyn FnMut(i32, i32, i32, i32, Option<&str>) = &mut **a;
32        let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
33            f(pos, inserted, deleted, restyled, temp);
34        }));
35    }
36}
37
38unsafe extern "C" fn text_predelete_shim(
39    pos: raw::c_int,
40    deleted: raw::c_int,
41    data: *mut raw::c_void,
42) {
43    let a: *mut Box<dyn FnMut(i32, i32)> = data as *mut Box<dyn FnMut(i32, i32)>;
44    let f: &mut dyn FnMut(i32, i32) = unsafe { &mut **a };
45    let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(pos, deleted)));
46}
47
48type BufWrapper = std::rc::Rc<*mut Fl_Text_Buffer>;
49
50/// the character position is the left edge of a character, whereas
51/// the cursor is thought to be between the centers of two consecutive
52/// characters.
53#[repr(i32)]
54#[derive(Debug, Copy, Clone, PartialEq, Eq)]
55pub enum PositionType {
56    /// The position is before the character
57    Cursor,
58    /// The position is after the character
59    Character,
60}
61
62/// Defines the text cursor styles supported by fltk
63#[repr(i32)]
64#[derive(Debug, Copy, Clone, PartialEq, Eq)]
65pub enum Cursor {
66    /// Normal
67    Normal,
68    /// Caret
69    Caret,
70    /// Dim
71    Dim,
72    /// Block
73    Block,
74    /// Heavy
75    Heavy,
76    /// Simple
77    Simple,
78}
79
80/// Wraps a text buffer, Cloning a text buffer invalidates the underlying pointer, thus the no derive(Clone)
81#[derive(Debug)]
82pub struct TextBuffer {
83    inner: BufWrapper,
84}
85
86impl std::default::Default for TextBuffer {
87    /// Initialized a default text buffer
88    fn default() -> TextBuffer {
89        unsafe {
90            let text_buffer = Fl_Text_Buffer_new();
91            assert!(!text_buffer.is_null());
92            TextBuffer {
93                inner: BufWrapper::new(text_buffer),
94            }
95        }
96    }
97}
98
99impl TextBuffer {
100    /// Deletes the `TextBuffer`
101    /// # Safety
102    /// The buffer shouldn't be deleted while the Display widget still needs it
103    pub unsafe fn delete(buf: Self) {
104        assert!(!buf.inner.is_null());
105        unsafe {
106            Fl_Text_Buffer_delete(*buf.inner);
107        }
108    }
109
110    /// Deletes the `TextBuffer`
111    /// # Safety
112    /// The buffer shouldn't be deleted while the Display widget still needs it
113    pub unsafe fn delete_buffer(buf: Self) {
114        unsafe { Self::delete(buf) }
115    }
116
117    /// Initializes a text buffer from a pointer
118    /// # Safety
119    /// The pointer must be valid
120    pub unsafe fn from_ptr(ptr: *mut Fl_Text_Buffer) -> Self {
121        assert!(!ptr.is_null());
122        unsafe {
123            let inner = BufWrapper::from(ptr);
124            let ptr = BufWrapper::into_raw(inner);
125            BufWrapper::increment_strong_count(ptr);
126            let inner = BufWrapper::from_raw(ptr);
127            TextBuffer { inner }
128        }
129    }
130
131    /// Returns the inner pointer from a text buffer
132    /// # Safety
133    /// Can return multiple mutable pointers to the same buffer
134    pub unsafe fn as_ptr(&self) -> *mut Fl_Text_Buffer {
135        unsafe {
136            let ptr = BufWrapper::into_raw(BufWrapper::clone(&self.inner));
137            BufWrapper::increment_strong_count(ptr);
138            let inner = BufWrapper::from_raw(ptr);
139            *inner
140        }
141    }
142
143    /// Sets the text of the buffer
144    pub fn set_text(&mut self, txt: &str) {
145        assert!(!self.inner.is_null());
146        unsafe {
147            let txt = CString::safe_new(txt);
148            Fl_Text_Buffer_set_text(*self.inner, txt.as_ptr())
149        }
150    }
151
152    /// Returns the text of the buffer
153    pub fn text(&self) -> String {
154        assert!(!self.inner.is_null());
155        unsafe {
156            let text = Fl_Text_Buffer_text(*self.inner);
157            assert!(!text.is_null());
158            CStr::from_ptr(text as *mut raw::c_char)
159                .to_string_lossy()
160                .to_string()
161        }
162    }
163
164    /**
165        Appends to the buffer.
166        To append and scroll to the end of the buffer:
167        ```rust,no_run
168        use fltk::{prelude::*, *};
169        let txt = "Some long text!";
170        let buf = text::TextBuffer::default();
171        let mut disp = text::TextDisplay::default();
172        disp.set_buffer(Some(buf));
173        disp.buffer().unwrap().append(txt);
174        disp.set_insert_position(disp.buffer().unwrap().length());
175        disp.scroll(
176            disp.count_lines(0, disp.buffer().unwrap().length(), true),
177            0,
178        );
179        ```
180    */
181    pub fn append(&mut self, text: &str) {
182        assert!(!self.inner.is_null());
183        let text = CString::safe_new(text);
184        unsafe { Fl_Text_Buffer_append(*self.inner, text.as_ptr()) }
185    }
186
187    /// Append bytes to the buffer
188    pub fn append2(&mut self, text: &[u8]) {
189        assert!(!self.inner.is_null());
190        unsafe { Fl_Text_Buffer_append2(*self.inner, text.as_ptr() as _, text.len() as _) }
191    }
192
193    /// Get the length of the buffer
194    pub fn length(&self) -> i32 {
195        assert!(!self.inner.is_null());
196        unsafe { Fl_Text_Buffer_length(*self.inner) }
197    }
198
199    /// Removes from the buffer
200    pub fn remove(&mut self, start: i32, end: i32) {
201        assert!(!self.inner.is_null());
202        unsafe {
203            Fl_Text_Buffer_remove(*self.inner, start, end);
204        }
205    }
206
207    /// Returns the text within the range
208    pub fn text_range(&self, start: i32, end: i32) -> Option<String> {
209        assert!(!self.inner.is_null());
210        unsafe {
211            let x = Fl_Text_Buffer_text_range(*self.inner, start, end);
212            if x.is_null() {
213                None
214            } else {
215                Some(
216                    CStr::from_ptr(x as *mut raw::c_char)
217                        .to_string_lossy()
218                        .to_string(),
219                )
220            }
221        }
222    }
223
224    /// Inserts text into a position
225    pub fn insert(&mut self, pos: i32, text: &str) {
226        assert!(!self.inner.is_null());
227        let text = CString::safe_new(text);
228        unsafe { Fl_Text_Buffer_insert(*self.inner, pos, text.as_ptr()) }
229    }
230
231    /// Replaces text from position `start` to `end`
232    pub fn replace(&mut self, start: i32, end: i32, text: &str) {
233        assert!(!self.inner.is_null());
234        assert!(end >= start);
235        let text = CString::safe_new(text);
236        unsafe { Fl_Text_Buffer_replace(*self.inner, start, end, text.as_ptr()) }
237    }
238
239    /// Copies text from a source buffer into the current buffer
240    pub fn copy_from(&mut self, source_buf: &TextBuffer, start: i32, end: i32, to: i32) {
241        assert!(!self.inner.is_null());
242        unsafe { Fl_Text_Buffer_copy(*self.inner, *source_buf.inner, start, end, to) }
243    }
244
245    /// Copies whole text from a source buffer into a new buffer
246    pub fn copy(&self) -> TextBuffer {
247        assert!(!self.inner.is_null());
248        let mut temp = TextBuffer::default();
249        temp.copy_from(self, 0, 0, self.length());
250        temp
251    }
252
253    /// Performs an undo operation on the buffer
254    /// # Errors
255    /// Errors on failure to undo
256    pub fn undo(&mut self) -> Result<(), FltkError> {
257        assert!(!self.inner.is_null());
258        unsafe {
259            match Fl_Text_Buffer_undo(*self.inner, std::ptr::null_mut()) {
260                0 => Err(FltkError::Unknown(String::from("Failed to undo"))),
261                _ => Ok(()),
262            }
263        }
264    }
265
266    /// Performs a redo operation on the buffer.
267    /// Returns the cursor position.
268    /// # Errors
269    /// Errors on failure to undo
270    pub fn redo(&mut self) -> Result<i32, FltkError> {
271        assert!(!self.inner.is_null());
272        unsafe {
273            let mut i = 0;
274            match Fl_Text_Buffer_redo(*self.inner, &mut i) {
275                0 => Err(FltkError::Unknown(String::from("Failed to redo"))),
276                _ => Ok(i),
277            }
278        }
279    }
280
281    /// Sets whether the buffer can undo
282    pub fn can_undo(&mut self, flag: bool) {
283        assert!(!self.inner.is_null());
284        unsafe { Fl_Text_Buffer_canUndo(*self.inner, flag as raw::c_char) }
285    }
286
287    /// Gets whether the buffer can undo
288    pub fn get_can_undo(&mut self) -> bool {
289        assert!(!self.inner.is_null());
290        unsafe { Fl_Text_Buffer_can_undo(*self.inner) != 0 }
291    }
292
293    /// Gets whether the buffer can redo
294    pub fn can_redo(&mut self) -> bool {
295        assert!(!self.inner.is_null());
296        unsafe { Fl_Text_Buffer_can_redo(*self.inner) != 0 }
297    }
298
299    /// Loads a file into the buffer
300    /// # Errors
301    /// Errors on failure to load file
302    pub fn load_file<P: AsRef<std::path::Path>>(&mut self, path: P) -> Result<(), FltkError> {
303        assert!(!self.inner.is_null());
304        if !path.as_ref().exists() {
305            return Err(FltkError::Internal(FltkErrorKind::ResourceNotFound));
306        }
307        let path = path
308            .as_ref()
309            .to_str()
310            .ok_or_else(|| FltkError::Unknown(String::from("Failed to convert path to string")))?;
311        let path = CString::new(path)?;
312        unsafe {
313            match Fl_Text_Buffer_load_file(*self.inner, path.as_ptr()) {
314                0 => Ok(()),
315                _ => Err(FltkError::Internal(FltkErrorKind::ResourceNotFound)),
316            }
317        }
318    }
319
320    /// Saves a buffer into a file
321    /// # Errors
322    /// Errors on failure to save file
323    pub fn save_file<P: AsRef<std::path::Path>>(&mut self, path: P) -> Result<(), FltkError> {
324        assert!(!self.inner.is_null());
325        let path = path
326            .as_ref()
327            .to_str()
328            .ok_or_else(|| FltkError::Unknown(String::from("Failed to convert path to string")))?;
329        let path = CString::new(path)?;
330        unsafe {
331            match Fl_Text_Buffer_save_file(*self.inner, path.as_ptr()) {
332                0 => Ok(()),
333                _ => Err(FltkError::Internal(FltkErrorKind::ResourceNotFound)),
334            }
335        }
336    }
337
338    /// Inserts a file at a position in the buffer
339    /// # Errors
340    /// Errors on failure to open or read file
341    pub fn insert_file<P: AsRef<std::path::Path>>(
342        &mut self,
343        path: P,
344        pos: i32,
345        buflen: i32,
346    ) -> Result<(), FltkError> {
347        assert!(!self.inner.is_null());
348        let path_ref = path.as_ref();
349        if !path_ref.exists() {
350            return Err(FltkError::Internal(FltkErrorKind::ResourceNotFound));
351        }
352        let path = path_ref
353            .to_str()
354            .ok_or_else(|| FltkError::Unknown(String::from("Failed to convert path to string")))?;
355        let path = CString::new(path)?;
356        unsafe {
357            match Fl_Text_Buffer_insertfile(*self.inner, path.as_ptr(), pos, buflen) {
358                0 => Ok(()),
359                _ => Err(FltkError::Internal(FltkErrorKind::FailedOperation)),
360            }
361        }
362    }
363
364    /// Appends a file to the end of the buffer
365    /// # Errors
366    /// Errors on failure to open or read file
367    pub fn append_file<P: AsRef<std::path::Path>>(
368        &mut self,
369        path: P,
370        buflen: i32,
371    ) -> Result<(), FltkError> {
372        assert!(!self.inner.is_null());
373        let path_ref = path.as_ref();
374        if !path_ref.exists() {
375            return Err(FltkError::Internal(FltkErrorKind::ResourceNotFound));
376        }
377        let path = path_ref
378            .to_str()
379            .ok_or_else(|| FltkError::Unknown(String::from("Failed to convert path to string")))?;
380        let path = CString::new(path)?;
381        unsafe {
382            match Fl_Text_Buffer_appendfile(*self.inner, path.as_ptr(), buflen) {
383                0 => Ok(()),
384                _ => Err(FltkError::Internal(FltkErrorKind::FailedOperation)),
385            }
386        }
387    }
388
389    /// Outputs a slice of the buffer into a file
390    /// # Errors
391    /// Errors on failure to open or write file
392    pub fn output_file<P: AsRef<std::path::Path>>(
393        &mut self,
394        path: P,
395        start: i32,
396        end: i32,
397        buflen: i32,
398    ) -> Result<(), FltkError> {
399        assert!(!self.inner.is_null());
400        let path_ref = path.as_ref();
401        let path = path_ref
402            .to_str()
403            .ok_or_else(|| FltkError::Unknown(String::from("Failed to convert path to string")))?;
404        let path = CString::new(path)?;
405        unsafe {
406            match Fl_Text_Buffer_outputfile(*self.inner, path.as_ptr(), start, end, buflen) {
407                0 => Ok(()),
408                _ => Err(FltkError::Internal(FltkErrorKind::FailedOperation)),
409            }
410        }
411    }
412
413    /// Returns the tab distance for the buffer
414    pub fn tab_distance(&self) -> i32 {
415        assert!(!self.inner.is_null());
416        unsafe { Fl_Text_Buffer_tab_distance(*self.inner) }
417    }
418
419    /// Sets the tab distance
420    pub fn set_tab_distance(&mut self, tab_dist: i32) {
421        assert!(!self.inner.is_null());
422        unsafe { Fl_Text_Buffer_set_tab_distance(*self.inner, tab_dist) }
423    }
424
425    /// Selects the text from start to end
426    pub fn select(&mut self, start: i32, end: i32) {
427        assert!(!self.inner.is_null());
428        unsafe { Fl_Text_Buffer_select(*self.inner, start, end) }
429    }
430
431    /// Returns whether text is selected
432    pub fn selected(&self) -> bool {
433        assert!(!self.inner.is_null());
434        unsafe { Fl_Text_Buffer_selected(*self.inner) != 0 }
435    }
436
437    /// Unselects text
438    pub fn unselect(&mut self) {
439        assert!(!self.inner.is_null());
440        unsafe { Fl_Text_Buffer_unselect(*self.inner) }
441    }
442
443    /// Returns the selection position
444    pub fn selection_position(&self) -> Option<(i32, i32)> {
445        assert!(!self.inner.is_null());
446        unsafe {
447            let mut start = 0;
448            let mut end = 0;
449            let ret =
450                Fl_Text_Buffer_selection_position(*self.inner, &mut start as _, &mut end as _);
451            if ret == 0 {
452                None
453            } else {
454                let x = (start, end);
455                Some(x)
456            }
457        }
458    }
459
460    /// Returns the selection text
461    pub fn selection_text(&self) -> String {
462        assert!(!self.inner.is_null());
463        unsafe {
464            let x = Fl_Text_Buffer_selection_text(*self.inner);
465            assert!(!x.is_null());
466            CStr::from_ptr(x as *mut raw::c_char)
467                .to_string_lossy()
468                .to_string()
469        }
470    }
471
472    /// Removes the selection
473    pub fn remove_selection(&mut self) {
474        assert!(!self.inner.is_null());
475        unsafe { Fl_Text_Buffer_remove_selection(*self.inner) }
476    }
477
478    /// Replaces selection
479    pub fn replace_selection(&mut self, text: &str) {
480        assert!(!self.inner.is_null());
481        let text = CString::safe_new(text);
482        unsafe { Fl_Text_Buffer_replace_selection(*self.inner, text.as_ptr()) }
483    }
484
485    /// Secondary selects the text from start to end
486    pub fn secondary_select(&mut self, start: i32, end: i32) {
487        assert!(!self.inner.is_null());
488        unsafe { Fl_Text_Buffer_secondary_select(*self.inner, start, end) }
489    }
490
491    /// Returns whether text is secondary selected
492    pub fn secondary_selected(&self) -> bool {
493        assert!(!self.inner.is_null());
494        unsafe { Fl_Text_Buffer_secondary_selected(*self.inner) != 0 }
495    }
496
497    /// Unselects text (secondary selection)
498    pub fn secondary_unselect(&mut self) {
499        assert!(!self.inner.is_null());
500        unsafe { Fl_Text_Buffer_secondary_unselect(*self.inner) }
501    }
502
503    /// Returns the secondary selection position
504    pub fn secondary_selection_position(&self) -> Option<(i32, i32)> {
505        assert!(!self.inner.is_null());
506        unsafe {
507            let mut start = 0;
508            let mut end = 0;
509            let ret = Fl_Text_Buffer_secondary_selection_position(
510                *self.inner,
511                &mut start as _,
512                &mut end as _,
513            );
514            if ret == 0 {
515                None
516            } else {
517                let x = (start, end);
518                Some(x)
519            }
520        }
521    }
522
523    /// Returns the secondary selection text
524    pub fn secondary_selection_text(&self) -> String {
525        assert!(!self.inner.is_null());
526        unsafe {
527            let x = Fl_Text_Buffer_secondary_selection_text(*self.inner);
528            assert!(!x.is_null());
529            CStr::from_ptr(x as *mut raw::c_char)
530                .to_string_lossy()
531                .to_string()
532        }
533    }
534
535    /// Removes the secondary selection
536    pub fn remove_secondary_selection(&mut self) {
537        assert!(!self.inner.is_null());
538        unsafe { Fl_Text_Buffer_remove_secondary_selection(*self.inner) }
539    }
540
541    /// Replaces the secondary selection
542    pub fn replace_secondary_selection(&mut self, text: &str) {
543        assert!(!self.inner.is_null());
544        let text = CString::safe_new(text);
545        unsafe { Fl_Text_Buffer_replace_secondary_selection(*self.inner, text.as_ptr()) }
546    }
547
548    /// Highlights selection
549    pub fn highlight(&mut self, start: i32, end: i32) {
550        assert!(!self.inner.is_null());
551        unsafe { Fl_Text_Buffer_highlight(*self.inner, start, end) }
552    }
553
554    /// Returns whether text is highlighted
555    pub fn is_highlighted(&self) -> bool {
556        assert!(!self.inner.is_null());
557        unsafe { Fl_Text_Buffer_is_highlighted(*self.inner) != 0 }
558    }
559
560    /// Unhighlights text
561    pub fn unhighlight(&mut self) {
562        assert!(!self.inner.is_null());
563        unsafe { Fl_Text_Buffer_unhighlight(*self.inner) }
564    }
565
566    /// Returns the highlight position
567    pub fn highlight_position(&self) -> Option<(i32, i32)> {
568        assert!(!self.inner.is_null());
569        unsafe {
570            let mut start = 0;
571            let mut end = 0;
572            let ret =
573                Fl_Text_Buffer_highlight_position(*self.inner, &mut start as _, &mut end as _);
574            if ret == 0 {
575                None
576            } else {
577                let x = (start, end);
578                Some(x)
579            }
580        }
581    }
582
583    /// Returns the highlighted text
584    pub fn highlight_text(&self) -> String {
585        assert!(!self.inner.is_null());
586        unsafe {
587            let x = Fl_Text_Buffer_highlight_text(*self.inner);
588            assert!(!x.is_null());
589            CStr::from_ptr(x as *mut raw::c_char)
590                .to_string_lossy()
591                .to_string()
592        }
593    }
594
595    /// Returns the line at pos
596    pub fn line_text(&self, pos: i32) -> String {
597        assert!(!self.inner.is_null());
598        unsafe {
599            let x = Fl_Text_Buffer_line_text(*self.inner, pos);
600            assert!(!x.is_null());
601            CStr::from_ptr(x as *mut raw::c_char)
602                .to_string_lossy()
603                .to_string()
604        }
605    }
606
607    /// Returns the index of the line's start position at pos
608    pub fn line_start(&self, pos: i32) -> i32 {
609        assert!(!self.inner.is_null());
610        unsafe { Fl_Text_Buffer_line_start(*self.inner, pos) }
611    }
612
613    /// Returns the index of the line's end position at pos
614    pub fn line_end(&self, pos: i32) -> i32 {
615        assert!(!self.inner.is_null());
616        unsafe { Fl_Text_Buffer_line_end(*self.inner, pos) }
617    }
618
619    /// Returns the index of the first character of a word at pos
620    pub fn word_start(&self, pos: i32) -> i32 {
621        assert!(!self.inner.is_null());
622        unsafe { Fl_Text_Buffer_word_start(*self.inner, pos) }
623    }
624
625    /// Returns the index of the last character of a word at pos
626    pub fn word_end(&self, pos: i32) -> i32 {
627        assert!(!self.inner.is_null());
628        unsafe { Fl_Text_Buffer_word_end(*self.inner, pos) }
629    }
630
631    /// Counts the lines from start to end
632    pub fn count_lines(&self, start: i32, end: i32) -> i32 {
633        assert!(!self.inner.is_null());
634        unsafe { Fl_Text_Buffer_count_lines(*self.inner, start, end) }
635    }
636
637    /// Calls the modify callbacks
638    pub fn call_modify_callbacks(&mut self) {
639        assert!(!self.inner.is_null());
640        unsafe { Fl_Text_Buffer_call_modify_callbacks(*self.inner) }
641    }
642
643    /// Calls the pre-delete callbacks
644    pub fn call_predelete_callbacks(&mut self) {
645        assert!(!self.inner.is_null());
646        unsafe { Fl_Text_Buffer_call_predelete_callbacks(*self.inner) }
647    }
648
649    /// Adds a modify callback.
650    /// callback args:
651    /// pos: i32, inserted items: i32, deleted items: i32, restyled items: i32, `deleted_text`
652    pub fn add_modify_callback_<F: FnMut(i32, i32, i32, i32, Option<&str>) + 'static>(
653        &mut self,
654        cb: F,
655    ) {
656        assert!(!self.inner.is_null());
657        unsafe {
658            let a: ModifyCbPtr = Box::into_raw(Box::new(Box::new(cb)));
659            let data: *mut raw::c_void = a as *mut std::ffi::c_void;
660            let callback: Fl_Text_Modify_Cb = Some(text_modify_shim);
661            Fl_Text_Buffer_add_modify_callback(*self.inner, callback, data);
662        }
663    }
664
665    /// Removes a modify callback.
666    /// callback args:
667    /// pos: i32, inserted items: i32, deleted items: i32, restyled items: i32, `deleted_text`
668    pub fn remove_modify_callback_<F: FnMut(i32, i32, i32, i32, Option<&str>) + 'static>(
669        &mut self,
670        cb: F,
671    ) {
672        assert!(!self.inner.is_null());
673        unsafe {
674            let a: ModifyCbPtr = Box::into_raw(Box::new(Box::new(cb)));
675            let data: *mut raw::c_void = a as *mut std::ffi::c_void;
676            let callback: Fl_Text_Modify_Cb = Some(text_modify_shim);
677            Fl_Text_Buffer_remove_modify_callback(*self.inner, callback, data);
678        }
679    }
680
681    /// Adds a modify callback.
682    /// callback args:
683    /// pos: i32, inserted items: i32, deleted items: i32, restyled items: i32, `deleted_text`
684    pub fn add_modify_callback<F: FnMut(&mut Self, i32, i32, i32, i32, Option<&str>) + 'static>(
685        &mut self,
686        mut cb: F,
687    ) {
688        let mut s = self.clone();
689        self.add_modify_callback_(move |pos, ins, del, restyled, txt| {
690            cb(&mut s, pos, ins, del, restyled, txt);
691        })
692    }
693
694    /// Removes a modify callback.
695    /// callback args:
696    /// pos: i32, inserted items: i32, deleted items: i32, restyled items: i32, `deleted_text`
697    pub fn remove_modify_callback<
698        F: FnMut(&mut Self, i32, i32, i32, i32, Option<&str>) + 'static,
699    >(
700        &mut self,
701        mut cb: F,
702    ) {
703        let mut s = self.clone();
704        self.remove_modify_callback_(move |pos, ins, del, restyled, txt| {
705            cb(&mut s, pos, ins, del, restyled, txt);
706        })
707    }
708
709    /// Adds a pre-delete callback.
710    /// callback args: pos: i32, deleted items: i32
711    pub fn add_predelete_callback_<F: FnMut(i32, i32) + 'static>(&mut self, cb: F) {
712        assert!(!self.inner.is_null());
713        unsafe {
714            let a: *mut Box<dyn FnMut(i32, i32)> = Box::into_raw(Box::new(Box::new(cb)));
715            let data: *mut raw::c_void = a as *mut std::ffi::c_void;
716            let callback: Fl_Text_Predelete_Cb = Some(text_predelete_shim);
717            Fl_Text_Buffer_add_predelete_callback(*self.inner, callback, data);
718        }
719    }
720
721    /// Removes a pre-delete callback.
722    /// callback args: pos: i32, deleted items: i32
723    pub fn remove_predelete_callback_<F: FnMut(i32, i32) + 'static>(&mut self, cb: F) {
724        assert!(!self.inner.is_null());
725        unsafe {
726            let a: *mut Box<dyn FnMut(i32, i32)> = Box::into_raw(Box::new(Box::new(cb)));
727            let data: *mut raw::c_void = a as *mut std::ffi::c_void;
728            let callback: Fl_Text_Predelete_Cb = Some(text_predelete_shim);
729            Fl_Text_Buffer_remove_predelete_callback(*self.inner, callback, data);
730        }
731    }
732
733    /// Adds a pre-delete callback.
734    /// callback args: pos: i32, deleted items: i32
735    pub fn add_predelete_callback<F: FnMut(&mut Self, i32, i32) + 'static>(&mut self, mut cb: F) {
736        let mut s = self.clone();
737        self.add_predelete_callback_(move |pos, del| {
738            cb(&mut s, pos, del);
739        })
740    }
741
742    /// Removes a pre-delete callback.
743    /// callback args: pos: i32, deleted items: i32
744    pub fn remove_predelete_callback<F: FnMut(&mut Self, i32, i32) + 'static>(
745        &mut self,
746        mut cb: F,
747    ) {
748        let mut s = self.clone();
749        self.remove_predelete_callback_(move |pos, del| {
750            cb(&mut s, pos, del);
751        })
752    }
753
754    /// Forward search for a string
755    pub fn search_forward(
756        &self,
757        start_pos: i32,
758        search_string: &str,
759        match_case: bool,
760    ) -> Option<i32> {
761        unsafe {
762            let search_string = CString::safe_new(search_string);
763            let mut found_pos = 0;
764            let ret = Fl_Text_Buffer_search_forward(
765                *self.inner,
766                start_pos,
767                search_string.as_ptr() as _,
768                &mut found_pos as _,
769                match_case as _,
770            );
771            if ret == 0 { None } else { Some(found_pos) }
772        }
773    }
774
775    /// Backward search for a string
776    pub fn search_backward(
777        &self,
778        start_pos: i32,
779        search_string: &str,
780        match_case: bool,
781    ) -> Option<i32> {
782        unsafe {
783            let search_string = CString::safe_new(search_string);
784            let mut found_pos = 0;
785            let ret = Fl_Text_Buffer_search_backward(
786                *self.inner,
787                start_pos,
788                search_string.as_ptr() as _,
789                &mut found_pos as _,
790                match_case as _,
791            );
792            if ret == 0 { None } else { Some(found_pos) }
793        }
794    }
795
796    /// Forward search for a char
797    pub fn find_char_forward(&self, start_pos: i32, search_char: char) -> Option<i32> {
798        unsafe {
799            let mut found_pos = 0;
800            let ret = Fl_Text_Buffer_findchar_forward(
801                *self.inner,
802                start_pos,
803                search_char as _,
804                &mut found_pos as _,
805            );
806            if ret == 0 { None } else { Some(found_pos) }
807        }
808    }
809
810    /// Backward search for a char
811    pub fn find_char_backward(&self, start_pos: i32, search_char: char) -> Option<i32> {
812        unsafe {
813            let mut found_pos = 0;
814            let ret = Fl_Text_Buffer_findchar_backward(
815                *self.inner,
816                start_pos,
817                search_char as _,
818                &mut found_pos as _,
819            );
820            if ret == 0 { None } else { Some(found_pos) }
821        }
822    }
823
824    /// Returns the Unicode character at byte position `pos` (aligned to UTF-8 boundary)
825    pub fn char_at(&self, pos: i32) -> Option<char> {
826        assert!(!self.inner.is_null());
827        unsafe {
828            let ch = Fl_Text_Buffer_char_at(*self.inner, pos);
829            std::char::from_u32(ch)
830        }
831    }
832
833    /// Returns the raw byte at byte position `pos`
834    pub fn byte_at(&self, pos: i32) -> u8 {
835        assert!(!self.inner.is_null());
836        unsafe { Fl_Text_Buffer_byte_at(*self.inner, pos) as u8 }
837    }
838
839    /// Returns a raw pointer to the underlying buffer at byte position `pos`
840    /// # Safety
841    /// The pointer becomes invalid on subsequent mutations of the buffer.
842    pub unsafe fn address(&self, pos: i32) -> *const raw::c_char {
843        assert!(!self.inner.is_null());
844        unsafe { Fl_Text_Buffer_address(*self.inner as *const _, pos) }
845    }
846
847    /// Returns a mutable raw pointer to the underlying buffer at byte position `pos`
848    /// # Safety
849    /// The pointer becomes invalid on subsequent mutations of the buffer.
850    pub unsafe fn address_mut(&mut self, pos: i32) -> *mut raw::c_char {
851        assert!(!self.inner.is_null());
852        unsafe { Fl_Text_Buffer_address2(*self.inner, pos) }
853    }
854
855    /// Align an index into the buffer to the current or previous UTF-8 boundary.
856    pub fn utf8_align(&self, pos: i32) -> i32 {
857        assert!(!self.inner.is_null());
858        unsafe { Fl_Text_Buffer_utf8_align(*self.inner as *const _, pos) }
859    }
860
861    /// Returns true if position `pos` is a word separator
862    pub fn is_word_separator(&self, pos: i32) -> bool {
863        assert!(!self.inner.is_null());
864        unsafe { Fl_Text_Buffer_is_word_separator(*self.inner as *const _, pos) != 0 }
865    }
866}
867
868#[cfg(not(feature = "single-threaded"))]
869unsafe impl Sync for TextBuffer {}
870
871#[cfg(not(feature = "single-threaded"))]
872unsafe impl Send for TextBuffer {}
873
874impl PartialEq for TextBuffer {
875    fn eq(&self, other: &Self) -> bool {
876        self.inner == other.inner
877    }
878}
879
880impl Eq for TextBuffer {}
881
882impl Clone for TextBuffer {
883    fn clone(&self) -> TextBuffer {
884        assert!(!self.inner.is_null());
885        TextBuffer {
886            inner: BufWrapper::clone(&self.inner),
887        }
888    }
889}
890
891impl Drop for TextBuffer {
892    fn drop(&mut self) {
893        assert!(!self.inner.is_null());
894        if BufWrapper::strong_count(&self.inner) == 1 {
895            unsafe {
896                Fl_Text_Buffer_delete(*self.inner);
897            }
898        }
899    }
900}
901
902/// Defines wrap modes
903#[repr(i32)]
904#[derive(Debug, Copy, Clone, PartialEq, Eq)]
905pub enum WrapMode {
906    /// No wrapping
907    None,
908    /// Wrap text at certain column
909    AtColumn,
910    /// Wrap text at certain pixel
911    AtPixel,
912    /// Wrap text at certain bounds
913    AtBounds,
914}
915
916/// Defines drag types
917#[repr(i32)]
918#[derive(Debug, Copy, Clone, PartialEq, Eq)]
919pub enum DragType {
920    /// No dragging
921    None = -2,
922    /// Drag Start "drag n drop" event
923    StartDnd = -1,
924    /// Drag single character
925    Char = 0,
926    /// Drag single word
927    Word = 1,
928    /// Drag single line
929    Line = 2,
930}
931
932/// Creates a non-editable text display widget
933#[derive(Debug)]
934pub struct TextDisplay {
935    inner: crate::widget::WidgetTracker,
936    is_derived: bool,
937}
938
939crate::macros::widget::impl_widget_ext!(TextDisplay, Fl_Text_Display);
940crate::macros::widget::impl_widget_base!(TextDisplay, Fl_Text_Display);
941crate::macros::widget::impl_widget_default!(TextDisplay, Fl_Text_Display);
942crate::macros::display::impl_display_ext!(TextDisplay, Fl_Text_Display);
943
944/// Creates an editable text display widget
945#[derive(Debug)]
946pub struct TextEditor {
947    inner: crate::widget::WidgetTracker,
948    is_derived: bool,
949}
950
951crate::macros::widget::impl_widget_ext!(TextEditor, Fl_Text_Editor);
952crate::macros::widget::impl_widget_base!(TextEditor, Fl_Text_Editor);
953crate::macros::widget::impl_widget_default!(TextEditor, Fl_Text_Editor);
954crate::macros::display::impl_display_ext!(TextEditor, Fl_Text_Editor);
955
956/// Alias Fl_Text_Editor for use in `add_key_binding`
957pub type TextEditorPtr = *mut Fl_Text_Editor;
958
959/// The attribute of the style entry
960#[derive(Debug, Clone, Copy, PartialEq, Eq)]
961#[repr(u32)]
962#[non_exhaustive]
963pub enum TextAttr {
964    /// No attribute
965    None = 0x0000,
966    /// Use the background color in the `bgcolor` field
967    BgColor = 0x0001,
968    /// Use the background color in the `bgcolor` field to highlight the whole line
969    BgColorExt = 0x0003,
970    /// A single underline, underline types are mutually exclusive
971    Underline = 0x0004,
972    /// Grammar suggestion (blue dotted underline)
973    Grammar = 0x0008,
974    /// Spelling suggestion (red dotted underline)
975    Spelling = 0x000C,
976    /// Line through the middle of the text
977    StrikeThrough = 0x0010,
978}
979
980/// Defines the styles used in the `set_highlight_data`, which is used with style buffers
981#[derive(Debug, Clone, Copy, PartialEq, Eq)]
982pub struct StyleTableEntry {
983    /// Font color
984    pub color: Color,
985    /// Font type
986    pub font: Font,
987    /// Font size
988    pub size: i32,
989    /// attribute
990    pub attr: TextAttr,
991    /// background color
992    pub bgcolor: Color,
993}
994
995impl Default for StyleTableEntry {
996    fn default() -> Self {
997        Self {
998            color: Color::Foreground,
999            font: Font::Helvetica,
1000            size: crate::app::font_size(),
1001            attr: TextAttr::None,
1002            bgcolor: Color::Background2,
1003        }
1004    }
1005}
1006
1007impl TextEditor {
1008    /// Any state/shortcut
1009    pub const AnyState: crate::enums::Shortcut = crate::enums::Shortcut::from_i32(-1);
1010
1011    /// Set to insert mode
1012    pub fn set_insert_mode(&mut self, b: bool) {
1013        assert!(self.has_buffer());
1014        unsafe { Fl_Text_Editor_set_insert_mode(self.inner.widget() as _, b as i32) }
1015    }
1016
1017    /// Returns whether insert mode is set
1018    pub fn insert_mode(&self) -> bool {
1019        assert!(self.has_buffer());
1020        unsafe { Fl_Text_Editor_insert_mode(self.inner.widget() as _) != 0 }
1021    }
1022
1023    /// Set tab navigation
1024    pub fn set_tab_nav(&mut self, val: bool) {
1025        assert!(self.has_buffer());
1026        unsafe { Fl_Text_Editor_set_tab_nav(self.inner.widget() as _, val as i32) }
1027    }
1028
1029    /// Returns whether tab navigation is set
1030    pub fn tab_nav(&self) -> bool {
1031        assert!(self.has_buffer());
1032        unsafe { Fl_Text_Editor_tab_nav(self.inner.widget() as _) != 0 }
1033    }
1034
1035    /// Copies the text within the `TextEditor` widget
1036    pub fn copy(&self) {
1037        assert!(self.has_buffer());
1038        unsafe {
1039            Fl_Text_Editor_kf_copy(self.inner.widget() as _);
1040        }
1041    }
1042
1043    /// Cuts the text within the `TextEditor` widget
1044    pub fn cut(&self) {
1045        assert!(self.has_buffer());
1046        unsafe {
1047            Fl_Text_Editor_kf_cut(self.inner.widget() as _);
1048        }
1049    }
1050
1051    /// Pastes text from the clipboard into the `TextEditor` widget
1052    pub fn paste(&self) {
1053        assert!(self.has_buffer());
1054        unsafe {
1055            Fl_Text_Editor_kf_paste(self.inner.widget() as _);
1056        }
1057    }
1058
1059    /// Undo changes in the `TextEditor` widget
1060    pub fn undo(&self) {
1061        assert!(self.has_buffer());
1062        unsafe {
1063            Fl_Text_Editor_kf_undo(self.inner.widget() as _);
1064        }
1065    }
1066
1067    /// Undo changes in the `TextEditor` widget
1068    pub fn redo(&self) {
1069        assert!(self.has_buffer());
1070        unsafe {
1071            Fl_Text_Editor_kf_redo(self.inner.widget() as _);
1072        }
1073    }
1074
1075    /// Inserts the text associated with key 'c'
1076    pub fn kf_default(&mut self, c: Key) {
1077        assert!(self.has_buffer());
1078        unsafe {
1079            Fl_Text_Editor_kf_default(c.bits(), self.inner.widget() as _);
1080        }
1081    }
1082
1083    /// Ignores the key 'c' in editor
1084    pub fn kf_ignore(&mut self, c: Key) {
1085        assert!(self.has_buffer());
1086        unsafe {
1087            Fl_Text_Editor_kf_ignore(c.bits(), self.inner.widget() as _);
1088        }
1089    }
1090
1091    /// Does a backspace
1092    pub fn kf_backspace(&mut self) {
1093        assert!(self.has_buffer());
1094        unsafe {
1095            Fl_Text_Editor_kf_backspace(self.inner.widget() as _);
1096        }
1097    }
1098
1099    /// Inserts a new line
1100    pub fn kf_enter(&mut self) {
1101        assert!(self.has_buffer());
1102        unsafe {
1103            Fl_Text_Editor_kf_enter(self.inner.widget() as _);
1104        }
1105    }
1106
1107    /// Moves the cursor in the direction indicated by the key
1108    pub fn kf_move(&mut self, c: Key) {
1109        assert!(self.has_buffer());
1110        unsafe {
1111            Fl_Text_Editor_kf_move(c.bits(), self.inner.widget() as _);
1112        }
1113    }
1114
1115    /// Extends the current selection in the direction of key 'c'
1116    pub fn kf_shift_move(&mut self, c: Key) {
1117        assert!(self.has_buffer());
1118        unsafe {
1119            Fl_Text_Editor_kf_shift_move(c.bits(), self.inner.widget() as _);
1120        }
1121    }
1122
1123    /// Moves the current text cursor in the direction indicated by control key 'c'
1124    pub fn kf_ctrl_move(&mut self, c: Key) {
1125        assert!(self.has_buffer());
1126        unsafe {
1127            Fl_Text_Editor_kf_ctrl_move(c.bits(), self.inner.widget() as _);
1128        }
1129    }
1130
1131    /// Extends the current selection in the direction indicated by control key 'c'
1132    pub fn kf_c_s_move(&mut self, c: Key) {
1133        assert!(self.has_buffer());
1134        unsafe {
1135            Fl_Text_Editor_kf_c_s_move(c.bits(), self.inner.widget() as _);
1136        }
1137    }
1138
1139    /// Moves the current text cursor in the direction indicated by meta key 'c'
1140    pub fn kf_meta_move(&mut self, c: Key) {
1141        assert!(self.has_buffer());
1142        unsafe {
1143            Fl_Text_Editor_kf_meta_move(c.bits(), self.inner.widget() as _);
1144        }
1145    }
1146
1147    /// Extends the current selection in the direction indicated by meta key 'c'
1148    pub fn kf_m_s_move(&mut self, c: Key) {
1149        assert!(self.has_buffer());
1150        unsafe {
1151            Fl_Text_Editor_kf_m_s_move(c.bits(), self.inner.widget() as _);
1152        }
1153    }
1154
1155    /// Moves the text cursor to the beginning of the current line
1156    pub fn kf_home(&mut self) {
1157        assert!(self.has_buffer());
1158        unsafe {
1159            Fl_Text_Editor_kf_home(self.inner.widget() as _);
1160        }
1161    }
1162
1163    /// Moves the text cursor to the end of the current line
1164    pub fn kf_end(&mut self) {
1165        assert!(self.has_buffer());
1166        unsafe {
1167            Fl_Text_Editor_kf_end(self.inner.widget() as _);
1168        }
1169    }
1170
1171    /// Moves the text cursor one character to the left
1172    pub fn kf_left(&mut self) {
1173        assert!(self.has_buffer());
1174        unsafe {
1175            Fl_Text_Editor_kf_left(self.inner.widget() as _);
1176        }
1177    }
1178
1179    /// Moves the text cursor one line up
1180    pub fn kf_up(&mut self) {
1181        assert!(self.has_buffer());
1182        unsafe {
1183            Fl_Text_Editor_kf_up(self.inner.widget() as _);
1184        }
1185    }
1186
1187    /// Moves the text cursor one character to the right
1188    pub fn kf_right(&mut self) {
1189        assert!(self.has_buffer());
1190        unsafe {
1191            Fl_Text_Editor_kf_right(self.inner.widget() as _);
1192        }
1193    }
1194
1195    /// Moves the text cursor one line down
1196    pub fn kf_down(&mut self) {
1197        assert!(self.has_buffer());
1198        unsafe {
1199            Fl_Text_Editor_kf_down(self.inner.widget() as _);
1200        }
1201    }
1202
1203    /// Moves the text cursor up one page
1204    pub fn kf_page_up(&mut self) {
1205        assert!(self.has_buffer());
1206        unsafe {
1207            Fl_Text_Editor_kf_page_up(self.inner.widget() as _);
1208        }
1209    }
1210
1211    /// Moves the text cursor down one page
1212    pub fn kf_page_down(&mut self) {
1213        assert!(self.has_buffer());
1214        unsafe {
1215            Fl_Text_Editor_kf_page_down(self.inner.widget() as _);
1216        }
1217    }
1218
1219    /// Toggles the insert mode for the editor
1220    pub fn kf_insert(&mut self) {
1221        assert!(self.has_buffer());
1222        unsafe {
1223            Fl_Text_Editor_kf_insert(self.inner.widget() as _);
1224        }
1225    }
1226
1227    /// Does a delete of selected text or the current character in the current buffer
1228    pub fn kf_delete(&mut self) {
1229        assert!(self.has_buffer());
1230        unsafe {
1231            Fl_Text_Editor_kf_delete(self.inner.widget() as _);
1232        }
1233    }
1234
1235    /// Selects all text in the associated buffer
1236    pub fn kf_select_all(&mut self) {
1237        assert!(self.has_buffer());
1238        unsafe {
1239            Fl_Text_Editor_kf_select_all(self.inner.widget() as _);
1240        }
1241    }
1242
1243    /// Add a key binding
1244    pub fn add_key_binding(
1245        &mut self,
1246        key: crate::enums::Key,
1247        shortcut: crate::enums::Shortcut,
1248        cb: fn(key: crate::enums::Key, editor: TextEditorPtr) -> i32,
1249    ) {
1250        unsafe {
1251            Fl_Text_Editor_add_key_binding(
1252                self.inner.widget() as _,
1253                key.bits(),
1254                shortcut.bits(),
1255                std::mem::transmute(Some(cb)),
1256            );
1257        }
1258    }
1259
1260    /// Remove a key binding
1261    pub fn remove_key_binding(&mut self, key: crate::enums::Key, shortcut: crate::enums::Shortcut) {
1262        unsafe {
1263            Fl_Text_Editor_remove_key_binding(
1264                self.inner.widget() as _,
1265                key.bits(),
1266                shortcut.bits(),
1267            );
1268        }
1269    }
1270}