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#[repr(i32)]
54#[derive(Debug, Copy, Clone, PartialEq, Eq)]
55pub enum PositionType {
56 Cursor,
58 Character,
60}
61
62#[repr(i32)]
64#[derive(Debug, Copy, Clone, PartialEq, Eq)]
65pub enum Cursor {
66 Normal,
68 Caret,
70 Dim,
72 Block,
74 Heavy,
76 Simple,
78}
79
80#[derive(Debug)]
82pub struct TextBuffer {
83 inner: BufWrapper,
84}
85
86impl std::default::Default for TextBuffer {
87 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 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 pub unsafe fn delete_buffer(buf: Self) {
114 unsafe { Self::delete(buf) }
115 }
116
117 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 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 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 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 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 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 pub fn length(&self) -> i32 {
195 assert!(!self.inner.is_null());
196 unsafe { Fl_Text_Buffer_length(*self.inner) }
197 }
198
199 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 pub fn tab_distance(&self) -> i32 {
415 assert!(!self.inner.is_null());
416 unsafe { Fl_Text_Buffer_tab_distance(*self.inner) }
417 }
418
419 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 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 pub fn selected(&self) -> bool {
433 assert!(!self.inner.is_null());
434 unsafe { Fl_Text_Buffer_selected(*self.inner) != 0 }
435 }
436
437 pub fn unselect(&mut self) {
439 assert!(!self.inner.is_null());
440 unsafe { Fl_Text_Buffer_unselect(*self.inner) }
441 }
442
443 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 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 pub fn remove_selection(&mut self) {
474 assert!(!self.inner.is_null());
475 unsafe { Fl_Text_Buffer_remove_selection(*self.inner) }
476 }
477
478 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 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 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 pub fn secondary_unselect(&mut self) {
499 assert!(!self.inner.is_null());
500 unsafe { Fl_Text_Buffer_secondary_unselect(*self.inner) }
501 }
502
503 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 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 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 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 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 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 pub fn unhighlight(&mut self) {
562 assert!(!self.inner.is_null());
563 unsafe { Fl_Text_Buffer_unhighlight(*self.inner) }
564 }
565
566 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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#[repr(i32)]
904#[derive(Debug, Copy, Clone, PartialEq, Eq)]
905pub enum WrapMode {
906 None,
908 AtColumn,
910 AtPixel,
912 AtBounds,
914}
915
916#[repr(i32)]
918#[derive(Debug, Copy, Clone, PartialEq, Eq)]
919pub enum DragType {
920 None = -2,
922 StartDnd = -1,
924 Char = 0,
926 Word = 1,
928 Line = 2,
930}
931
932#[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#[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
956pub type TextEditorPtr = *mut Fl_Text_Editor;
958
959#[derive(Debug, Clone, Copy, PartialEq, Eq)]
961#[repr(u32)]
962#[non_exhaustive]
963pub enum TextAttr {
964 None = 0x0000,
966 BgColor = 0x0001,
968 BgColorExt = 0x0003,
970 Underline = 0x0004,
972 Grammar = 0x0008,
974 Spelling = 0x000C,
976 StrikeThrough = 0x0010,
978}
979
980#[derive(Debug, Clone, Copy, PartialEq, Eq)]
982pub struct StyleTableEntry {
983 pub color: Color,
985 pub font: Font,
987 pub size: i32,
989 pub attr: TextAttr,
991 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 pub const AnyState: crate::enums::Shortcut = crate::enums::Shortcut::from_i32(-1);
1010
1011 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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}