Text.cpp

Zbigniew Rebacz, 04/25/2014 09:10 PM

Download (13.6 KB)

 
1
#include <CtrlLib/CtrlLib.h>
2

    
3
NAMESPACE_UPP
4

    
5
TextCtrl::TextCtrl()
6
{
7
        Unicode();
8
        undosteps = 10;
9
        Clear();
10
        undoserial = 0;
11
        incundoserial = false;
12
        undo_op = false;
13
        WhenBar = THISBACK(StdBar);
14
        charset = CHARSET_UNICODE;
15
        color[INK_NORMAL] = SColorText;
16
        color[INK_DISABLED] = SColorDisabled;
17
        color[INK_SELECTED] = SColorHighlightText;
18
        color[PAPER_NORMAL] = SColorPaper;
19
        color[PAPER_READONLY] = SColorFace;
20
        color[PAPER_SELECTED] = SColorHighlight;
21
        processtab = true;
22
        processenter = true;
23
        nobg = false;
24
}
25

    
26
TextCtrl::~TextCtrl() {}
27

    
28
void TextCtrl::MiddleDown(Point p, dword flags)
29
{
30
        if(IsReadOnly())
31
                return;
32
        if(AcceptText(Selection())) {
33
                WString w = GetWString(Selection());
34
                selclick = false;
35
                LeftDown(p, flags);
36
                Paste(w);
37
                Action();
38
        }
39
}
40

    
41
void TextCtrl::CancelMode()
42
{
43
        selclick = false;
44
        dropcaret = Null;
45
        isdrag = false;
46
}
47

    
48
void TextCtrl::Clear()
49
{
50
        cline = cpos = 0;
51
        total = 0;
52
        line.Clear();
53
        line.Shrink();
54
        ClearLines();
55
        line.Add();
56
        InsertLines(0, 1);
57
        DirtyFrom(0);
58
        undo.Clear();
59
        redo.Clear();
60
        ClearDirty();
61
        anchor = -1;
62
        cursor = 0;
63
        SetSb();
64
        PlaceCaret(0);
65
        SelectionChanged();
66
        Refresh();
67
}
68

    
69
void TextCtrl::DirtyFrom(int line) {}
70
void TextCtrl::SelectionChanged() {}
71
void TextCtrl::ClearLines() {}
72
void TextCtrl::InsertLines(int line, int count) {}
73
void TextCtrl::RemoveLines(int line, int count) {}
74
void TextCtrl::PreInsert(int pos, const WString& text) {}
75
void TextCtrl::PostInsert(int pos, const WString& text) {}
76
void TextCtrl::PreRemove(int pos, int size) {}
77
void TextCtrl::PostRemove(int pos, int size) {}
78
void TextCtrl::RefreshLine(int i) {}
79
void TextCtrl::InvalidateLine(int i) {}
80
void TextCtrl::SetSb() {}
81
void TextCtrl::PlaceCaret(int newcursor, bool sel) {}
82

    
83
void   TextCtrl::CachePos(int pos) {
84
        int p = pos;
85
        cline = GetLinePos(p);
86
        cpos = pos - p;
87
}
88

    
89
int   TextCtrl::Load(Stream& s, byte charset) {
90
        Clear();
91
        line.Clear();
92
        ClearLines();
93
        StringBuffer ln;
94
        total = 0;
95
        SetCharset(charset);
96
        if(charset == CHARSET_UTF8_BOM && s.GetLeft() >= 3) {
97
                int64 pos = s.GetPos();
98
                byte h[3];
99
                if(!(s.Get(h, 3) == 3 && h[0] == 0xEF && h[1] == 0xBB && h[2] == 0xBF))
100
                        s.Seek(pos);
101
                charset = CHARSET_UTF8;
102
        }
103
        bool cr = false;
104
        byte b8 = 0;
105
        while(!s.IsEof()) {
106
                int c = s.Get();
107
                if(c == '\r')
108
                        cr = true;
109
                if(c == '\n') {
110
                        if(charset != CHARSET_UTF8 || (b8 & 128)) {
111
                                WString w = ToUnicode(~ln, ln.GetCount(), charset);
112
                                line.Add(w);
113
                                total += w.GetLength() + 1;
114
                        }
115
                        else {
116
                                total += ln.GetCount() + 1;
117
                                Ln& l = line.Add();
118
                                l.len = ln.GetCount();
119
                                l.text = ln;
120
                        }
121
                        ln.Clear();
122
                        b8 = 0;
123
                }
124
                if(c >= ' ' || c == '\t') {
125
                        b8 |= c;
126
                        ln.Cat(c);
127
                }
128
        }
129
        WString w = ToUnicode(~ln, ln.GetCount(), charset);
130
        line.Add(w);
131
        total += w.GetLength();
132
        InsertLines(0, line.GetCount());
133
        Update();
134
        SetSb();
135
        PlaceCaret(0);
136
        return line.GetCount() > 1 ? cr ? LE_CRLF : LE_LF : LE_DEFAULT;
137
}
138

    
139
void   TextCtrl::Save(Stream& s, byte charset, int line_endings) const {
140
        if(charset == CHARSET_UTF8_BOM) {
141
                static byte bom[] = { 0xEF, 0xBB, 0xBF };
142
                s.Put(bom, 3);
143
                charset = CHARSET_UTF8;
144
        }
145
        charset = ResolveCharset(charset);
146
        String le = "\n";
147
#ifdef PLATFORM_WIN32
148
        if(line_endings == LE_DEFAULT)
149
                le = "\r\n";
150
#endif
151
        if(line_endings == LE_CRLF)
152
                le = "\r\n";
153
        for(int i = 0; i < line.GetCount(); i++) {
154
                if(i)
155
                        s.Put(le);
156
                String txt = charset == CHARSET_UTF8 ? line[i].text
157
                                                     : FromUnicode(line[i], charset);
158
                const char *e = txt.End();
159
                for(const char *w = txt; w != e; w++)
160
                        s.Put(*w == DEFAULTCHAR ? '?' : *w);
161
        }
162
}
163

    
164
void   TextCtrl::Set(const String& s, byte charset) {
165
        StringStream ss(s);
166
        Load(ss, charset);
167
}
168

    
169
String TextCtrl::Get(byte charset) const
170
{
171
        StringStream ss;
172
        Save(ss, charset);
173
        return ss;
174
}
175

    
176
int TextCtrl::GetInvalidCharPos(byte charset) const
177
{
178
        int q = 0;
179
        if(charset != CHARSET_UTF8 && charset != CHARSET_UTF8_BOM)
180
                for(int i = 0; i < line.GetCount(); i++) {
181
                        WString txt = line[i];
182
                        WString ctxt = ToUnicode(FromUnicode(txt, charset), charset);
183
                        for(int w = 0; w < txt.GetLength(); w++)
184
                                if(txt[w] != ctxt[w])
185
                                        return q + w;
186
                        q += txt.GetLength() + 1;
187
                }
188
        return -1;
189
}
190

    
191
void   TextCtrl::ClearDirty()
192
{
193
        dirty = 0;
194
        ClearModify();
195
        WhenState();
196
}
197

    
198
TextCtrl::UndoData TextCtrl::PickUndoData()
199
{
200
        UndoData data;
201
        data.undo = pick(undo);
202
        data.redo = pick(redo);
203
        data.undoserial = undoserial;
204
        return data;
205
}
206

    
207
void TextCtrl::SetPickUndoData(TextCtrl::UndoData rval_ data)
208
{
209
        undo = pick(data.undo);
210
        redo = pick(data.redo);
211
        undoserial = data.undoserial;
212
        incundoserial = true;
213
}
214

    
215
void TextCtrl::Set(const WString& s)
216
{
217
        Clear();
218
        Insert0(0, s);
219
}
220

    
221
void  TextCtrl::SetData(const Value& v)
222
{
223
        Set((WString)v);
224
}
225

    
226
Value TextCtrl::GetData() const
227
{
228
        return GetW();
229
}
230

    
231
String TextCtrl::GetEncodedLine(int i, byte charset) const
232
{
233
        charset = ResolveCharset(charset);
234
        if(charset == CHARSET_UTF8 && charset != CHARSET_UTF8_BOM)
235
                return line[i].text;
236
        return FromUnicode(FromUtf8(line[i].text), charset);
237
}
238

    
239
int   TextCtrl::GetLinePos(int& pos) const {
240
        if(pos < cpos && cpos - pos < pos) {
241
                int i = cline;
242
                int ps = cpos;
243
                for(;;) {
244
                        ps -= line[--i].GetLength() + 1;
245
                        if(ps <= pos) {
246
                                pos = pos - ps;
247
                                return i;
248
                        }
249
                }
250
        }
251
        else {
252
                int i = 0;
253
                if(pos >= cpos) {
254
                        pos -= cpos;
255
                        i = cline;
256
                }
257
                for(;;) {
258
                        int n = line[i].GetLength() + 1;
259
                        if(pos < n) return i;
260
                        pos -= n;
261
                        i++;
262
                        if(i >= line.GetCount()) {
263
                                pos = line.Top().GetLength();
264
                                return line.GetCount() - 1;
265
                        }
266
                }
267
        }
268
}
269

    
270
int   TextCtrl::GetPos(int ln, int lpos) const {
271
        ln = minmax(ln, 0, line.GetCount() - 1);
272
        int i, pos;
273
        if(ln < cline && cline - ln < ln) {
274
                pos = cpos;
275
                i = cline;
276
                while(i > ln)
277
                        pos -= line[--i].GetLength() + 1;
278
        }
279
        else {
280
                if(ln >= cline) {
281
                        pos = cpos;
282
                        i = cline;
283
                }
284
                else {
285
                        pos = 0;
286
                        i = 0;
287
                }
288
                while(i < ln)
289
                        pos += line[i++].GetLength() + 1;
290
        }
291
        return pos + min(line[ln].GetLength(), lpos);
292
}
293

    
294
WString TextCtrl::GetW(int pos, int size) const
295
{
296
        int i = GetLinePos(pos);
297
        WString r;
298
        for(;;) {
299
                if(i >= line.GetCount()) break;
300
                WString ln = line[i++];
301
                int sz = min(ln.GetLength() - pos, size);
302
                r.Cat(ln.Mid(pos, sz));
303
                size -= sz;
304
                if(size == 0) break;
305
#ifdef PLATFORM_WIN32
306
                r.Cat('\r');
307
#endif
308
                r.Cat('\n');
309
                size--;
310
                if(size == 0) break;
311
                pos = 0;
312
        }
313
        return r;
314
}
315

    
316
String TextCtrl::Get(int pos, int size, byte charset) const
317
{
318
        return FromUnicode(GetW(pos, size), charset);
319
}
320

    
321
int  TextCtrl::GetChar(int pos) const {
322
        int i = GetLinePos(pos);
323
        WString ln = line[i];
324
        int c = ln.GetLength() == pos ? '\n' : ln[pos];
325
        return c;
326
}
327

    
328
int TextCtrl::Insert0(int pos, const WString& txt) {
329
        int inspos = pos;
330
        PreInsert(inspos, txt);
331
        if(pos < cpos)
332
                cpos = cline = 0;
333
        int i = GetLinePos(pos);
334
        DirtyFrom(i);
335
        int size = 0;
336
        WString ln;
337
        Vector<Ln> iln;
338
        for(const wchar *s = txt; s < txt.End(); s++)
339
                if(*s >= ' ' || *s == '\t') {
340
                        ln.Cat(*s);
341
                        size++;
342
                }
343
                else
344
                if(*s == '\n') {
345
                        iln.Add(ln);
346
                        size++;
347
                        ln.Clear();
348
                }
349

    
350
        WString l = line[i];
351
        if(iln.GetCount()) {
352
                iln[0] = l.Mid(0, pos) + WString(iln[0]);
353
                ln.Cat(l.Mid(pos));
354
                line[i] = ln;
355
                InvalidateLine(i);
356
                line.Insert(i, iln);
357
                InsertLines(i, iln.GetCount());
358
                Refresh();
359
        }
360
        else {
361
                line[i] = l.Mid(0, pos) + ln + l.Mid(pos);
362
                InvalidateLine(i);
363
                RefreshLine(i);
364
        }
365
        total += size;
366
        SetSb();
367
        Update();
368
        PostInsert(inspos, txt);
369
        return size;
370
}
371

    
372
void TextCtrl::Remove0(int pos, int size) {
373
        int rmpos = pos, rmsize = size;
374
        PreRemove(rmpos, rmsize);
375
        total -= size;
376
        if(pos < cpos)
377
                cpos = cline = 0;
378
        int i = GetLinePos(pos);
379
        DirtyFrom(i);
380
        WString ln = line[i];
381
        int sz = min(ln.GetLength() - pos, size);
382
        ln.Remove(pos, sz);
383
        size -= sz;
384
        line[i] = ln;
385
        if(size == 0) {
386
                InvalidateLine(i);
387
                RefreshLine(i);
388
        }
389
        else {
390
                size--;
391
                int j = i + 1;
392
                for(;;) {
393
                        int sz = line[j].GetLength() + 1;
394
                        if(sz > size) break;
395
                        j++;
396
                        size -= sz;
397
                }
398
                WString p1 = line[i];
399
                WString p2 = line[j];
400
                p1.Insert(p1.GetLength(), p2.Mid(size, p2.GetLength() - size));
401
                line[i] = p1;
402
                line.Remove(i + 1, j - i);
403
                RemoveLines(i + 1, j - i);
404
                InvalidateLine(i);
405
                Refresh();
406
        }
407
        Update();
408
        PostRemove(rmpos, rmsize);
409
        SetSb();
410
}
411

    
412
void TextCtrl::Undodo()
413
{
414
        while(undo.GetCount() > undosteps)
415
                undo.DropHead();
416
        redo.Clear();
417
}
418

    
419
void TextCtrl::NextUndo()
420
{
421
        undoserial += incundoserial;
422
        incundoserial = false;
423
}
424

    
425
void TextCtrl::IncDirty() {
426
        dirty++;
427
        if(dirty == 0 || dirty == 1)
428
        {
429
                if(dirty)
430
                        SetModify();
431
                else
432
                        ClearModify();
433
                WhenState();
434
        }
435
}
436

    
437
void TextCtrl::DecDirty() {
438
        dirty--;
439
        if(dirty == 0 || dirty == -1)
440
        {
441
                if(dirty)
442
                        SetModify();
443
                else
444
                        ClearModify();
445
                WhenState();
446
        }
447
}
448

    
449
int TextCtrl::InsertU(int pos, const WString& txt, bool typing) {
450
        int sz = Insert0(pos, txt);
451
        if(undosteps) {
452
                if(undo.GetCount() > 1 && typing && *txt != '\n' && IsDirty()) {
453
                        UndoRec& u = undo.Tail();
454
                        if(u.typing && u.pos + u.size == pos) {
455
                                u.size += txt.GetLength();
456
                                return sz;
457
                        }
458
                }
459
                UndoRec& u = undo.AddTail();
460
                incundoserial = true;
461
                IncDirty();
462
                u.serial = undoserial;
463
                u.pos = pos;
464
                u.size = sz;
465
                u.typing = typing;
466
        }
467
        return sz;
468
}
469

    
470
void TextCtrl::RemoveU(int pos, int size) {
471
        if(size + pos > total)
472
                size = total - pos;
473
        if(size <= 0) return;
474
        if(undosteps) {
475
                UndoRec& u = undo.AddTail();
476
                incundoserial = true;
477
                IncDirty();
478
                u.serial = undoserial;
479
                u.pos = pos;
480
                u.size = 0;
481
                u.text = Get(pos, size, CHARSET_UTF8);
482
                u.typing = false;
483
        }
484
        Remove0(pos, size);
485
}
486

    
487
int TextCtrl::Insert(int pos, const WString& _txt, bool typing) {
488
        WString txt = _txt;
489
        if(charset != CHARSET_UNICODE && charset != CHARSET_UTF8_BOM)
490
                for(int i = 0; i < txt.GetCount(); i++)
491
                        if(FromUnicode(txt[i], charset) == DEFAULTCHAR)
492
                                txt.Set(i, '?');
493
        int sz = InsertU(pos, txt, typing);
494
        Undodo();
495
        return sz;
496
}
497

    
498
int TextCtrl::Insert(int pos, const String& txt, byte charset)
499
{
500
        return Insert(pos, ToUnicode(txt, charset), false);
501
}
502

    
503
void TextCtrl::Remove(int pos, int size) {
504
        RemoveU(pos, size);
505
        Undodo();
506
}
507

    
508
void TextCtrl::Undo() {
509
        if(undo.IsEmpty()) return;
510
        undo_op = true;
511
        int nc = 0;
512
        int s = undo.Tail().serial;
513
        while(undo.GetCount()) {
514
                const UndoRec& u = undo.Tail();
515
                if(u.serial != s)
516
                        break;
517
                UndoRec& r = redo.AddTail();
518
                r.serial = s;
519
                r.typing = false;
520
                nc = r.pos = u.pos;
521
                CachePos(r.pos);
522
                if(u.size) {
523
                        r.size = 0;
524
                        r.text = Get(u.pos, u.size, CHARSET_UTF8);
525
                        Remove0(u.pos, u.size);
526
                }
527
                else {
528
                        WString text = FromUtf8(u.text);
529
                        r.size = Insert0(u.pos, text);
530
                        nc += r.size;
531
                }
532
                undo.DropTail();
533
                DecDirty();
534
        }
535
        ClearSelection();
536
        PlaceCaret(nc, false);
537
        Action();
538
        undo_op = false;
539
}
540

    
541
void TextCtrl::Redo() {
542
        if(!redo.GetCount()) return;
543
        NextUndo();
544
        int s = redo.Tail().serial;
545
        int nc = 0;
546
        while(redo.GetCount()) {
547
                const UndoRec& r = redo.Tail();
548
                if(r.serial != s)
549
                        break;
550
                nc = r.pos + r.size;
551
                CachePos(r.pos);
552
                if(r.size)
553
                        RemoveU(r.pos, r.size);
554
                else
555
                        nc += InsertU(r.pos, FromUtf8(r.text));
556
                redo.DropTail();
557
                IncDirty();
558
        }
559
        ClearSelection();
560
        PlaceCaret(nc, false);
561
        Action();
562
}
563

    
564
void  TextCtrl::ClearSelection() {
565
        anchor = -1;
566
        Refresh();
567
        WhenSel();
568
}
569

    
570
void   TextCtrl::SetSelection(int l, int h) {
571
        if(l != h) {
572
                PlaceCaret(minmax(l, 0, total), false);
573
                PlaceCaret(minmax(h, 0, total), true);
574
        }
575
        else
576
                SetCursor(l);
577
}
578

    
579
bool   TextCtrl::GetSelection(int& l, int& h) const {
580
        if(anchor < 0) {
581
                l = h = cursor;
582
                return false;
583
        }
584
        else {
585
                l = min(anchor, cursor);
586
                h = max(anchor, cursor);
587
                return true;
588
        }
589
}
590

    
591
String TextCtrl::GetSelection(byte charset) const {
592
        int l, h;
593
        if(GetSelection(l, h))
594
                return Get(l, h - l, charset);
595
        return String();
596
}
597

    
598
WString TextCtrl::GetSelectionW() const {
599
        int l, h;
600
        if(GetSelection(l, h))
601
                return GetW(l, h - l);
602
        return WString();
603
}
604

    
605
bool   TextCtrl::RemoveSelection() {
606
        int l, h;
607
        if(anchor < 0) return false;
608
        GetSelection(l, h);
609
        Remove(l, h - l);
610
        anchor = -1;
611
        Refresh();
612
        PlaceCaret(l);
613
        Action();
614
        return true;
615
}
616

    
617
void TextCtrl::RefreshLines(int l1, int l2) {
618
        int h = max(l1, l2);
619
        for(int i = min(l1, l2); i <= h; i++)
620
                RefreshLine(i);
621
}
622

    
623
void TextCtrl::Cut() {
624
        if(!IsReadOnly() && IsSelection()) {
625
                Copy();
626
                RemoveSelection();
627
        }
628
}
629

    
630
void TextCtrl::Copy() {
631
        int l, h;
632
        if(!GetSelection(l, h)) {
633
                int i = GetLine(cursor);
634
                l = GetPos(i);
635
                h = l + line[i].GetLength() + 1;
636
        }
637
        WString txt = GetW(l, h - l);
638
        ClearClipboard();
639
        AppendClipboardUnicodeText(txt);
640
        AppendClipboardText(txt.ToString());
641
}
642

    
643
void TextCtrl::SelectAll() {
644
        SetSelection();
645
}
646

    
647
int  TextCtrl::Paste(const WString& text) {
648
        if(IsReadOnly()) return 0;
649
        RemoveSelection();
650
        int n = Insert(cursor, text);
651
        PlaceCaret(cursor + n);
652
        Refresh();
653
        return n;
654
}
655

    
656
void TextCtrl::Paste() {
657
        WString w = ReadClipboardUnicodeText();
658
        if(w.IsEmpty())
659
                w = ReadClipboardText().ToWString();
660
        Paste(w);
661
        Action();
662
}
663

    
664
void TextCtrl::StdBar(Bar& menu) {
665
        NextUndo();
666
        if(undosteps) {
667
                menu.Add(undo.GetCount() && IsEditable(), t_("Undo"), CtrlImg::undo(), THISBACK(Undo))
668
                        .Key(K_ALT_BACKSPACE)
669
                        .Key(K_CTRL_Z);
670
                menu.Add(redo.GetCount() && IsEditable(), t_("Redo"), CtrlImg::redo(), THISBACK(Redo))
671
                        .Key(K_SHIFT|K_ALT_BACKSPACE)
672
                        .Key(K_SHIFT_CTRL_Z);
673
                menu.Separator();
674
        }
675
        menu.Add(IsEditable() && IsSelection(),
676
                        t_("Cut"), CtrlImg::cut(), THISBACK(Cut))
677
                .Key(K_SHIFT_DELETE)
678
                .Key(K_CTRL_X);
679
        menu.Add(IsSelection(),
680
                        t_("Copy"), CtrlImg::copy(), THISBACK(Copy))
681
                .Key(K_CTRL_INSERT)
682
                .Key(K_CTRL_C);
683
        menu.Add(IsEditable() && IsClipboardAvailableText(),
684
                        t_("Paste"), CtrlImg::paste(), THISBACK(DoPaste))
685
                .Key(K_SHIFT_INSERT)
686
                .Key(K_CTRL_V);
687
        menu.Add(IsEditable() && IsSelection(),
688
                        t_("Erase"), CtrlImg::remove(), THISBACK(DoRemoveSelection))
689
                .Key(K_DELETE);
690
        menu.Separator();
691
        menu.Add(GetLength(),
692
                        t_("Select all"), CtrlImg::select_all(), THISBACK(SelectAll))
693
                .Key(K_CTRL_A);
694
}
695

    
696
String TextCtrl::GetSelectionData(const String& fmt) const
697
{
698
        return GetTextClip(GetSelectionW(), fmt);
699
}
700

    
701
END_UPP_NAMESPACE