rat_text/
text_mask_core.rs

1use crate::clipboard::{Clipboard, global_clipboard};
2use crate::core::{TextCore, TextString};
3#[allow(deprecated)]
4use crate::glyph::{Glyph, GlyphIter};
5use crate::glyph2::{Glyph2, GlyphIter2, TextWrap2};
6use crate::grapheme::StrGraphemes;
7use crate::text_mask_core::mask::{EditDirection, Mask, MaskToken};
8use crate::text_store::SkipLine;
9use crate::undo_buffer::{UndoBuffer, UndoEntry, UndoVec};
10use crate::{Cursor, Grapheme, TextError, TextPosition, TextRange, upos_type};
11use format_num_pattern::core::{clean_num, map_num};
12use format_num_pattern::{CurrencySym, NumberFormat, NumberSymbols};
13use std::borrow::Cow;
14use std::iter::once;
15use std::ops::Range;
16use std::{fmt, slice};
17use unicode_segmentation::UnicodeSegmentation;
18
19/// Text editing core for MaskedInput.
20#[derive(Debug, Clone)]
21pub struct MaskedCore {
22    // text
23    masked: TextCore<TextString>,
24    // number symbols
25    sym: Option<NumberSymbols>,
26    // parsed mask
27    mask: Vec<MaskToken>,
28}
29
30impl Default for MaskedCore {
31    fn default() -> Self {
32        let mut value = TextCore::new(
33            Some(Box::new(UndoVec::new(99))),
34            Some(Box::new(global_clipboard())),
35        );
36        value.set_glyph_line_break(false);
37
38        Self {
39            masked: value,
40            sym: None,
41            mask: Default::default(),
42        }
43    }
44}
45
46impl MaskedCore {
47    pub fn new() -> Self {
48        Self::default()
49    }
50
51    /// Set the decimal separator and other symbols.
52    /// Only used for rendering and to map user input.
53    /// The value itself uses "."
54    pub fn set_num_symbols(&mut self, sym: NumberSymbols) {
55        self.sym = Some(sym);
56    }
57
58    fn dec_sep(&self) -> char {
59        if let Some(sym) = &self.sym {
60            sym.decimal_sep
61        } else {
62            '.'
63        }
64    }
65
66    fn grp_sep(&self) -> char {
67        if let Some(sym) = &self.sym {
68            // fallback for empty grp-char.
69            // it would be really ugly, if we couldn't keep
70            //   mask-idx == grapheme-idx
71            sym.decimal_grp.unwrap_or(' ')
72        } else {
73            ','
74        }
75    }
76
77    fn neg_sym(&self) -> char {
78        if let Some(sym) = &self.sym {
79            sym.negative_sym
80        } else {
81            '-'
82        }
83    }
84
85    fn pos_sym(&self) -> char {
86        if let Some(sym) = &self.sym {
87            sym.positive_sym
88        } else {
89            ' '
90        }
91    }
92
93    /// Changes the mask.
94    /// Resets the value to a default.
95    pub fn set_mask<S: AsRef<str>>(&mut self, s: S) -> Result<(), fmt::Error> {
96        self.mask = Self::parse_mask(s.as_ref())?;
97        self.clear();
98        Ok(())
99    }
100
101    #[allow(clippy::needless_range_loop)]
102    fn parse_mask(mask_str: &str) -> Result<Vec<MaskToken>, fmt::Error> {
103        let mut out = Vec::<MaskToken>::new();
104
105        let mut start_sub = 0;
106        let mut start_sec = 0;
107        let mut sec_id = 0;
108        let mut last_mask = Mask::None;
109        let mut dec_dir = EditDirection::Rtol;
110        let mut esc = false;
111        let mut idx = 0;
112        for m in mask_str.graphemes(true).chain(once("")) {
113            let mask = if esc {
114                esc = false;
115                Mask::Separator(Box::from(m))
116            } else {
117                match m {
118                    "0" => Mask::Digit0(dec_dir),
119                    "9" => Mask::Digit(dec_dir),
120                    "#" => Mask::Numeric(dec_dir),
121                    "." => Mask::DecimalSep,
122                    "," => Mask::GroupingSep,
123                    "-" => Mask::Sign,
124                    "+" => Mask::Plus,
125                    "h" => Mask::Hex,
126                    "H" => Mask::Hex0,
127                    "o" => Mask::Oct,
128                    "O" => Mask::Oct0,
129                    "d" => Mask::Dec,
130                    "D" => Mask::Dec0,
131                    "l" => Mask::Letter,
132                    "a" => Mask::LetterOrDigit,
133                    "c" => Mask::LetterDigitSpace,
134                    "_" => Mask::AnyChar,
135                    "" => Mask::None,
136                    " " => Mask::Separator(Box::from(m)),
137                    "\\" => {
138                        esc = true;
139                        continue;
140                    }
141                    _ => return Err(fmt::Error),
142                }
143            };
144
145            match mask {
146                Mask::Digit0(_)
147                | Mask::Digit(_)
148                | Mask::Numeric(_)
149                | Mask::GroupingSep
150                | Mask::Sign
151                | Mask::Plus => {
152                    // no change
153                }
154                Mask::DecimalSep => {
155                    dec_dir = EditDirection::Ltor;
156                }
157                Mask::Hex0
158                | Mask::Hex
159                | Mask::Oct0
160                | Mask::Oct
161                | Mask::Dec0
162                | Mask::Dec
163                | Mask::Letter
164                | Mask::LetterOrDigit
165                | Mask::LetterDigitSpace
166                | Mask::AnyChar
167                | Mask::Separator(_) => {
168                    // reset to default number input direction
169                    dec_dir = EditDirection::Rtol
170                }
171                Mask::None => {
172                    // no change, doesn't matter
173                }
174            }
175
176            if matches!(mask, Mask::Separator(_)) || mask.section() != last_mask.section() {
177                for j in start_sec..idx {
178                    out[j].sec_id = sec_id;
179                    out[j].sec_start = start_sec as upos_type;
180                    out[j].sec_end = idx as upos_type;
181                }
182                sec_id += 1;
183                start_sec = idx;
184            }
185            if matches!(mask, Mask::Separator(_)) || mask.sub_section() != last_mask.sub_section() {
186                for j in start_sub..idx {
187                    out[j].sub_start = start_sub as upos_type;
188                    out[j].sub_end = idx as upos_type;
189                }
190                start_sub = idx;
191            }
192
193            let tok = MaskToken {
194                sec_id: 0,
195                sec_start: 0,
196                sec_end: 0,
197                sub_start: 0,
198                sub_end: 0,
199                peek_left: last_mask,
200                right: mask.clone(),
201                edit: mask.edit_value().into(),
202            };
203            out.push(tok);
204
205            idx += 1;
206            last_mask = mask;
207        }
208        for j in start_sec..out.len() {
209            out[j].sec_id = sec_id;
210            out[j].sec_start = start_sec as upos_type;
211            out[j].sec_end = mask_str.graphemes(true).count() as upos_type;
212        }
213        for j in start_sub..out.len() {
214            out[j].sub_start = start_sub as upos_type;
215            out[j].sub_end = mask_str.graphemes(true).count() as upos_type;
216        }
217
218        Ok(out)
219    }
220
221    /// Return the mask.
222    pub fn mask(&self) -> String {
223        use std::fmt::Write;
224
225        let mut buf = String::new();
226        for t in self.mask.iter() {
227            _ = write!(buf, "{}", t.right);
228        }
229        buf
230    }
231}
232
233impl MaskedCore {
234    /// Clipboard
235    pub fn set_clipboard(&mut self, clip: Option<Box<dyn Clipboard + 'static>>) {
236        self.masked.set_clipboard(clip);
237    }
238
239    /// Clipboard
240    pub fn clipboard(&self) -> Option<&dyn Clipboard> {
241        self.masked.clipboard()
242    }
243}
244
245impl MaskedCore {
246    /// Undo
247    #[inline]
248    pub fn set_undo_buffer(&mut self, undo: Option<Box<dyn UndoBuffer>>) {
249        self.masked.set_undo_buffer(undo);
250    }
251
252    /// Set undo count
253    #[inline]
254    pub fn set_undo_count(&mut self, n: u32) {
255        self.masked.set_undo_count(n);
256    }
257
258    /// Begin a sequence of changes that should be undone in one go.
259    #[inline]
260    pub fn begin_undo_seq(&mut self) {
261        self.masked.begin_undo_seq();
262    }
263
264    /// End a sequence of changes that should be undone in one go.
265    #[inline]
266    pub fn end_undo_seq(&mut self) {
267        self.masked.end_undo_seq();
268    }
269
270    /// Undo
271    #[inline]
272    pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
273        self.masked.undo_buffer()
274    }
275
276    /// Undo
277    #[inline]
278    pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
279        self.masked.undo_buffer_mut()
280    }
281
282    /// Undo last.
283    pub fn undo(&mut self) -> bool {
284        self.masked.undo()
285    }
286
287    /// Redo last.
288    pub fn redo(&mut self) -> bool {
289        self.masked.redo()
290    }
291
292    /// Get last replay recording.
293    pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
294        self.masked.recent_replay_log()
295    }
296
297    /// Replay a recording of changes.
298    pub fn replay_log(&mut self, replay: &[UndoEntry]) {
299        self.masked.replay_log(replay)
300    }
301}
302
303impl MaskedCore {
304    /// Set all styles.
305    ///
306    /// The ranges are byte-ranges. The usize value is the index of the
307    /// actual style. Those are set with the widget.
308    #[inline]
309    pub fn set_styles(&mut self, new_styles: Vec<(Range<usize>, usize)>) {
310        self.masked.set_styles(new_styles);
311    }
312
313    /// Add a style for the given byte-range.
314    ///
315    /// The usize value is the index of the actual style.
316    /// Those are set at the widget.
317    #[inline]
318    pub fn add_style(&mut self, range: Range<usize>, style: usize) {
319        self.masked.add_style(range, style);
320    }
321
322    /// Remove a style for the given byte-range.
323    ///
324    /// Range and style must match to be removed.
325    #[inline]
326    pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
327        self.masked.remove_style(range, style);
328    }
329
330    /// Find all values for the given position.
331    ///
332    /// Creates a cache for the styles in range.
333    #[inline]
334    pub(crate) fn styles_at_page(&self, pos: usize, range: Range<usize>, buf: &mut Vec<usize>) {
335        self.masked.styles_at_page(pos, range, buf);
336    }
337
338    /// Find all styles that touch the given range.
339    pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
340        self.masked.styles_in(range, buf)
341    }
342
343    /// Finds all styles for the given position.
344    #[inline]
345    pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
346        self.masked.styles_at(byte_pos, buf);
347    }
348
349    /// Check if the given style applies at the position and
350    /// return the complete range for the style.
351    #[inline]
352    pub fn style_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
353        self.masked.style_match(byte_pos, style)
354    }
355
356    /// List of all styles.
357    #[inline]
358    pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
359        self.masked.styles()
360    }
361}
362
363impl MaskedCore {
364    /// Set the cursor position.
365    /// The value is capped to the number of text lines and
366    /// the line-width for the given line.
367    ///
368    /// Returns true, if the cursor actually changed.
369    pub fn set_cursor(&mut self, cursor: upos_type, extend_selection: bool) -> bool {
370        self.masked
371            .set_cursor(TextPosition::new(cursor, 0), extend_selection)
372    }
373
374    // find default cursor position for a number range
375    fn number_cursor(&self, range: Range<upos_type>) -> upos_type {
376        for (i, t) in self.mask[range.start as usize..range.end as usize]
377            .iter()
378            .enumerate()
379            .rev()
380        {
381            match t.right {
382                Mask::Digit(EditDirection::Rtol)
383                | Mask::Digit0(EditDirection::Rtol)
384                | Mask::Numeric(EditDirection::Rtol) => {
385                    return range.start + i as upos_type + 1;
386                }
387                _ => {}
388            }
389        }
390        range.start
391    }
392
393    /// Get the default cursor for the section at the given cursor position,
394    /// if it is an editable section.
395    pub fn section_cursor(&self, cursor: upos_type) -> Option<upos_type> {
396        if cursor as usize >= self.mask.len() {
397            return None;
398        }
399
400        let mask = &self.mask[cursor as usize];
401
402        if mask.right.is_number() {
403            Some(self.number_cursor(mask.sec_start..mask.sec_end))
404        } else if mask.right.is_separator() {
405            None
406        } else if mask.right.is_none() {
407            None
408        } else {
409            Some(mask.sec_start)
410        }
411    }
412
413    /// Get the default cursor position for the next editable section.
414    pub fn next_section_cursor(&self, cursor: upos_type) -> Option<upos_type> {
415        if cursor as usize >= self.mask.len() {
416            return None;
417        }
418
419        let mut mask = &self.mask[cursor as usize];
420        let mut next;
421        loop {
422            if mask.right.is_none() {
423                return None;
424            }
425
426            next = mask.sec_end;
427            mask = &self.mask[next as usize];
428
429            if mask.right.is_number() {
430                return Some(self.number_cursor(mask.sec_start..mask.sec_end));
431            } else if mask.right.is_separator() {
432                continue;
433            } else if mask.right.is_none() {
434                return None;
435            } else {
436                return Some(mask.sec_start);
437            }
438        }
439    }
440
441    /// Get the default cursor position for the next editable section.
442    pub fn prev_section_cursor(&self, cursor: upos_type) -> Option<upos_type> {
443        if cursor as usize >= self.mask.len() {
444            return None;
445        }
446
447        let mut prev = self.mask[cursor as usize].sec_start;
448        let mut mask = &self.mask[prev as usize];
449
450        loop {
451            if mask.peek_left.is_none() {
452                return None;
453            }
454
455            prev = self.mask[mask.sec_start as usize - 1].sec_start;
456            mask = &self.mask[prev as usize];
457
458            if mask.right.is_number() {
459                return Some(self.number_cursor(mask.sec_start..mask.sec_end));
460            } else if mask.right.is_separator() {
461                continue;
462            } else {
463                return Some(mask.sec_start);
464            }
465        }
466    }
467
468    /// Is the position at a word boundary?
469    pub fn is_section_boundary(&self, pos: upos_type) -> bool {
470        if pos == 0 {
471            return false;
472        }
473        if pos as usize >= self.mask.len() {
474            return false;
475        }
476        let prev = &self.mask[pos as usize - 1];
477        let mask = &self.mask[pos as usize];
478        prev.sec_id != mask.sec_id
479    }
480
481    /// Get the range for the section at the given cursor position,
482    /// if it is an editable section.
483    pub fn section_range(&self, cursor: upos_type) -> Option<Range<upos_type>> {
484        if cursor as usize >= self.mask.len() {
485            return None;
486        }
487
488        let mask = &self.mask[cursor as usize];
489        if mask.right.is_number() {
490            Some(mask.sec_start..mask.sec_end)
491        } else if mask.right.is_separator() {
492            None
493        } else if mask.right.is_none() {
494            None
495        } else {
496            Some(mask.sec_start..mask.sec_end)
497        }
498    }
499
500    /// Get the default cursor position for the next editable section.
501    pub fn next_section_range(&self, cursor: upos_type) -> Option<Range<upos_type>> {
502        if cursor as usize >= self.mask.len() {
503            return None;
504        }
505
506        let mut mask = &self.mask[cursor as usize];
507        let mut next;
508        loop {
509            if mask.right.is_none() {
510                return None;
511            }
512
513            next = mask.sec_end;
514            mask = &self.mask[next as usize];
515
516            if mask.right.is_number() {
517                return Some(mask.sec_start..mask.sec_end);
518            } else if mask.right.is_separator() {
519                continue;
520            } else if mask.right.is_none() {
521                return None;
522            } else {
523                return Some(mask.sec_start..mask.sec_end);
524            }
525        }
526    }
527
528    /// Get the default cursor position for the next editable section.
529    pub fn prev_section_range(&self, cursor: upos_type) -> Option<Range<upos_type>> {
530        if cursor as usize >= self.mask.len() {
531            return None;
532        }
533
534        let mut prev = self.mask[cursor as usize].sec_start;
535        let mut mask = &self.mask[prev as usize];
536        loop {
537            if mask.peek_left.is_none() {
538                return None;
539            }
540
541            prev = self.mask[mask.sec_start as usize - 1].sec_start;
542            mask = &self.mask[prev as usize];
543
544            if mask.right.is_number() {
545                return Some(mask.sec_start..mask.sec_end);
546            } else if mask.right.is_separator() {
547                continue;
548            } else {
549                return Some(mask.sec_start..mask.sec_end);
550            }
551        }
552    }
553
554    /// Place cursor at decimal separator, if any.
555    /// 0 otherwise.
556    #[inline]
557    pub fn set_default_cursor(&mut self) {
558        if let Some(pos) = self.section_cursor(0) {
559            self.masked.set_cursor(TextPosition::new(pos, 0), false);
560        } else if let Some(pos) = self.next_section_cursor(0) {
561            self.masked.set_cursor(TextPosition::new(pos, 0), false);
562        } else {
563            self.masked.set_cursor(TextPosition::new(0, 0), false);
564        }
565    }
566
567    /// Cursor position as grapheme-idx.
568    #[inline]
569    pub fn cursor(&self) -> upos_type {
570        self.masked.cursor().x
571    }
572
573    /// Selection anchor
574    #[inline]
575    pub fn anchor(&self) -> upos_type {
576        self.masked.anchor().x
577    }
578
579    /// Any text selection.
580    #[inline]
581    pub fn has_selection(&self) -> bool {
582        self.masked.has_selection()
583    }
584
585    /// Select text.
586    #[inline]
587    pub fn set_selection(&mut self, anchor: upos_type, cursor: upos_type) -> bool {
588        self.masked
589            .set_selection(TextPosition::new(anchor, 0), TextPosition::new(cursor, 0))
590    }
591
592    /// Select all text.
593    #[inline]
594    pub fn select_all(&mut self) -> bool {
595        self.masked.select_all()
596    }
597
598    /// Returns the selection as TextRange.
599    #[inline]
600    pub fn selection(&self) -> Range<upos_type> {
601        let mut v = self.masked.selection();
602        if v.start == TextPosition::new(0, 1) {
603            v.start = TextPosition::new(self.line_width(), 0);
604        }
605        if v.end == TextPosition::new(0, 1) {
606            v.end = TextPosition::new(self.line_width(), 0);
607        }
608        v.start.x..v.end.x
609    }
610
611    /// Selection.
612    #[inline]
613    pub fn selected_text(&self) -> &str {
614        match self
615            .masked
616            .str_slice(self.masked.selection())
617            .expect("valid_range")
618        {
619            Cow::Borrowed(v) => v,
620            Cow::Owned(_) => {
621                unreachable!()
622            }
623        }
624    }
625}
626
627impl MaskedCore {
628    /// Empty.
629    #[inline]
630    pub fn is_empty(&self) -> bool {
631        self.masked.text().as_str() == self.default_value()
632    }
633
634    /// Grapheme position to byte position.
635    /// This is the (start,end) position of the single grapheme after pos.
636    #[inline]
637    pub fn byte_at(&self, pos: upos_type) -> Result<Range<usize>, TextError> {
638        self.masked.byte_at(TextPosition::new(pos, 0))
639    }
640
641    /// Grapheme range to byte range.
642    #[inline]
643    pub fn bytes_at_range(&self, range: Range<upos_type>) -> Result<Range<usize>, TextError> {
644        self.masked
645            .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))
646    }
647
648    /// Byte position to grapheme position.
649    /// Returns the position that contains the given byte index.
650    #[inline]
651    pub fn byte_pos(&self, byte: usize) -> Result<upos_type, TextError> {
652        Ok(self.masked.byte_pos(byte)?.x)
653    }
654
655    /// Byte range to grapheme range.
656    #[inline]
657    pub fn byte_range(&self, bytes: Range<usize>) -> Result<Range<upos_type>, TextError> {
658        let r = self.masked.byte_range(bytes)?;
659        Ok(r.start.x..r.end.x)
660    }
661
662    /// Text slice as `Cow<str>`. Uses a byte range.
663    #[inline]
664    pub fn str_slice_byte(&self, range: Range<usize>) -> Result<Cow<'_, str>, TextError> {
665        self.masked.str_slice_byte(range)
666    }
667
668    /// A range of the text as `Cow<str>`
669    #[inline]
670    pub fn str_slice(&self, range: Range<upos_type>) -> Result<Cow<'_, str>, TextError> {
671        self.masked
672            .str_slice(TextRange::new((range.start, 0), (range.end, 0)))
673    }
674
675    /// Iterator for the glyphs of the lines in range.
676    /// Glyphs here a grapheme + display length.
677    #[inline]
678    #[deprecated(since = "1.1.0", note = "discontinued api")]
679    #[allow(deprecated)]
680    pub fn glyphs(
681        &self,
682        rows: Range<upos_type>,
683        screen_offset: u16,
684        screen_width: u16,
685    ) -> Result<impl Iterator<Item = Glyph<'_>>, TextError> {
686        let grapheme_iter = self.masked.graphemes(
687            TextRange::new((0, rows.start), (0, rows.end)),
688            TextPosition::new(0, rows.start),
689        )?;
690
691        let mask_iter = self.mask.iter();
692
693        let sym_neg = || self.neg_sym().to_string();
694        let sym_dec = || self.dec_sep().to_string();
695        let sym_grp = || self.grp_sep().to_string();
696        let sym_pos = || self.pos_sym().to_string();
697
698        let iter = grapheme_iter
699            .zip(mask_iter)
700            .map(move |(g, t)| match (&t.right, g.grapheme()) {
701                (Mask::Numeric(_), "-") => Grapheme::new(Cow::Owned(sym_neg()), g.text_bytes()),
702                (Mask::DecimalSep, ".") => Grapheme::new(Cow::Owned(sym_dec()), g.text_bytes()),
703                (Mask::GroupingSep, ",") => Grapheme::new(Cow::Owned(sym_grp()), g.text_bytes()),
704                (Mask::GroupingSep, "-") => Grapheme::new(Cow::Owned(sym_neg()), g.text_bytes()),
705                (Mask::Sign, "-") => Grapheme::new(Cow::Owned(sym_neg()), g.text_bytes()),
706                (Mask::Sign, _) => Grapheme::new(Cow::Owned(sym_pos()), g.text_bytes()),
707                (_, _) => g,
708            });
709
710        let mut it = GlyphIter::new(TextPosition::new(0, rows.start), iter);
711        it.set_screen_offset(screen_offset);
712        it.set_screen_width(screen_width);
713        it.set_tabs(self.masked.tab_width());
714        it.set_show_ctrl(self.masked.glyph_ctrl());
715        it.set_line_break(self.masked.glyph_line_break());
716        Ok(it)
717    }
718
719    /// Iterator for the glyphs of the lines in range.
720    /// Glyphs here a grapheme + display length.
721    ///
722    /// This omits unnecessary white-space.
723    #[inline]
724    #[deprecated(since = "1.1.0", note = "discontinued api")]
725    #[allow(deprecated)]
726    pub fn condensed_glyphs(
727        &self,
728        rows: Range<upos_type>,
729        screen_offset: u16,
730        screen_width: u16,
731    ) -> Result<impl Iterator<Item = Glyph<'_>>, TextError> {
732        let grapheme_iter = self.masked.graphemes(
733            TextRange::new((0, rows.start), (0, rows.end)),
734            TextPosition::new(0, rows.start),
735        )?;
736
737        let mask_iter = self.mask.iter();
738
739        let sym_neg = || self.neg_sym().to_string();
740        let sym_dec = || self.dec_sep().to_string();
741        let sym_grp = || self.grp_sep().to_string();
742        let sym_pos = || self.pos_sym().to_string();
743
744        let iter =
745            grapheme_iter
746                .zip(mask_iter)
747                .filter_map(move |(g, t)| match (&t.right, g.grapheme()) {
748                    (Mask::Numeric(_), "-") => {
749                        Some(Grapheme::new(Cow::Owned(sym_neg()), g.text_bytes()))
750                    }
751                    (Mask::DecimalSep, ".") => {
752                        Some(Grapheme::new(Cow::Owned(sym_dec()), g.text_bytes()))
753                    }
754                    (Mask::GroupingSep, ",") => {
755                        Some(Grapheme::new(Cow::Owned(sym_grp()), g.text_bytes()))
756                    }
757                    (Mask::GroupingSep, "-") => {
758                        Some(Grapheme::new(Cow::Owned(sym_neg()), g.text_bytes()))
759                    }
760                    (Mask::Sign, "-") => Some(Grapheme::new(Cow::Owned(sym_neg()), g.text_bytes())),
761
762                    (Mask::Numeric(_), " ") => None,
763                    (Mask::Digit(_), " ") => None,
764                    (Mask::DecimalSep, " ") => None,
765                    (Mask::GroupingSep, " ") => None,
766                    (Mask::Sign, _) => {
767                        if self.pos_sym() != ' ' {
768                            Some(Grapheme::new(Cow::Owned(sym_pos()), g.text_bytes()))
769                        } else {
770                            None
771                        }
772                    }
773                    (Mask::Hex, " ") => None,
774                    (Mask::Oct, " ") => None,
775                    (Mask::Dec, " ") => None,
776
777                    (_, _) => Some(g),
778                });
779
780        let mut it = GlyphIter::new(TextPosition::new(0, rows.start), iter);
781        it.set_screen_offset(screen_offset);
782        it.set_screen_width(screen_width);
783        it.set_tabs(self.masked.tab_width());
784        it.set_show_ctrl(self.masked.glyph_ctrl());
785        it.set_line_break(self.masked.glyph_line_break());
786        Ok(it)
787    }
788
789    /// Iterator for the glyphs of the lines in range.
790    /// Glyphs here a grapheme + display length.
791    ///
792    /// * text_break: only TextBreak2::ShiftText has been tested
793    /// * condensed: skip unnecessary white-space
794    #[inline]
795    pub(crate) fn glyphs2(
796        &self,
797        left_margin: upos_type,
798        right_margin: upos_type,
799        compact: bool,
800    ) -> Result<Box<dyn Iterator<Item = Glyph2<'_>> + '_>, TextError> {
801        let grapheme_iter = self
802            .masked
803            .graphemes(TextRange::new((0, 0), (0, 1)), TextPosition::new(0, 0))?;
804        let mask_iter = self.mask.iter();
805
806        let iter = MaskedGraphemes {
807            iter_str: grapheme_iter,
808            iter_mask: mask_iter,
809            compact,
810            sym_neg: self.neg_sym().to_string(),
811            sym_dec: self.dec_sep().to_string(),
812            sym_grp: self.grp_sep().to_string(),
813            sym_pos: self.pos_sym().to_string(),
814            byte_pos: 0,
815        };
816
817        let mut it = GlyphIter2::new(TextPosition::new(0, 0), 0, iter, Default::default());
818        it.set_tabs(self.masked.tab_width() as upos_type);
819        it.set_show_ctrl(self.masked.glyph_ctrl());
820        it.set_lf_breaks(self.masked.glyph_line_break());
821        it.set_text_wrap(TextWrap2::Shift);
822        it.set_left_margin(left_margin);
823        it.set_right_margin(right_margin);
824        it.set_word_margin(right_margin);
825        it.prepare()?;
826        Ok(Box::new(it))
827    }
828
829    /// Get the grapheme at the given position.
830    #[inline]
831    pub fn grapheme_at(&self, pos: upos_type) -> Result<Option<Grapheme<'_>>, TextError> {
832        self.masked.grapheme_at(TextPosition::new(pos, 0))
833    }
834
835    /// Get a cursor over all the text with the current position set at pos.
836    #[inline]
837    pub fn text_graphemes(
838        &self,
839        pos: upos_type,
840    ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
841        self.masked.text_graphemes(TextPosition::new(pos, 0))
842    }
843
844    /// Get a cursor over the text-range the current position set at pos.
845    #[inline]
846    pub fn graphemes(
847        &self,
848        range: Range<upos_type>,
849        pos: upos_type,
850    ) -> Result<impl Cursor<Item = Grapheme<'_>>, TextError> {
851        self.masked.graphemes(
852            TextRange::new((range.start, 0), (range.end, 0)),
853            TextPosition::new(pos, 0),
854        )
855    }
856
857    #[inline]
858    pub fn line_width(&self) -> upos_type {
859        self.masked.line_width(0).expect("valid_row")
860    }
861}
862
863#[derive(Debug, Clone)]
864struct MaskedGraphemes<'a> {
865    iter_str: StrGraphemes<'a>,
866    iter_mask: slice::Iter<'a, MaskToken>,
867
868    compact: bool,
869    sym_neg: String,
870    sym_dec: String,
871    sym_grp: String,
872    sym_pos: String,
873
874    byte_pos: usize,
875}
876
877impl<'a> Iterator for MaskedGraphemes<'a> {
878    type Item = Grapheme<'a>;
879
880    fn next(&mut self) -> Option<Self::Item> {
881        loop {
882            let g = self.iter_str.next();
883            let t = self.iter_mask.next();
884
885            let (Some(g), Some(t)) = (g, t) else {
886                return None;
887            };
888
889            self.byte_pos = g.text_bytes().end;
890
891            let r = match (self.compact, &t.right, g.grapheme()) {
892                (true, Mask::Numeric(_), "-") => Some(Grapheme::new(
893                    Cow::Owned(self.sym_neg.clone()),
894                    g.text_bytes(),
895                )),
896                (true, Mask::DecimalSep, ".") => Some(Grapheme::new(
897                    Cow::Owned(self.sym_dec.clone()),
898                    g.text_bytes(),
899                )),
900                (true, Mask::GroupingSep, ",") => Some(Grapheme::new(
901                    Cow::Owned(self.sym_grp.clone()),
902                    g.text_bytes(),
903                )),
904                (true, Mask::GroupingSep, "-") => Some(Grapheme::new(
905                    Cow::Owned(self.sym_neg.clone()),
906                    g.text_bytes(),
907                )),
908                (true, Mask::Sign, "-") => Some(Grapheme::new(
909                    Cow::Owned(self.sym_neg.clone()),
910                    g.text_bytes(),
911                )),
912
913                (true, Mask::Numeric(_), " ") => None,
914                (true, Mask::Digit(_), " ") => None,
915                (true, Mask::DecimalSep, " ") => None,
916                (true, Mask::GroupingSep, " ") => None,
917                (true, Mask::Sign, _) => {
918                    if self.sym_pos != " " {
919                        Some(Grapheme::new(
920                            Cow::Owned(self.sym_pos.clone()),
921                            g.text_bytes(),
922                        ))
923                    } else {
924                        None
925                    }
926                }
927                (true, Mask::Hex, " ") => None,
928                (true, Mask::Oct, " ") => None,
929                (true, Mask::Dec, " ") => None,
930
931                (false, Mask::Numeric(_), "-") => Some(Grapheme::new(
932                    Cow::Owned(self.sym_neg.clone()),
933                    g.text_bytes(),
934                )),
935                (false, Mask::DecimalSep, ".") => Some(Grapheme::new(
936                    Cow::Owned(self.sym_dec.clone()),
937                    g.text_bytes(),
938                )),
939                (false, Mask::GroupingSep, ",") => Some(Grapheme::new(
940                    Cow::Owned(self.sym_grp.clone()),
941                    g.text_bytes(),
942                )),
943                (false, Mask::GroupingSep, "-") => Some(Grapheme::new(
944                    Cow::Owned(self.sym_neg.clone()),
945                    g.text_bytes(),
946                )),
947                (false, Mask::Sign, "-") => Some(Grapheme::new(
948                    Cow::Owned(self.sym_neg.clone()),
949                    g.text_bytes(),
950                )),
951                (false, Mask::Sign, _) => Some(Grapheme::new(
952                    Cow::Owned(self.sym_pos.clone()),
953                    g.text_bytes(),
954                )),
955
956                (_, _, _) => Some(g),
957            };
958            if r.is_some() {
959                break r;
960            }
961        }
962    }
963}
964
965impl<'a> SkipLine for MaskedGraphemes<'a> {
966    fn skip_line(&mut self) -> Result<(), TextError> {
967        // all in one line, eat the rest.
968        for _ in self.iter_str.by_ref() {}
969        for _ in self.iter_mask.by_ref() {}
970        Ok(())
971    }
972
973    fn skip_to(&mut self, byte_pos: usize) -> Result<(), TextError> {
974        if byte_pos > self.byte_pos {
975            Err(TextError::ByteIndexOutOfBounds(byte_pos, self.byte_pos))
976        } else if byte_pos == self.byte_pos {
977            Ok(())
978        } else {
979            for g in self.iter_str.by_ref() {
980                _ = self.iter_mask.next();
981
982                if byte_pos == g.text_bytes().end {
983                    return Ok(());
984                } else if byte_pos < g.text_bytes().end {
985                    return Err(TextError::ByteIndexNotCharBoundary(byte_pos));
986                }
987            }
988            Err(TextError::ByteIndexOutOfBounds(byte_pos, byte_pos))
989        }
990    }
991}
992
993impl MaskedCore {
994    /// Create a default value according to the mask.
995    #[inline]
996    fn default_value(&self) -> String {
997        MaskToken::empty_section(&self.mask)
998    }
999}
1000
1001impl MaskedCore {
1002    /// Reset value but not the mask and width.
1003    /// Resets offset and cursor position too.
1004    #[inline]
1005    pub fn clear(&mut self) {
1006        self.masked
1007            .set_text(TextString::new_string(self.default_value()));
1008        self.set_default_cursor();
1009    }
1010
1011    /// Copy of the text-value.
1012    pub fn text(&self) -> &str {
1013        self.masked.text().as_str()
1014    }
1015
1016    /// Sets the value.
1017    /// No checks if the value conforms to the mask.
1018    /// If the value is too short it will be filled with space.
1019    /// if the value is too long it will be truncated.
1020    #[allow(clippy::comparison_chain)]
1021    pub fn set_text<S: Into<String>>(&mut self, s: S) {
1022        let mut text = s.into();
1023        while text.graphemes(true).count() > self.mask.len().saturating_sub(1) {
1024            text.pop();
1025        }
1026        while text.graphemes(true).count() < self.mask.len().saturating_sub(1) {
1027            text.push(' ');
1028        }
1029        let len = text.graphemes(true).count();
1030
1031        assert_eq!(len, self.mask.len().saturating_sub(1));
1032
1033        self.masked.set_text(TextString::new_string(text));
1034    }
1035
1036    /// Start at the cursor position and find a valid insert position for the input c.
1037    /// Put the cursor at that position.
1038    #[allow(clippy::if_same_then_else)]
1039    pub fn advance_cursor(&mut self, c: char) -> bool {
1040        if self.mask.is_empty() {
1041            return false;
1042        }
1043
1044        let mask_c = &self.mask[self.masked.cursor().x as usize];
1045
1046        let mut new_cursor = self.masked.cursor().x;
1047
1048        loop {
1049            let mask = &self.mask[new_cursor as usize];
1050
1051            if self.can_insert_integer_left(mask, new_cursor, c) {
1052                // At the gap between an integer field and something else.
1053                // Integer fields are served first.
1054                break;
1055            } else if self.can_insert_integer(mask, new_cursor, c) {
1056                // Insert position inside an integer field. After any spaces
1057                // and the sign.
1058                break;
1059            } else if self.can_insert_sign(mask, new_cursor, c) {
1060                // Can insert a sign here.
1061                break;
1062            } else if self.can_insert_decimal_sep(mask, c) {
1063                // Decimal separator matches.
1064                break;
1065            } else if mask.right == Mask::GroupingSep {
1066                // Never stop here.
1067                new_cursor += 1;
1068            } else if self.can_insert_separator(mask, c) {
1069                break;
1070            } else if self.can_move_left_in_fraction(mask_c, mask, new_cursor, c) {
1071                // skip left
1072                new_cursor -= 1;
1073            } else if self.can_insert_fraction(mask_c, mask, c) {
1074                break;
1075            } else if self.can_insert_other(mask, c) {
1076                break;
1077            } else if mask.right == Mask::None {
1078                // No better position found. Reset and break;
1079                new_cursor = self.masked.cursor().x;
1080                break;
1081            } else {
1082                new_cursor += 1;
1083            }
1084        }
1085
1086        self.masked
1087            .set_cursor(TextPosition::new(new_cursor, 0), false)
1088    }
1089
1090    /// Valid input for this mask.
1091    fn is_valid_char(&self, mask: &Mask, c: char) -> bool {
1092        match mask {
1093            Mask::Digit0(_) => c.is_ascii_digit(),
1094            Mask::Digit(_) => c.is_ascii_digit() || c == ' ',
1095            Mask::Numeric(_) => c.is_ascii_digit() || c == self.neg_sym() || c == '-',
1096            Mask::DecimalSep => c == self.dec_sep(),
1097            Mask::GroupingSep => false,
1098            Mask::Sign => c == self.neg_sym() || c == '-',
1099            Mask::Plus => c == self.neg_sym() || c == '-',
1100            Mask::Hex0 => c.is_ascii_hexdigit(),
1101            Mask::Hex => c.is_ascii_hexdigit() || c == ' ',
1102            Mask::Oct0 => c.is_digit(8),
1103            Mask::Oct => c.is_digit(8) || c == ' ',
1104            Mask::Dec0 => c.is_ascii_digit(),
1105            Mask::Dec => c.is_ascii_digit() || c == ' ',
1106            Mask::Letter => c.is_alphabetic(),
1107            Mask::LetterOrDigit => c.is_alphanumeric(),
1108            Mask::LetterDigitSpace => c.is_alphanumeric() || c == ' ',
1109            Mask::AnyChar => true,
1110            Mask::Separator(sep) => {
1111                // ',' and '.' match any separator.
1112                if c == '.' || c == ',' {
1113                    true
1114                } else if let Some(sepc) = sep.chars().next() {
1115                    // todo: don't know better
1116                    sepc == c
1117                } else {
1118                    false
1119                }
1120            }
1121            Mask::None => false,
1122        }
1123    }
1124
1125    // Can insert other field types
1126    #[inline]
1127    fn can_insert_other(&self, mask: &MaskToken, c: char) -> bool {
1128        match mask.right {
1129            Mask::Hex0
1130            | Mask::Hex
1131            | Mask::Oct0
1132            | Mask::Oct
1133            | Mask::Dec0
1134            | Mask::Dec
1135            | Mask::Letter
1136            | Mask::LetterOrDigit
1137            | Mask::LetterDigitSpace
1138            | Mask::AnyChar => self.is_valid_char(&mask.right, c),
1139            _ => false,
1140        }
1141    }
1142
1143    // Can insert fraction.
1144    #[inline]
1145    fn can_insert_fraction(&self, mask_c: &MaskToken, mask: &MaskToken, c: char) -> bool {
1146        if !mask.right.is_fraction() {
1147            return false;
1148        }
1149        if !self.is_valid_char(&mask.right, c) {
1150            return false;
1151        }
1152        // don't jump from integer to fraction
1153        if mask_c.is_integer_part() {
1154            return false;
1155        }
1156
1157        true
1158    }
1159
1160    // When inserting to the fraction we want to left-align
1161    // the digits. This checks if a digit could possibly be
1162    // inserted to the left of the current position.
1163    #[inline]
1164    fn can_move_left_in_fraction(
1165        &self,
1166        mask_c: &MaskToken,
1167        mask: &MaskToken,
1168        new_cursor: upos_type,
1169        c: char,
1170    ) -> bool {
1171        if !mask.peek_left.is_fraction() {
1172            return false;
1173        }
1174        if !self.is_valid_char(&mask.peek_left, c) {
1175            return false;
1176        }
1177        // don't jump from integer to fraction
1178        if mask_c.is_integer_part() {
1179            return false;
1180        }
1181
1182        let gl = self
1183            .masked
1184            .grapheme_at(TextPosition::new(new_cursor - 1, 0))
1185            .expect("valid_position")
1186            .expect("grapheme");
1187
1188        // is there space to the left?
1189        if gl != " " {
1190            return false;
1191        }
1192
1193        true
1194    }
1195
1196    // Can input a sign here?
1197    #[inline]
1198    fn can_insert_sign<'a>(
1199        &'a self,
1200        mut mask: &'a MaskToken,
1201        new_cursor: upos_type,
1202        c: char,
1203    ) -> bool {
1204        if !self.is_valid_char(&Mask::Sign, c) {
1205            return false;
1206        }
1207        // boundary right/left. prefer right, change mask.
1208        if mask.peek_left.is_number() && (mask.right.is_ltor() || mask.right.is_none()) {
1209            mask = &self.mask[new_cursor as usize - 1];
1210        }
1211        if !mask.right.is_number() {
1212            return false;
1213        }
1214
1215        // check possible positions for the sign.
1216        for i in mask.sec_start..mask.sec_end {
1217            let t = &self.mask[i as usize];
1218            match t.right {
1219                Mask::Plus => return true,
1220                Mask::Sign => return true,
1221                Mask::Numeric(EditDirection::Rtol) => {
1222                    // Numeric fields can hold a sign.
1223                    // If they are not otherwise occupied.
1224                    let gi = self
1225                        .masked
1226                        .grapheme_at(TextPosition::new(i, 0))
1227                        .expect("valid_position")
1228                        .expect("grapheme");
1229
1230                    return t.right.can_drop(gi.grapheme()) || gi == "-";
1231                }
1232                _ => {}
1233            }
1234        }
1235
1236        false
1237    }
1238
1239    // Is this the correct input position for a rtol field
1240    #[inline]
1241    fn can_insert_integer(&self, mask: &MaskToken, new_cursor: upos_type, c: char) -> bool {
1242        if !mask.right.is_rtol() {
1243            return false;
1244        }
1245
1246        if !self.is_valid_char(&mask.right, c) {
1247            return false;
1248        }
1249
1250        let g = self
1251            .masked
1252            .grapheme_at(TextPosition::new(new_cursor, 0))
1253            .expect("valid_position")
1254            .expect("grapheme");
1255        if mask.right.can_drop(g.grapheme()) {
1256            return false;
1257        }
1258        if g == "-" {
1259            return false;
1260        }
1261
1262        true
1263    }
1264
1265    // Separator char matches
1266    #[inline]
1267    fn can_insert_separator(&self, mask: &MaskToken, c: char) -> bool {
1268        if !matches!(mask.right, Mask::Separator(_)) {
1269            return false;
1270        }
1271        if !self.is_valid_char(&mask.right, c) {
1272            return false;
1273        }
1274        true
1275    }
1276
1277    // Can insert a decimal separator.
1278    #[inline]
1279    fn can_insert_decimal_sep(&self, mask: &MaskToken, c: char) -> bool {
1280        if mask.right != Mask::DecimalSep {
1281            return false;
1282        }
1283        if !self.is_valid_char(&mask.right, c) {
1284            return false;
1285        }
1286        true
1287    }
1288
1289    // Can edit the field left of the cursor.
1290    #[inline]
1291    fn can_insert_integer_left(&self, mask: &MaskToken, new_cursor: upos_type, c: char) -> bool {
1292        if !mask.peek_left.is_rtol() {
1293            return false;
1294        }
1295        if !mask.right.is_ltor() && !mask.right.is_none() {
1296            return false;
1297        }
1298
1299        let left = &self.mask[new_cursor as usize - 1];
1300        if !self.is_valid_char(&left.right, c) {
1301            return false;
1302        }
1303
1304        let mask0 = &self.mask[left.sub_start as usize];
1305        let g0 = self
1306            .masked
1307            .grapheme_at(TextPosition::new(left.sub_start, 0))
1308            .expect("valid_position")
1309            .expect("grapheme");
1310        if !mask0.right.can_drop(g0.grapheme()) {
1311            return false;
1312        }
1313
1314        true
1315    }
1316
1317    /// Insert the char if it matches the cursor mask and the current section is not full.
1318    ///
1319    /// `advance_cursor()` must be called before for correct functionality.
1320    ///
1321    /// Otherwise: your mileage might vary.
1322    pub fn insert_char(&mut self, c: char) -> bool {
1323        if self.mask.is_empty() {
1324            return false;
1325        }
1326
1327        let cursor = self.masked.cursor();
1328
1329        // note: because of borrow checker. calls &mut methods.
1330        {
1331            let mask = &self.mask[cursor.x as usize];
1332            if mask.right.is_number() && self.can_insert_sign(mask, cursor.x, c) {
1333                if self.insert_sign(c) {
1334                    return true;
1335                }
1336            }
1337        }
1338        {
1339            let mask = &self.mask[cursor.x as usize];
1340            if mask.peek_left.is_number() && (mask.right.is_ltor() || mask.right.is_none()) {
1341                let left = &self.mask[cursor.x as usize - 1];
1342                if self.can_insert_sign(left, cursor.x, c) {
1343                    if self.insert_sign(c) {
1344                        return true;
1345                    }
1346                }
1347            }
1348        }
1349        {
1350            let mask = &self.mask[cursor.x as usize];
1351            if mask.right.is_rtol() {
1352                if self.insert_rtol(c) {
1353                    return true;
1354                }
1355            }
1356        }
1357        {
1358            let mask = &self.mask[cursor.x as usize];
1359            if mask.peek_left.is_rtol() && (mask.right.is_ltor() || mask.right.is_none()) {
1360                if self.insert_rtol(c) {
1361                    return true;
1362                }
1363            }
1364        }
1365        {
1366            let mask = &self.mask[cursor.x as usize];
1367            if mask.right.is_ltor() {
1368                if self.insert_ltor(c) {
1369                    #[allow(clippy::needless_return)]
1370                    return true;
1371                }
1372            }
1373        }
1374
1375        false
1376    }
1377
1378    /// Insert c into a ltor section.
1379    fn insert_ltor(&mut self, c: char) -> bool {
1380        let cursor = self.masked.cursor();
1381
1382        let mask = &self.mask[cursor.x as usize];
1383        let mask9 = &self.mask[mask.sub_end as usize - 1];
1384
1385        // overwrite digit in fraction?
1386        let g = self
1387            .masked
1388            .grapheme_at(cursor)
1389            .expect("valid_cursor")
1390            .expect("mask");
1391        if mask.right.is_fraction()
1392            && mask.right.can_overwrite_fraction(g.grapheme())
1393            && self.is_valid_char(&mask.right, c)
1394        {
1395            // to the right only defaults
1396            let frac_mask = &self.mask[cursor.x as usize + 1..mask.sub_end as usize];
1397            let frac_str = self
1398                .masked
1399                .str_slice(TextRange::new((cursor.x + 1, 0), (mask.sub_end, 0)))
1400                .expect("valid_range");
1401            if frac_str == MaskToken::empty_section(frac_mask) {
1402                self.masked.begin_undo_seq();
1403                self.masked
1404                    .remove_char_range(TextRange::new(cursor, (cursor.x + 1, 0)))
1405                    .expect("valid_cursor");
1406                self.masked.insert_char(cursor, c).expect("valid_cursor");
1407                self.masked.end_undo_seq();
1408                return true;
1409            }
1410        }
1411
1412        let g = self
1413            .masked
1414            .grapheme_at(cursor)
1415            .expect("valid_cursor")
1416            .expect("mask");
1417        if mask.right.can_overwrite(g.grapheme()) && self.is_valid_char(&mask.right, c) {
1418            if mask.right.is_separator() {
1419                self.masked.begin_undo_seq();
1420                let r = if let Some(next) = self.next_section_cursor(cursor.x) {
1421                    self.masked.set_cursor(TextPosition::new(next, 0), false)
1422                } else {
1423                    self.masked
1424                        .set_cursor(TextPosition::new(self.line_width(), 0), false)
1425                };
1426                self.masked.end_undo_seq();
1427                return r;
1428            } else if mask.right == Mask::DecimalSep {
1429                self.masked.begin_undo_seq();
1430                self.masked
1431                    .set_cursor(TextPosition::new(cursor.x + 1, 0), false);
1432                self.masked.end_undo_seq();
1433                return true;
1434            } else {
1435                self.masked.begin_undo_seq();
1436                self.masked
1437                    .remove_char_range(TextRange::new(cursor, (cursor.x + 1, 0)))
1438                    .expect("valid_cursor");
1439                self.masked.insert_char(cursor, c).expect("valid_cursor");
1440                self.masked.end_undo_seq();
1441                return true;
1442            }
1443        }
1444
1445        // can shift right
1446        let g9 = self
1447            .masked
1448            .grapheme_at(TextPosition::new(mask.sub_end - 1, 0))
1449            .expect("valid_pos")
1450            .expect("mask");
1451        if mask9.right.can_drop(g9.grapheme()) && self.is_valid_char(&mask.right, c) {
1452            self.masked.begin_undo_seq();
1453            self.masked
1454                .remove_char_range(TextRange::new((mask.sub_end - 1, 0), (mask.sub_end, 0)))
1455                .expect("valid_range");
1456            self.masked.insert_char(cursor, c).expect("valid_cursor");
1457            self.masked.end_undo_seq();
1458            return true;
1459        }
1460        false
1461    }
1462
1463    /// Insert c into a rtol section
1464    fn insert_rtol(&mut self, c: char) -> bool {
1465        let cursor = self.masked.cursor();
1466
1467        let mut mask = &self.mask[cursor.x as usize];
1468
1469        // boundary right/left. prefer right, change mask.
1470        if mask.peek_left.is_rtol() && (mask.right.is_ltor() || mask.right.is_none()) {
1471            mask = &self.mask[cursor.x as usize - 1];
1472        }
1473
1474        let mask0 = &self.mask[mask.sub_start as usize];
1475
1476        let g0 = self
1477            .masked
1478            .grapheme_at(TextPosition::new(mask.sub_start, 0))
1479            .expect("valid_pos")
1480            .expect("grapheme");
1481        if mask0.right.can_drop(g0.grapheme()) && self.is_valid_char(&mask.right, c) {
1482            self.masked.begin_undo_seq();
1483            self.masked
1484                .remove_char_range(TextRange::new((mask.sub_start, 0), (mask.sub_start + 1, 0)))
1485                .expect("valid_position");
1486            self.masked
1487                .insert_char(TextPosition::new(cursor.x - 1, 0), c)
1488                .expect("valid_position");
1489            Self::reformat(&mut self.masked, &self.mask, mask.sub_start..mask.sub_end);
1490            self.masked.end_undo_seq();
1491            return true;
1492        }
1493
1494        false
1495    }
1496
1497    /// Insert a sign c into the current number section
1498    #[allow(clippy::single_match)]
1499    fn insert_sign(&mut self, c: char) -> bool {
1500        let cursor = self.masked.cursor();
1501
1502        let mut mask = &self.mask[cursor.x as usize];
1503        // boundary right/left. prefer right, change mask.
1504        if mask.peek_left.is_number() && (mask.right.is_ltor() || mask.right.is_none()) {
1505            mask = &self.mask[cursor.x as usize - 1];
1506        }
1507
1508        // explicit sign?
1509        let idx = self.mask[mask.sec_start as usize..mask.sec_end as usize]
1510            .iter()
1511            .enumerate()
1512            .find(|(_, t)| matches!(t.right, Mask::Sign | Mask::Plus))
1513            .map(|(i, _)| mask.sec_start as usize + i);
1514
1515        // existing sign somewhere?
1516        let idx = if idx.is_none() {
1517            self.masked
1518                .graphemes(
1519                    TextRange::new((mask.sec_start, 0), (mask.sec_end, 0)),
1520                    TextPosition::new(mask.sec_start, 0),
1521                )
1522                .expect("valid_range")
1523                .enumerate()
1524                .find(|(_, g)| *g == "-" || *g == "+")
1525                .map(|(i, _)| mask.sec_start as usize + i)
1526        } else {
1527            idx
1528        };
1529
1530        let idx = if idx.is_none() {
1531            // moving sign
1532            let mut idx = mask.sec_end - 1;
1533            'f: {
1534                while idx >= mask.sec_start {
1535                    if self.mask[idx as usize].right == Mask::Numeric(EditDirection::Rtol) {
1536                        let g = self
1537                            .grapheme_at(idx)
1538                            .expect("valid_position")
1539                            .expect("grapheme");
1540
1541                        if self.mask[idx as usize].right.can_drop(g.grapheme()) {
1542                            break 'f Some(idx as usize);
1543                        }
1544                    }
1545                    idx -= 1;
1546                }
1547                None
1548            }
1549        } else {
1550            idx
1551        };
1552
1553        if let Some(idx) = idx {
1554            let mask_sign = &self.mask[idx];
1555
1556            if c == self.neg_sym() || c == '-' {
1557                // negate current
1558                let g = self
1559                    .masked
1560                    .str_slice(TextRange::new(
1561                        (idx as upos_type, 0),
1562                        (idx as upos_type + 1, 0),
1563                    ))
1564                    .expect("valid_pos")
1565                    .to_string();
1566
1567                self.masked.begin_undo_seq();
1568                self.masked
1569                    .remove_char_range(TextRange::new(
1570                        (idx as upos_type, 0),
1571                        (idx as upos_type + 1, 0),
1572                    ))
1573                    .expect("valid_range");
1574
1575                let cc = match &mask_sign.right {
1576                    Mask::Numeric(_) | Mask::Sign => {
1577                        if g == "-" {
1578                            ' '
1579                        } else {
1580                            '-'
1581                        }
1582                    }
1583                    Mask::Plus => {
1584                        if g == "-" {
1585                            '+'
1586                        } else {
1587                            '-'
1588                        }
1589                    }
1590                    _ => unreachable!(),
1591                };
1592
1593                self.masked
1594                    .insert_char(TextPosition::new(idx as upos_type, 0), cc)
1595                    .expect("valid_range");
1596                self.set_cursor(cursor.x, false);
1597                self.masked.end_undo_seq();
1598                true
1599            } else {
1600                false
1601            }
1602        } else {
1603            false
1604        }
1605    }
1606
1607    /// Remove the previous char.
1608    pub fn remove_prev(&mut self) {
1609        let cursor = self.masked.cursor();
1610
1611        if cursor.x == 0 {
1612            return;
1613        }
1614
1615        let left = &self.mask[cursor.x as usize - 1];
1616
1617        if left.right.is_rtol() {
1618            // Check if the section is empty
1619            let sec_empty = if left.right.is_rtol() {
1620                let sec_str = self
1621                    .masked
1622                    .str_slice(TextRange::new((left.sub_start, 0), (left.sub_end, 0)))
1623                    .expect("valid_range");
1624                let sec_mask = &self.mask[left.sub_start as usize..left.sub_end as usize];
1625                sec_str == MaskToken::empty_section(sec_mask)
1626            } else {
1627                false
1628            };
1629
1630            let l0 = &self.mask[left.sub_start as usize];
1631
1632            self.masked.begin_undo_seq();
1633            self.masked
1634                .remove_char_range(TextRange::new((cursor.x - 1, 0), cursor))
1635                .expect("valid_range");
1636            self.masked
1637                .insert_str(TextPosition::new(left.sub_start, 0), &l0.edit)
1638                .expect("valid_position");
1639            Self::reformat(&mut self.masked, &self.mask, left.sub_start..left.sub_end);
1640
1641            // in a rtol field keep the cursor at the same position until the
1642            // whole section is empty. Only then put it at the beginning of the section
1643            // to continue left of the section.
1644            if sec_empty {
1645                self.masked
1646                    .set_cursor(TextPosition::new(left.sub_start, 0), false);
1647            } else {
1648                // cursor stays
1649            }
1650
1651            self.masked.end_undo_seq();
1652        } else if left.right.is_ltor() {
1653            let l9 = &self.mask[left.sub_end as usize - 1];
1654
1655            self.masked.begin_undo_seq();
1656            self.masked
1657                .remove_char_range(TextRange::new((cursor.x - 1, 0), cursor))
1658                .expect("valid_range");
1659            self.masked
1660                .insert_str(TextPosition::new(left.sub_end - 1, 0), &l9.edit)
1661                .expect("valid_position");
1662
1663            Self::reformat(&mut self.masked, &self.mask, left.sub_start..left.sub_end);
1664
1665            self.masked
1666                .set_cursor(TextPosition::new(cursor.x - 1, 0), false);
1667
1668            self.masked.end_undo_seq();
1669        }
1670    }
1671
1672    /// Remove the previous char.
1673    pub fn remove_next(&mut self) {
1674        let cursor = self.masked.cursor();
1675
1676        if cursor.x as usize == self.mask.len() - 1 {
1677            return;
1678        }
1679
1680        let right = &self.mask[cursor.x as usize];
1681
1682        // remove and fill with empty
1683        if right.right.is_rtol() {
1684            let l0 = &self.mask[right.sub_start as usize];
1685
1686            self.masked.begin_undo_seq();
1687            self.masked
1688                .remove_char_range(TextRange::new(cursor, (cursor.x + 1, 0)))
1689                .expect("valid_range");
1690            self.masked
1691                .insert_str(TextPosition::new(right.sub_start, 0), &l0.edit)
1692                .expect("valid_position");
1693            Self::reformat(&mut self.masked, &self.mask, right.sub_start..right.sub_end);
1694
1695            self.masked
1696                .set_cursor(TextPosition::new(cursor.x + 1, 0), false);
1697
1698            self.masked.end_undo_seq();
1699        } else if right.right.is_ltor() {
1700            // Check if the section is empty
1701            let sec_str = self
1702                .masked
1703                .str_slice(TextRange::new((right.sub_start, 0), (right.sub_end, 0)))
1704                .expect("valid_range");
1705            let sec_mask = &self.mask[right.sub_start as usize..right.sub_end as usize];
1706            let sec_empty = sec_str == MaskToken::empty_section(sec_mask);
1707
1708            let l9 = &self.mask[right.sub_end as usize - 1];
1709
1710            self.masked.begin_undo_seq();
1711            self.masked
1712                .remove_char_range(TextRange::new(cursor, (cursor.x + 1, 0)))
1713                .expect("valid_range");
1714            self.masked
1715                .insert_str(TextPosition::new(right.sub_end - 1, 0), &l9.edit)
1716                .expect("valid_position");
1717
1718            Self::reformat(&mut self.masked, &self.mask, right.sub_start..right.sub_end);
1719
1720            // in a ltor field keep the cursor at the same position until the
1721            // whole section is empty. Only then put it at the end of the section
1722            // to continue right of the section.
1723            if sec_empty {
1724                self.masked
1725                    .set_cursor(TextPosition::new(right.sub_end, 0), false);
1726            } else {
1727                // cursor stays
1728            }
1729
1730            self.masked.end_undo_seq();
1731        }
1732    }
1733
1734    /// Remove the selection
1735    pub fn remove_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
1736        // check valid range
1737        self.masked
1738            .bytes_at_range(TextRange::new((range.start, 0), (range.end, 0)))?;
1739
1740        if range.is_empty() {
1741            return Ok(false);
1742        }
1743
1744        let mask = &self.mask[range.start as usize];
1745        if range.start >= mask.sub_start && range.end <= mask.sub_end {
1746            if mask.right.is_rtol() {
1747                self.masked.begin_undo_seq();
1748                self.masked
1749                    .remove_str_range(TextRange::new((range.start, 0), (range.end, 0)))
1750                    .expect("valid_range");
1751                let fill_before =
1752                    &self.mask[mask.sub_start as usize..mask.sub_start as usize + range.len()];
1753                self.masked
1754                    .insert_str(
1755                        TextPosition::new(mask.sub_start, 0),
1756                        &MaskToken::empty_section(fill_before),
1757                    )
1758                    .expect("valid_range");
1759                Self::reformat(&mut self.masked, &self.mask, mask.sub_start..mask.sub_end);
1760                self.masked.end_undo_seq();
1761            } else if mask.right.is_ltor() {
1762                self.masked.begin_undo_seq();
1763                self.masked
1764                    .remove_str_range(TextRange::new((range.start, 0), (range.end, 0)))
1765                    .expect("valid_range");
1766                let fill_after =
1767                    &self.mask[mask.sub_end as usize - range.len()..mask.sub_end as usize];
1768                self.masked
1769                    .insert_str(
1770                        TextPosition::new(mask.sub_end - range.len() as upos_type, 0),
1771                        &MaskToken::empty_section(fill_after),
1772                    )
1773                    .expect("valid_range");
1774                Self::reformat(&mut self.masked, &self.mask, mask.sub_start..mask.sub_end);
1775                self.masked.end_undo_seq();
1776            }
1777
1778            return Ok(true);
1779        }
1780
1781        let mut pos = range.start;
1782        self.masked.begin_undo_seq();
1783        loop {
1784            let mask = &self.mask[pos as usize];
1785
1786            if mask.sub_start < range.start {
1787                // partial start
1788                if mask.right.is_rtol() {
1789                    self.masked
1790                        .remove_str_range(TextRange::new((range.start, 0), (mask.sub_end, 0)))
1791                        .expect("valid_range");
1792
1793                    let len = mask.sub_end - range.start;
1794                    let fill_before =
1795                        &self.mask[mask.sub_start as usize..(mask.sub_start + len) as usize];
1796                    self.masked
1797                        .insert_str(
1798                            TextPosition::new(mask.sub_start, 0),
1799                            &MaskToken::empty_section(fill_before),
1800                        )
1801                        .expect("valid_range");
1802
1803                    Self::reformat(&mut self.masked, &self.mask, mask.sub_start..mask.sub_end);
1804
1805                    pos = mask.sub_end;
1806                } else if mask.right.is_ltor() {
1807                    self.masked
1808                        .remove_str_range(TextRange::new((range.start, 0), (mask.sub_end, 0)))
1809                        .expect("valid_range");
1810
1811                    let fill_after = &self.mask[range.start as usize..mask.sub_end as usize];
1812                    self.masked
1813                        .insert_str(
1814                            TextPosition::new(range.start, 0),
1815                            &MaskToken::empty_section(fill_after),
1816                        )
1817                        .expect("valid_range");
1818
1819                    Self::reformat(&mut self.masked, &self.mask, mask.sub_start..mask.sub_end);
1820
1821                    pos = mask.sub_end;
1822                }
1823            } else if mask.sub_end > range.end {
1824                // partial end
1825                if mask.right.is_rtol() {
1826                    self.masked
1827                        .remove_str_range(TextRange::new((mask.sub_start, 0), (range.end, 0)))
1828                        .expect("valid_range");
1829
1830                    let fill_before = &self.mask[mask.sub_start as usize..range.end as usize];
1831                    self.masked
1832                        .insert_str(
1833                            TextPosition::new(mask.sub_start, 0),
1834                            &MaskToken::empty_section(fill_before),
1835                        )
1836                        .expect("valid_range");
1837
1838                    Self::reformat(&mut self.masked, &self.mask, mask.sub_start..mask.sub_end);
1839                    pos = mask.sub_end;
1840                } else if mask.right.is_ltor() {
1841                    self.masked
1842                        .remove_str_range(TextRange::new((mask.sub_start, 0), (range.end, 0)))
1843                        .expect("valid_range");
1844
1845                    let len = range.end - mask.sub_start;
1846                    let fill_after =
1847                        &self.mask[(mask.sub_end - len) as usize..mask.sub_end as usize];
1848                    self.masked
1849                        .insert_str(
1850                            TextPosition::new(mask.sub_end - len, 0),
1851                            &MaskToken::empty_section(fill_after),
1852                        )
1853                        .expect("valid_range");
1854
1855                    pos = mask.sub_end;
1856                }
1857            } else {
1858                // full section
1859                self.masked
1860                    .remove_str_range(TextRange::new((mask.sub_start, 0), (mask.sub_end, 0)))
1861                    .expect("valid_range");
1862
1863                let sec_range = &self.mask[mask.sub_start as usize..mask.sub_end as usize];
1864                self.masked
1865                    .insert_str(
1866                        TextPosition::new(mask.sub_start, 0),
1867                        &MaskToken::empty_section(sec_range),
1868                    )
1869                    .expect("valid_range");
1870
1871                // todo: needed?: Self::reformat(&mut self.masked, &self.mask, mask.sec_start..mask.sec_end);
1872                pos = mask.sub_end;
1873            }
1874
1875            if pos >= range.end {
1876                break;
1877            }
1878        }
1879        self.masked.end_undo_seq();
1880
1881        Ok(true)
1882    }
1883
1884    /// Rebuild a section according to number-formatting.
1885    /// The main purpose is to rebuild the grouping separators.
1886    fn reformat(core: &mut TextCore<TextString>, mask: &[MaskToken], section: Range<upos_type>) {
1887        if mask[section.start as usize].right.is_rtol() {
1888            let cursor = core.cursor();
1889            let anchor = core.anchor();
1890
1891            let sec_str = core
1892                .str_slice(TextRange::new((section.start, 0), (section.end, 0)))
1893                .expect("valid_range");
1894
1895            // to be safe, always use our internal symbol set.
1896            let sym = NumberSymbols {
1897                decimal_sep: '.',
1898                decimal_grp: Some(','),
1899                negative_sym: '-',
1900                positive_sym: ' ',
1901                exponent_upper_sym: 'E',
1902                exponent_lower_sym: 'e',
1903                currency_sym: CurrencySym::new("$"),
1904            };
1905
1906            // remove all non numbers and leading 0.
1907            let mut clean = String::new();
1908            _ = clean_num(sec_str.as_ref(), &sym, &mut clean);
1909
1910            // create number format
1911            let mut tok = String::new();
1912            let submask = &mask[section.start as usize..section.end as usize];
1913            // default fmt.sym is nice
1914            for t in submask {
1915                match &t.right {
1916                    Mask::Digit0(_) => tok.push('0'),
1917                    Mask::Digit(_) => tok.push('9'),
1918                    Mask::Numeric(_) => tok.push('#'),
1919                    Mask::DecimalSep => tok.push('.'),
1920                    Mask::GroupingSep => tok.push(','),
1921                    Mask::Sign => tok.push('-'),
1922                    Mask::Plus => tok.push('+'),
1923                    Mask::Separator(s) => {
1924                        for c in s.chars() {
1925                            tok.push('\\');
1926                            tok.push(c);
1927                        }
1928                    }
1929                    Mask::None => {}
1930                    _ => unreachable!("invalid mask"),
1931                }
1932            }
1933
1934            let fmt = match NumberFormat::news(tok, sym) {
1935                Ok(v) => v,
1936                Err(_) => unreachable!("invalid mask"),
1937            };
1938            let mut out = String::new();
1939            match map_num::<_, false>(clean.as_str(), &fmt, fmt.sym(), &mut out) {
1940                Ok(_) => {}
1941                Err(_) => unreachable!("invalid mask"),
1942            }
1943
1944            core.remove_char_range(TextRange::new((section.start, 0), (section.end, 0)))
1945                .expect("valid_range");
1946            core.insert_str(TextPosition::new(section.start, 0), &out)
1947                .expect("valid_position");
1948
1949            // keep cursor intact
1950            core.set_cursor(anchor, false);
1951            core.set_cursor(cursor, true);
1952        } else if mask[section.start as usize].right.is_ltor() {
1953            let cursor = core.cursor();
1954            let anchor = core.anchor();
1955
1956            let sec_str = core
1957                .str_slice(TextRange::new((section.start, 0), (section.end, 0)))
1958                .expect("valid_range");
1959            let sec_mask = &mask[section.start as usize..section.end as usize];
1960            let mut str_new = String::new();
1961            for (g, t) in sec_str.graphemes(true).zip(sec_mask.iter()) {
1962                match t.right {
1963                    Mask::Digit0(_) | Mask::Hex0 | Mask::Oct0 | Mask::Dec0 => {
1964                        if g == " " {
1965                            str_new.push('0');
1966                        } else {
1967                            str_new.push_str(g);
1968                        }
1969                    }
1970                    _ => {
1971                        str_new.push_str(g);
1972                    }
1973                }
1974            }
1975
1976            if sec_str != str_new {
1977                core.remove_char_range(TextRange::new((section.start, 0), (section.end, 0)))
1978                    .expect("valid_range");
1979                core.insert_str(TextPosition::new(section.start, 0), &str_new)
1980                    .expect("valid_position");
1981
1982                // keep cursor intact
1983                core.set_cursor(anchor, false);
1984                core.set_cursor(cursor, true);
1985            }
1986        }
1987    }
1988}
1989
1990mod mask {
1991    use crate::upos_type;
1992    use std::fmt;
1993    use std::fmt::{Debug, Display, Formatter};
1994
1995    /// Edit direction for part of a mask.
1996    /// Numeric values can switch between right-to-left (integer part) and left-to-right (fraction).
1997    #[derive(Clone, Copy, PartialEq, Eq)]
1998    pub(super) enum EditDirection {
1999        Ltor,
2000        Rtol,
2001    }
2002
2003    /// One char of the input mask.
2004    #[allow(variant_size_differences)]
2005    #[derive(Clone, PartialEq, Eq, Default)]
2006    #[non_exhaustive]
2007    pub(super) enum Mask {
2008        // 0-9, display 0
2009        Digit0(EditDirection),
2010        // 0-9, display space
2011        Digit(EditDirection),
2012        // 0-9;sign, display space
2013        Numeric(EditDirection),
2014        DecimalSep,
2015        GroupingSep,
2016        Sign,
2017        Plus,
2018        Hex0,
2019        Hex,
2020        Oct0,
2021        Oct,
2022        Dec0,
2023        Dec,
2024        Letter,
2025        LetterOrDigit,
2026        LetterDigitSpace,
2027        AnyChar,
2028        Separator(Box<str>),
2029        #[default]
2030        None,
2031    }
2032
2033    /// One token of the input mask.
2034    ///
2035    /// Which field of the mask does this belong to:
2036    /// * Number with integer part, decimal separator, fraction and sign.
2037    /// * Consecutive mask parts of similar type.
2038    ///
2039    /// At this cursor position, what is the main mask (right) and what is possible left of
2040    /// the cursor position (peek_left).
2041    ///
2042    /// Default-values for editing and display.
2043    #[derive(Clone, PartialEq, Eq)]
2044    pub(super) struct MaskToken {
2045        pub sec_id: u16,
2046        // section/number
2047        pub sec_start: upos_type,
2048        // section/number
2049        pub sec_end: upos_type,
2050        // part of a number/section
2051        pub sub_start: upos_type,
2052        // part of a number/section
2053        pub sub_end: upos_type,
2054
2055        // token left of the cursor
2056        pub peek_left: Mask,
2057        // token right of the cursor
2058        pub right: Mask,
2059
2060        // edit-value of the token
2061        pub edit: Box<str>,
2062    }
2063
2064    impl Debug for EditDirection {
2065        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2066            write!(
2067                f,
2068                "{}",
2069                match self {
2070                    EditDirection::Ltor => ">",
2071                    EditDirection::Rtol => "<",
2072                }
2073            )
2074        }
2075    }
2076
2077    impl Display for Mask {
2078        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2079            let s = match self {
2080                Mask::Digit0(_) => "0",
2081                Mask::Digit(_) => "9",
2082                Mask::Numeric(_) => "#",
2083                Mask::DecimalSep => ".",
2084                Mask::GroupingSep => ",",
2085                Mask::Sign => "-",
2086                Mask::Plus => "+",
2087                Mask::Hex0 => "H",
2088                Mask::Hex => "h",
2089                Mask::Oct0 => "O",
2090                Mask::Oct => "o",
2091                Mask::Dec0 => "D",
2092                Mask::Dec => "d",
2093                Mask::Letter => "l",
2094                Mask::LetterOrDigit => "a",
2095                Mask::LetterDigitSpace => "c",
2096                Mask::AnyChar => "_",
2097                Mask::Separator(s) => {
2098                    write!(f, "\\")?;
2099                    s
2100                }
2101                Mask::None => "",
2102            };
2103            write!(f, "{}", s)
2104        }
2105    }
2106
2107    impl Debug for Mask {
2108        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2109            match self {
2110                Mask::Digit0(d) => {
2111                    write!(f, "{:?}0", d)
2112                }
2113                Mask::Digit(d) => {
2114                    write!(f, "{:?}9", d)
2115                }
2116                Mask::Numeric(d) => {
2117                    write!(f, "{:?}#", d)
2118                }
2119                Mask::DecimalSep => write!(f, "."),
2120                Mask::GroupingSep => write!(f, ","),
2121                Mask::Sign => write!(f, "-"),
2122                Mask::Plus => write!(f, "+"),
2123                Mask::Hex0 => write!(f, "H"),
2124                Mask::Hex => write!(f, "h"),
2125                Mask::Oct0 => write!(f, "O"),
2126                Mask::Oct => write!(f, "o"),
2127                Mask::Dec0 => write!(f, "D"),
2128                Mask::Dec => write!(f, "d"),
2129                Mask::Letter => write!(f, "l"),
2130                Mask::LetterOrDigit => write!(f, "a"),
2131                Mask::LetterDigitSpace => write!(f, "c"),
2132                Mask::AnyChar => write!(f, "_"),
2133                Mask::Separator(s) => {
2134                    write!(f, "\\")?;
2135                    write!(f, "{}", s)
2136                }
2137                Mask::None => write!(f, ""),
2138            }
2139        }
2140    }
2141
2142    impl EditDirection {
2143        pub(super) fn is_ltor(&self) -> bool {
2144            *self == EditDirection::Ltor
2145        }
2146
2147        pub(super) fn is_rtol(&self) -> bool {
2148            *self == EditDirection::Rtol
2149        }
2150    }
2151
2152    impl Mask {
2153        /// is not editable. the last field of the mask at position txt.len() can not be edited,
2154        /// but it's a valid cursor position.
2155        pub(super) fn is_none(&self) -> bool {
2156            *self == Mask::None
2157        }
2158
2159        /// left to right editing
2160        #[inline]
2161        pub(super) fn is_ltor(&self) -> bool {
2162            match self {
2163                Mask::Digit0(d) => d.is_ltor(),
2164                Mask::Digit(d) => d.is_ltor(),
2165                Mask::Numeric(d) => d.is_ltor(),
2166                Mask::GroupingSep => false,
2167                Mask::Sign => false,
2168                Mask::Plus => false,
2169                Mask::DecimalSep => true,
2170                Mask::Hex0 => true,
2171                Mask::Hex => true,
2172                Mask::Oct0 => true,
2173                Mask::Oct => true,
2174                Mask::Dec0 => true,
2175                Mask::Dec => true,
2176                Mask::Letter => true,
2177                Mask::LetterOrDigit => true,
2178                Mask::LetterDigitSpace => true,
2179                Mask::AnyChar => true,
2180                Mask::Separator(_) => true,
2181                Mask::None => false,
2182            }
2183        }
2184
2185        /// right to left editing
2186        #[inline]
2187        pub(super) fn is_rtol(&self) -> bool {
2188            match self {
2189                Mask::Digit0(d) => d.is_rtol(),
2190                Mask::Digit(d) => d.is_rtol(),
2191                Mask::Numeric(d) => d.is_rtol(),
2192                Mask::GroupingSep => true,
2193                Mask::Sign => true,
2194                Mask::Plus => true,
2195                Mask::DecimalSep => false,
2196                Mask::Hex0 => false,
2197                Mask::Hex => false,
2198                Mask::Oct0 => false,
2199                Mask::Oct => false,
2200                Mask::Dec0 => false,
2201                Mask::Dec => false,
2202                Mask::Letter => false,
2203                Mask::LetterOrDigit => false,
2204                Mask::LetterDigitSpace => false,
2205                Mask::AnyChar => false,
2206                Mask::Separator(_) => false,
2207                Mask::None => false,
2208            }
2209        }
2210
2211        /// is a number mask
2212        #[inline]
2213        pub(super) fn is_number(&self) -> bool {
2214            match self {
2215                Mask::Digit0(_)
2216                | Mask::Digit(_)
2217                | Mask::Numeric(_)
2218                | Mask::DecimalSep
2219                | Mask::GroupingSep
2220                | Mask::Sign
2221                | Mask::Plus => true,
2222                Mask::None => false,
2223                _ => false,
2224            }
2225        }
2226
2227        /// is a separator
2228        #[inline]
2229        pub(super) fn is_separator(&self) -> bool {
2230            match self {
2231                Mask::Separator(_) => true,
2232                Mask::None => false,
2233                _ => false,
2234            }
2235        }
2236
2237        #[inline]
2238        pub(super) fn is_fraction(&self) -> bool {
2239            match self {
2240                Mask::Digit0(d) | Mask::Digit(d) | Mask::Numeric(d) => d.is_ltor(),
2241                Mask::None => false,
2242                _ => false,
2243            }
2244        }
2245
2246        /// which mask-types are put together.
2247        #[inline]
2248        pub(super) fn sub_section(&self) -> u8 {
2249            match self {
2250                Mask::Digit0(_) => 0,
2251                Mask::Digit(_) => 0,
2252                Mask::Numeric(_) => 0,
2253                Mask::GroupingSep => 0,
2254
2255                Mask::Sign => 1,
2256
2257                Mask::Plus => 2,
2258
2259                Mask::DecimalSep => 3,
2260
2261                Mask::Hex0 => 4,
2262                Mask::Hex => 4,
2263
2264                Mask::Oct0 => 5,
2265                Mask::Oct => 5,
2266
2267                Mask::Dec0 => 6,
2268                Mask::Dec => 6,
2269
2270                Mask::Letter => 7,
2271                Mask::LetterOrDigit => 8,
2272                Mask::LetterDigitSpace => 9,
2273                Mask::AnyChar => 10,
2274
2275                Mask::Separator(_) => 11,
2276
2277                Mask::None => 12,
2278            }
2279        }
2280
2281        /// which mask-types constitute a number/section
2282        #[inline]
2283        pub(super) fn section(&self) -> u8 {
2284            match self {
2285                Mask::Digit0(_) => 0,
2286                Mask::Digit(_) => 0,
2287                Mask::Numeric(_) => 0,
2288                Mask::GroupingSep => 0,
2289                Mask::Sign => 0,
2290                Mask::Plus => 0,
2291                Mask::DecimalSep => 0,
2292
2293                Mask::Hex0 => 1,
2294                Mask::Hex => 1,
2295                Mask::Oct0 => 1,
2296                Mask::Oct => 1,
2297                Mask::Dec0 => 1,
2298                Mask::Dec => 1,
2299                Mask::Letter => 1,
2300                Mask::LetterOrDigit => 1,
2301                Mask::LetterDigitSpace => 1,
2302                Mask::AnyChar => 1,
2303
2304                Mask::Separator(_) => 2,
2305                Mask::None => 3,
2306            }
2307        }
2308
2309        /// mask should overwrite instead of insert
2310        #[inline]
2311        pub(super) fn can_overwrite_fraction(&self, c: &str) -> bool {
2312            match self {
2313                Mask::Digit0(_) => c == "0",
2314                Mask::Digit(_) | Mask::Numeric(_) => c == " ",
2315                Mask::DecimalSep => false,
2316                Mask::GroupingSep => false,
2317                Mask::Sign => false,
2318                Mask::Plus => false,
2319                Mask::Hex0 => false,
2320                Mask::Hex => false,
2321                Mask::Oct0 => false,
2322                Mask::Oct => false,
2323                Mask::Dec0 => false,
2324                Mask::Dec => false,
2325                Mask::Letter => false,
2326                Mask::LetterOrDigit => false,
2327                Mask::LetterDigitSpace => false,
2328                Mask::AnyChar => false,
2329                Mask::Separator(_) => false,
2330                Mask::None => false,
2331            }
2332        }
2333
2334        /// mask should overwrite instead of insert
2335        #[inline]
2336        pub(super) fn can_overwrite(&self, c: &str) -> bool {
2337            match self {
2338                Mask::Digit0(_) | Mask::Digit(_) | Mask::Numeric(_) => false,
2339                Mask::DecimalSep => "." == c,
2340                Mask::GroupingSep => false,
2341                Mask::Sign => "-" == c || " " == c,
2342                Mask::Plus => "-" == c || "+" == c || " " == c,
2343                Mask::Hex0 => c == "0",
2344                Mask::Hex => false,
2345                Mask::Oct0 => c == "0",
2346                Mask::Oct => false,
2347                Mask::Dec0 => c == "0",
2348                Mask::Dec => false,
2349                Mask::Letter => false,
2350                Mask::LetterOrDigit => false,
2351                Mask::LetterDigitSpace => false,
2352                Mask::AnyChar => false,
2353                Mask::Separator(sep) => sep.as_ref() == c,
2354                Mask::None => false,
2355            }
2356        }
2357
2358        /// char can be dropped from the text and it's ok.
2359        #[inline]
2360        pub(super) fn can_drop(&self, c: &str) -> bool {
2361            match self {
2362                Mask::Digit0(_) => c == "0",
2363                Mask::Digit(_) => c == " ",
2364                Mask::Numeric(_) => c == " ",
2365                Mask::DecimalSep => false,
2366                Mask::Sign => false,
2367                Mask::Plus => false,
2368                Mask::GroupingSep => true,
2369                Mask::Hex0 => c == "0",
2370                Mask::Hex => c == " ",
2371                Mask::Oct0 => c == "0",
2372                Mask::Oct => c == " ",
2373                Mask::Dec0 => c == "0",
2374                Mask::Dec => c == " ",
2375                Mask::Letter => c == " ",
2376                Mask::LetterOrDigit => c == " ",
2377                Mask::LetterDigitSpace => c == " ",
2378                Mask::AnyChar => c == " ",
2379                Mask::Separator(_sep) => false,
2380                Mask::None => false,
2381            }
2382        }
2383
2384        /// default char for this mask.
2385        #[inline]
2386        pub(super) fn edit_value(&self) -> &str {
2387            match self {
2388                Mask::Digit0(_) => "0",
2389                Mask::Digit(_) => " ",
2390                Mask::Numeric(_) => " ",
2391                Mask::DecimalSep => ".",
2392                Mask::GroupingSep => " ", // don't show. reformat fills it in if necessary.
2393                Mask::Sign => " ",
2394                Mask::Plus => "+",
2395                Mask::Hex0 => "0",
2396                Mask::Hex => " ",
2397                Mask::Oct0 => "0",
2398                Mask::Oct => " ",
2399                Mask::Dec0 => "0",
2400                Mask::Dec => " ",
2401                Mask::Letter => " ",
2402                Mask::LetterOrDigit => " ",
2403                Mask::LetterDigitSpace => " ",
2404                Mask::AnyChar => " ",
2405                Mask::Separator(g) => g.as_ref(),
2406                Mask::None => "",
2407            }
2408        }
2409    }
2410
2411    impl Debug for MaskToken {
2412        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2413            write!(
2414                f,
2415                "Mask #{}:{}-{} {:?} | {:?}",
2416                self.sec_id, self.sub_start, self.sub_end, self.peek_left, self.right
2417            )
2418        }
2419    }
2420
2421    impl MaskToken {
2422        /// is somewhere in the integer part of a number.
2423        #[inline]
2424        pub(super) fn is_integer_part(&self) -> bool {
2425            self.peek_left.is_rtol() || self.peek_left.is_none() && self.right.is_rtol()
2426        }
2427
2428        /// Create a string with the default edit mask.
2429        pub(super) fn empty_section(mask: &[MaskToken]) -> String {
2430            let mut buf = String::new();
2431            for m in mask {
2432                buf.push_str(&m.edit);
2433            }
2434            buf
2435        }
2436    }
2437}