1
2 // ------------------------------------------------------------------
3 // GoldED+
4 // Copyright (C) 1990-1999 Odinn Sorensen
5 // Copyright (C) 1999-2000 Alexander S. Aganichev
6 // ------------------------------------------------------------------
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20 // MA 02111-1307 USA
21 // ------------------------------------------------------------------
22 // $Id: geedit.cpp,v 1.80 2012/05/15 17:02:38 stas_degteff Exp $
23 // ------------------------------------------------------------------
24 // The Internal Editor (IE), part 1.
25 // ------------------------------------------------------------------
26
27 #ifdef __GNUG__
28 #pragma implementation "geedit.h"
29 #endif
30
31 // ------------------------------------------------------------------
32
33 #include <golded.h>
34 #include <geedit.h>
35
36 gkey kbxget_raw(int mode);
37
38
39 // ------------------------------------------------------------------
40 // Globals
41
42 Line* Edit__killbuf = NULL;
43 Line* Edit__pastebuf = NULL;
44
45 Path Edit__exportfilename = {""};
46
47 UndoItem** UndoItem::last_item;
48
49
50 // ------------------------------------------------------------------
51
52 #ifndef NDEBUG
53
debugtest(char * __test,int __a,int __b,char * __file,int __line,int __values)54 void IEclass::debugtest(char* __test, int __a, int __b, char* __file, int __line, int __values) {
55
56 #if defined(GFTRK_ENABLE)
57 int _tmp = __gftrk_on;
58 __gftrk_on = false;
59 #endif
60
61 savefile(MODE_UPDATE);
62
63 #if defined(GFTRK_ENABLE)
64 __gftrk_on = _tmp;
65 #endif
66
67 if(__values)
68 w_infof(" Range check: (%s) <%i,%i> [%s,%u] ", __test, __a, __b, __file, __line);
69 else
70 w_infof(" Range check: (%s) [%s,%u] ", __test, __file, __line);
71 update_statusline(LNG->EscOrContinue);
72 SayBibi();
73 while(kbxhit())
74 kbxget();
75 gkey _key = kbxget();
76 w_info(NULL);
77 if(_key == Key_Esc) {
78 LOG.errtest(__file, __line);
79 LOG.printf("! An internal editor range check failed.");
80 if(__values)
81 LOG.printf(": Details: (%s) <%i,%i>.", __test, __a, __b);
82 else {
83 LOG.printf(": Details: (%s).", __test);
84 LOG.printf(": Details: r%u,c%u,mr%u,mc%u,i%u,dm%u,qm%u,eqm%u.",
85 row, col, maxrow, maxcol, insert,
86 CFG->dispmargin, CFG->quotemargin, EDIT->QuoteMargin());
87 }
88 LOG.printf("+ Advice: Report to the Author.");
89 TestErrorExit();
90 }
91 }
92
93 #endif
94
95
96 // ------------------------------------------------------------------
97
98 #if defined(GCFG_SPELL_INCLUDED)
isscchar(int c)99 inline bool isscchar(int c)
100 {
101 return isxalnum(c) || (c == '-') || (c == '\'') || (c == '.') ;
102 }
103 #endif
104
105
106 // ------------------------------------------------------------------
107 // Make sure line type is correct
108
setlinetype(Line * __line)109 void IEclass::setlinetype(Line* __line) {
110
111 _test_halt(__line == NULL);
112
113 __line->type &= ~(GLINE_ALL|GLINE_TEAR|GLINE_ORIG|GLINE_TAGL);
114
115 if (is_quote(__line->txt.c_str()) &&
116 is_quote2(__line, __line->txt.c_str()))
117 {
118 __line->type |= GLINE_QUOT;
119 }
120 else if (__line->txt[0] == CTRL_A)
121 {
122 __line->type |= GLINE_HIDD;
123 }
124 }
125
126
127 // ------------------------------------------------------------------
128 // Insert character in string at position
129
130 // ------------------------------------------------------------------
131 // Zero-based
132
dispchar(vchar __ch,vattr attr)133 vattr IEclass::dispchar(vchar __ch, vattr attr) {
134
135 if(__ch == NUL) // possible if line empty
136 __ch = ' ';
137 if(__ch == '\n') {
138 __ch = EDIT->CharPara();
139 }
140 else if(__ch == ' ') {
141 __ch = EDIT->CharSpace();
142 }
143
144 vattr atr;
145 vchar chr;
146 editwin.getc(crow, ccol, &atr, &chr);
147 editwin.printc(crow, ccol, attr == DEFATTR ? atr : attr, __ch);
148 return atr;
149 }
150
151
152 // ------------------------------------------------------------------
153
cursoroff()154 void IEclass::cursoroff() {
155
156 vcurhide();
157 }
158
159
160 // ------------------------------------------------------------------
161
cursoron()162 void IEclass::cursoron() {
163
164 vcurshow();
165 }
166
167
168 // ------------------------------------------------------------------
169 // Zero-based
170
scrollup(int __scol,int __srow,int __ecol,int __erow,int __lines)171 void IEclass::scrollup(int __scol, int __srow, int __ecol, int __erow, int __lines) {
172
173 editwin.scroll_box_up(__srow, __scol, __erow, __ecol, __lines);
174 }
175
176
177 // ------------------------------------------------------------------
178 // Zero-based
179
scrolldown(int __scol,int __srow,int __ecol,int __erow,int __lines)180 void IEclass::scrolldown(int __scol, int __srow, int __ecol, int __erow, int __lines) {
181
182 editwin.scroll_box_down(__srow, __scol, __erow, __ecol, __lines);
183 }
184
185
186 // ------------------------------------------------------------------
187 // Zero-based
188
prints(int wrow,int wcol,vattr atr,const char * str)189 void IEclass::prints(int wrow, int wcol, vattr atr, const char* str) {
190
191 editwin.prints(wrow, wcol, atr, str);
192 }
193
194
195 // ------------------------------------------------------------------
196
findfirstline()197 Line* IEclass::findfirstline() {
198
199 GFTRK("Editfindfirstline");
200
201 if(not currline)
202 return NULL;
203
204 // Rewind to the first line
205 Line* _firstline = currline;
206 while(_firstline->prev)
207 _firstline = _firstline->prev;
208
209 GFTRK(0);
210
211 return _firstline;
212 }
213
214
215 // ------------------------------------------------------------------
216 // Find out what number the current line is and put it in "thisrow"
217
getthisrow(Line * __currline)218 void IEclass::getthisrow(Line* __currline) {
219
220 GFTRK("Editgetthisrow");
221
222 Line* _templine = findfirstline();
223
224 thisrow = 0;
225 while((_templine != __currline) and _templine->next) {
226 _templine = _templine->next;
227 thisrow++;
228 }
229
230 GFTRK(0);
231 }
232
233
234 // ------------------------------------------------------------------
235 // Zero-based
236
gotorowcol(uint __col,uint __row)237 void IEclass::gotorowcol(uint __col, uint __row) {
238
239 GFTRK("Editgotorowcol");
240
241 _test_haltab(__col > maxcol, __col, maxcol);
242 _test_haltab(__row > maxrow, __row, maxrow);
243
244 editwin.move_cursor(__row, __col);
245
246 ccol = __col;
247 crow = __row;
248
249 GFTRK(0);
250 }
251
252
253 // ------------------------------------------------------------------
254
255 #if defined(GCFG_SPELL_INCLUDED)
dispstringsc(char * __buf,uint __beg,uint __end,uint __row,uint __col,char endchar)256 void IEclass::dispstringsc(char *__buf, uint __beg, uint __end, uint __row, uint __col, char endchar)
257 {
258 char scbuf[EDIT_BUFLEN];
259
260 uint bbeg = __beg;
261 uint bend = __beg;
262 uint bpos = __beg;
263
264 if ((bbeg > 0) && isscchar(__buf[bbeg]) && isscchar(__buf[bbeg-1]))
265 {
266 for (; (bpos < __end) && isscchar(__buf[bpos]); bpos++);
267 bend = bpos;
268 }
269
270 while (bpos < __end)
271 {
272 for (; (bpos < __end) && !isscchar(__buf[bpos]); bpos++);
273 bend = bpos;
274
275 uint scpos = 0;
276 for (; (bpos < __end) && isscchar(__buf[bpos]); bpos++, scpos++)
277 scbuf[scpos] = __buf[bpos];
278
279 if ((scpos == 0) || ((bpos == __end) && isscchar(endchar)))
280 {
281 bend = bpos;
282 break;
283 }
284
285 scbuf[scpos] = NUL;
286
287 if (schecker.Check(scbuf))
288 bend = bpos;
289 else
290 {
291 char savechar = __buf[bend];
292 __buf[bend] = NUL;
293 StyleCodeHighlight(__buf+bbeg, __row, __col+bbeg-__beg, false, DEFATTR);
294 __buf[bend] = savechar;
295
296 bbeg = bend; bend += scpos;
297
298 bool oldusestylies = AA->adat->usestylies;
299 bool oldhighlighturls = CFG->highlighturls;
300 AA->adat->usestylies = false;
301 CFG->highlighturls = false;
302
303 savechar = __buf[bend];
304 __buf[bend] = NUL;
305 StyleCodeHighlight(__buf+bbeg, __row, __col+bbeg-__beg, false, C_SCERROR);
306 __buf[bend] = savechar;
307
308 AA->adat->usestylies = oldusestylies;
309 CFG->highlighturls = oldhighlighturls;
310
311 bbeg = bend;
312 }
313 }
314
315 if (bbeg < bend)
316 StyleCodeHighlight(__buf+bbeg, __row, __col+bbeg-__beg, false, DEFATTR);
317 }
318
319
320 // ------------------------------------------------------------------
321
dispstring(Line * line,uint __row)322 void IEclass::dispstring(Line* line, uint __row)
323 {
324 GFTRK("Editdispstring");
325
326 // Get string length
327 uint _length = strlen(line->txt.c_str());
328
329 // String longer than window width?
330 _test_haltab(_length > (maxcol+1), _length, (maxcol+1));
331 _length = MinV(_length, (maxcol+1));
332
333 // Buffer for translation to visual representation
334 char _buf[EDIT_BUFLEN];
335
336 // Space-pad and nul-terminate the buffer
337 memset(_buf, ' ', maxcol+1);
338 _buf[maxcol+1] = NUL;
339
340 // Copy/translate string into buffer
341 for (uint _pos = 0; _pos < _length; _pos++)
342 {
343 char chr = line->txt[_pos];
344 switch (chr)
345 {
346 case ' ': _buf[_pos] = EDIT->CharSpace(); break;
347 case '\n': _buf[_pos] = EDIT->CharPara(); break;
348 default: _buf[_pos] = chr;
349 }
350 }
351
352 int selected = 0;
353 uint begblock = 0;
354 uint endblock = 0;
355 Line *fbline = NULL;
356 Line *lbline = NULL;
357
358 if (blockcol != -1)
359 {
360 for (Line *ln = findfirstline(); ln; ln = ln->next)
361 {
362 if (ln == currline)
363 {
364 if (selected) { lbline = ln; selected ^= 1; endblock = col; }
365 else { fbline = ln; selected ^= 1; begblock = col; }
366 }
367
368 if (ln->type & GLINE_BLOK)
369 {
370 if (selected) { lbline = ln; selected ^= 1; endblock = blockcol; }
371 else { fbline = ln; selected ^= 1; begblock = blockcol; }
372 }
373
374 if (ln == line) { if (ln == lbline) selected = 1; break; }
375 if (lbline) break;
376 }
377
378 if (selected)
379 {
380 if (fbline == lbline)
381 {
382 if (begblock > endblock)
383 {
384 int temp = begblock;
385 begblock = endblock;
386 endblock = temp;
387 }
388 else if (begblock == endblock)
389 begblock = endblock = 0;
390 }
391 else if (line == fbline)
392 endblock = maxcol+1;
393 else if (line == lbline)
394 begblock = 0;
395 else
396 {
397 begblock = 0;
398 endblock = maxcol+1;
399 }
400 }
401 else
402 begblock = endblock = 0;
403 }
404
405
406 if (0 < begblock)
407 {
408 char savechar = _buf[begblock];
409 _buf[begblock] = NUL;
410
411 if ((CFG->scheckerenabled == GAUTO) && schecker.IsLoaded() &&
412 !(line->type & (GLINE_TAGL|GLINE_QUOT|GLINE_KLUD|GLINE_TEAR|GLINE_ORIG|GLINE_HIDD)))
413 {
414 dispstringsc(_buf, 0, begblock, __row, mincol, savechar);
415 }
416 else
417 StyleCodeHighlight(_buf, __row, mincol, false, DEFATTR);
418
419 _buf[begblock] = savechar;
420 }
421
422 if (begblock < endblock)
423 {
424 bool oldusestylies = AA->adat->usestylies;
425 bool oldhighlighturls = CFG->highlighturls;
426 AA->adat->usestylies = false;
427 CFG->highlighturls = false;
428
429 char savechar = _buf[endblock];
430 _buf[endblock] = NUL;
431 StyleCodeHighlight(_buf+begblock, __row, mincol+begblock, false, C_READA);
432 _buf[endblock] = savechar;
433
434 AA->adat->usestylies = oldusestylies;
435 CFG->highlighturls = oldhighlighturls;
436 }
437
438 if (endblock < (maxcol+1))
439 {
440 if ((CFG->scheckerenabled == GAUTO) && schecker.IsLoaded() &&
441 !(line->type & (GLINE_TAGL|GLINE_QUOT|GLINE_KLUD|GLINE_TEAR|GLINE_ORIG|GLINE_HIDD)))
442 {
443 dispstringsc(_buf, endblock, maxcol+1, __row, mincol+endblock, 0);
444 }
445 else
446 StyleCodeHighlight(_buf+endblock, __row, mincol+endblock, false, DEFATTR);
447 }
448
449 GFTRK(0);
450 }
451 #else // #if defined(GCFG_SPELL_INCLUDED)
452
453
454 // ------------------------------------------------------------------
455
dispstring(const char * __string,uint __row,int attr,Line * line)456 void IEclass::dispstring(const char* __string, uint __row, int attr, Line* line) {
457
458 GFTRK("Editdispstring");
459
460 _test_halt(__string == NULL);
461 _test_haltab(__row > maxrow, __row, maxrow);
462
463 // Get string length
464 uint _length = strlen(__string);
465
466 // String longer than window width?
467 _test_haltab(_length > (maxcol+1), _length, (maxcol+1));
468 _length = MinV(_length, (maxcol+1));
469
470 // Buffer for translation to visual representation
471 char _buf[EDIT_BUFLEN];
472
473 // Space-pad and nul-terminate the buffer
474 memset(_buf, ' ', maxcol+1);
475 _buf[maxcol+1] = NUL;
476
477 // Copy/translate string into buffer
478 if(attr == -1) {
479 char* _bufptr = _buf;
480 uint _position = 0;
481 const char* __str = __string;
482 while(_position < _length) {
483 switch(*__str) {
484 case ' ': *_bufptr = EDIT->CharSpace(); break;
485 case '\n': *_bufptr = EDIT->CharPara(); break;
486 default: *_bufptr = *__str;
487 }
488 _position++;
489 _bufptr++;
490 __str++;
491 }
492 }
493 else {
494 if(_length)
495 memcpy(_buf, __string, _length);
496 }
497
498 // mark selected block
499 if(line and (blockcol != -1)) {
500 int selected = 0;
501 for(Line *ln = findfirstline(); ln and ln != line; ln = ln->next) {
502 if(ln == currline)
503 selected ^= 1;
504 if(ln->type & GLINE_BLOK)
505 selected ^= 1;
506 }
507 if((line->type & GLINE_BLOK) and (line == currline)) {
508 int begblock = ((col < blockcol) ? col : blockcol) - mincol;
509 int endblock = ((col > blockcol) ? col : blockcol) - mincol;
510
511 char savechar = _buf[begblock];
512 _buf[begblock] = NUL;
513 StyleCodeHighlight(_buf, __row, mincol, false, attr);
514 _buf[begblock] = savechar;
515
516 savechar = _buf[endblock];
517 _buf[endblock] = NUL;
518 bool oldusestylies = AA->adat->usestylies;
519 bool oldhighlighturls = CFG->highlighturls;
520 AA->adat->usestylies = false;
521 CFG->highlighturls = false;
522 StyleCodeHighlight(_buf+begblock, __row, mincol+begblock, false, C_READA);
523 AA->adat->usestylies = oldusestylies;
524 CFG->highlighturls = oldhighlighturls;
525 _buf[endblock] = savechar;
526 StyleCodeHighlight(_buf+endblock, __row, mincol+endblock, false, attr);
527 }
528 else if((line->type & GLINE_BLOK) or (line == currline)) {
529 int blockmark = ((line->type & GLINE_BLOK) ? blockcol : col) - mincol;
530
531 char savechar = _buf[blockmark];
532 _buf[blockmark] = NUL;
533 bool oldusestylies = AA->adat->usestylies;
534 bool oldhighlighturls = CFG->highlighturls;
535 if(selected) {
536 AA->adat->usestylies = false;
537 CFG->highlighturls = false;
538 }
539 StyleCodeHighlight(_buf, __row, mincol, false, selected ? C_READA : attr);
540 AA->adat->usestylies = oldusestylies;
541 _buf[blockmark] = savechar;
542 if(not selected) {
543 AA->adat->usestylies = false;
544 CFG->highlighturls = false;
545 }
546 StyleCodeHighlight(_buf+blockmark, __row, mincol+blockmark, false, selected ? attr : C_READA);
547 AA->adat->usestylies = oldusestylies;
548 CFG->highlighturls = oldhighlighturls;
549 }
550 else {
551 bool oldusestylies = AA->adat->usestylies;
552 bool oldhighlighturls = CFG->highlighturls;
553 if(selected) {
554 AA->adat->usestylies = false;
555 CFG->highlighturls = false;
556 }
557 StyleCodeHighlight(_buf, __row, mincol, false, selected ? C_READA : attr);
558 AA->adat->usestylies = oldusestylies;
559 CFG->highlighturls = oldhighlighturls;
560 }
561 }
562 else
563 StyleCodeHighlight(_buf, __row, mincol, false, attr);
564
565 GFTRK(0);
566 }
567 #endif // #if defined(GCFG_SPELL_INCLUDED)
568
569
570 // ------------------------------------------------------------------
571
setcolor(Line * __line)572 void IEclass::setcolor(Line* __line) {
573
574 // Set window attribute
575 if(__line->type & GLINE_HIDD)
576 editwin.text_color(C_READKH);
577 else if(__line->type & GLINE_KLUD)
578 editwin.text_color(C_READK);
579 else if(__line->type & GLINE_TAGL)
580 editwin.text_color(C_READG);
581 else if(__line->type & GLINE_TEAR)
582 editwin.text_color(C_READT);
583 else if(__line->type & GLINE_ORIG)
584 editwin.text_color(C_READO);
585 else if(__line->type & GLINE_QUOT)
586 editwin.text_color(quotecolor(__line->txt.c_str()));
587 else if(__line->type & GLINE_SIGN)
588 editwin.text_color(C_READS);
589 else
590 editwin.text_color(C_READW);
591
592 }
593
594
595 // ------------------------------------------------------------------
596 // Zero-based
597
displine(Line * __line,uint __row)598 void IEclass::displine(Line* __line, uint __row) {
599
600 GFTRK("Editdispline");
601
602 _test_halt(__line == NULL);
603 _test_haltab(__row > maxrow, __row, maxrow);
604
605 // Display line
606 setcolor(__line);
607 #if defined(GCFG_SPELL_INCLUDED)
608 dispstring(__line, __row);
609 #else
610 dispstring(__line->txt.c_str(), __row, -1, __line);
611 #endif
612
613 GFTRK(0);
614 }
615
616
617 // ------------------------------------------------------------------
618 // Zero-based
619
clreol(int __col,int __row)620 void IEclass::clreol(int __col, int __row) {
621
622 GFTRK("Editclreol");
623
624 if(__col == -1)
625 __col = ccol;
626
627 if(__row == -1)
628 __row = crow;
629
630 if((uint)__col <= maxcol)
631 editwin.fill(__row, __col, __row, maxcol, ' ', C_READW);
632
633 GFTRK(0);
634 }
635
636
637 // ------------------------------------------------------------------
638 // Zero-based
639
refresh(Line * __currline,uint __row)640 void IEclass::refresh(Line* __currline, uint __row) {
641
642 GFTRK("Editrefresh");
643
644 _test_halt(__currline == NULL);
645
646 cursoroff();
647
648 // Display as many lines as we can
649 while(__currline and (__row <= maxrow)) {
650 displine(__currline, __row++);
651 __currline = __currline->next;
652 }
653
654 // If we ran out of lines, blank the rest
655 #if defined(GCFG_SPELL_INCLUDED)
656 if (__row <= maxrow)
657 editwin.fill(__row, mincol, __row, maxcol, _box_table(W_BREAD, 1), C_READB|ACSET);
658 if (++__row <= maxrow)
659 editwin.fill(__row, mincol, maxrow, maxcol, ' ', C_READW);
660 #else
661 if(__row <= maxrow) {
662 vchar vbuf[256];
663 for(int c = 0; c < maxcol+1; c++)
664 vbuf[c] = _box_table(W_BREAD, 1);
665 vbuf[maxcol+1] = NUL;
666 wprintvs(__row++, mincol, C_READB|ACSET, vbuf);
667 while(__row <= maxrow)
668 dispstring("", __row++);
669 }
670 #endif
671
672 GFTRK(0);
673 }
674
675
676 // ------------------------------------------------------------------
677
insertlinebelow(Line * __currline,const char * __text,long __batch_mode)678 Line* IEclass::insertlinebelow(Line* __currline, const char* __text, long __batch_mode) {
679
680 GFTRK("Editinsertlinebelow");
681
682 Line* _nextline = new Line(__text ? __text : "");
683 throw_xnew(_nextline);
684
685 if(__currline) {
686
687 _nextline->prev = __currline;
688 _nextline->next = __currline->next;
689 if(_nextline->next)
690 _nextline->next->prev = _nextline;
691 __currline->next = _nextline;
692 }
693
694 Undo->PushItem(EDIT_UNDO_NEW_LINE|batch_mode|__batch_mode, _nextline);
695
696 GFTRK(0);
697
698 return _nextline;
699 }
700
701
702 // ------------------------------------------------------------------
703 // Zero-based
704
downoneline(uint __row)705 int IEclass::downoneline(uint __row) {
706
707 GFTRK("Editdownoneline");
708
709 _test_haltab(__row > maxrow, __row, maxrow);
710
711 thisrow++;
712
713 if(__row == maxrow)
714 scrollup(mincol, minrow, maxcol, maxrow);
715 else
716 __row++;
717
718 gotorowcol(mincol, __row);
719
720 GFTRK(0);
721
722 return __row;
723 }
724
725
726 // ------------------------------------------------------------------
727
GoEOL()728 void IEclass::GoEOL()
729 {
730 GFTRK("EditGoEOL");
731
732 // Move cursor to the last char on the line
733 if (mincol != (col = currline->txt.length()))
734 {
735 if ((currline->txt[col-1] == '\n') || (currline->txt[col-1] == ' '))
736 --col;
737 }
738
739 // String must not be longer than the window width
740 _test_haltab(col > maxcol, col, maxcol);
741
742 if(blockcol != -1)
743 displine(currline, row);
744
745 GFTRK(0);
746 }
747
748
749 // ------------------------------------------------------------------
750
GoUp()751 void IEclass::GoUp() {
752
753 GFTRK("EditGoUp");
754
755 _test_haltab(row < minrow, row, minrow);
756
757 if(currline->prev) {
758
759 currline = currline->prev;
760 thisrow--;
761
762 if(row == minrow) {
763 scrolldown(mincol, row, maxcol, maxrow);
764 if(blockcol != -1)
765 displine(currline->next, row+1);
766 displine(currline, row);
767 }
768 else {
769 row--;
770 if(blockcol != -1) {
771 displine(currline->next, row+1);
772 displine(currline, row);
773 }
774 }
775
776 if((col+1) > currline->txt.length())
777 GoEOL();
778 }
779
780 GFTRK(0);
781 }
782
783
784 // ------------------------------------------------------------------
785
GoDown()786 void IEclass::GoDown() {
787
788 GFTRK("EditGoDown");
789
790 _test_haltab(row > maxrow, row, maxrow);
791
792 if(currline->next) {
793
794 currline = currline->next;
795 thisrow++;
796
797 if(row == maxrow) {
798 scrollup(mincol, minrow, maxcol, maxrow);
799 if(blockcol != -1)
800 displine(currline->prev, row-1);
801 displine(currline,row);
802 }
803 else {
804 row++;
805 if(blockcol != -1) {
806 displine(currline->prev, row-1);
807 displine(currline, row);
808 }
809 }
810
811 if((col+1) > currline->txt.length())
812 GoEOL();
813 }
814
815 GFTRK(0);
816 }
817
818
819 // ------------------------------------------------------------------
820
GoLeft()821 void IEclass::GoLeft() {
822
823 GFTRK("EditGoLeft");
824
825 _test_haltab(col < mincol, col, mincol);
826
827 if(col == mincol) {
828 if(currline->prev) {
829 GoUp();
830 GoEOL();
831 if((col != mincol) and (currline->txt.c_str()[col] == NUL))
832 col--;
833 }
834 }
835 else
836 col--;
837
838 if(blockcol != -1)
839 displine(currline, row);
840
841 GFTRK(0);
842 }
843
844
845 // ------------------------------------------------------------------
846
GoRight()847 void IEclass::GoRight() {
848
849 GFTRK("EditGoRight");
850
851 _test_haltab(col > maxcol, col, maxcol);
852
853 if((col == maxcol) or ((col+1) >= currline->txt.length())) {
854 if(currline->next != NULL) {
855 GoDown();
856 col = mincol;
857 }
858 }
859 else
860 col++;
861
862 if(blockcol != -1)
863 displine(currline, row);
864
865 GFTRK(0);
866 }
867
868
869 // ------------------------------------------------------------------
870
wrapit(Line ** __currline,uint * __curr_col,uint * __curr_row,bool __display)871 Line* IEclass::wrapit(Line** __currline, uint* __curr_col, uint* __curr_row, bool __display) {
872
873 _test_halt(__currline == NULL);
874 _test_halt(*__currline == NULL);
875
876 int scroll = 0;
877
878 uint _quotelen;
879 char _quotebuf[MAXQUOTELEN];
880 *_quotebuf = NUL;
881
882 uint _curscol = *__curr_col;
883 uint _cursrow = *__curr_row;
884
885 uint _thisrow = *__curr_row;
886 Line* _thisline = *__currline;
887 Line* _lastadded = _thisline;
888
889 bool _wrapped_above = false;
890
891 // Start wrapping from the current line onwards
892 while(_thisline) {
893
894 // Length of this line
895 uint _thislen = _thisline->txt.length();
896
897 setlinetype(_thisline);
898 uint _wrapmargin = (_thisline->type & GLINE_QUOT) ? marginquotes : margintext;
899
900 // Does this line need wrapping?
901 if((_thislen > _wrapmargin) or ((_thislen == _wrapmargin) and not ((_thisline->txt[_thislen-1] == ' ') or (_thisline->txt[_thislen-1] == '\n')))) {
902
903 // Reset quote string length
904 _quotelen = 0;
905
906 // Is this line quoted?
907 if(_thisline->type & GLINE_QUOT) {
908
909 // Get quote string and length
910 GetQuotestr(_thisline->txt.c_str(), _quotebuf, &_quotelen);
911 }
912
913 // wrapmargin = 40
914 // 1111111111222222222233333333334444444444
915 // 01234567890123456789012345678901234567890123456789
916 // --------------------------------------------------
917 // v- wrapptr
918 // Case 1: this is another test with a bit of text to wrap.
919 // Case 2: this is a test with a line that need wrapping.
920 // Case 3: thisxisxaxtestxwithxaxlinexthatxneedxwrapping.
921 // Case 4: >thisxisxaxtestxwithxaxlinexthatxneedxwrapping.
922
923 // Point to the last char inside the margin
924 int _wrappos = _wrapmargin - 1;
925
926 // Locate the word to be wrapped
927
928 // Did we find a space?
929 if(_thisline->txt[_wrappos] == ' ') {
930
931 // Case 1: A space was found as the last char inside the margin
932 //
933 // Now we must locate the first word outside the margin.
934 // NOTE: Leading spaces to this word will be nulled out!
935
936 // Begin at the first char outside the margin
937 if(_wrappos <= maxcol)
938 _wrappos++;
939 }
940 else {
941
942 // Case 2: A non-space was found as the last char inside the margin
943 //
944 // Now we must locate the beginning of the word we found.
945
946 // Keep copy of original pointer
947 int _atmargin = _wrappos;
948
949 // Search backwards until a space or the beginning of the line is found
950 while((_wrappos > _quotelen) and (_thisline->txt[_wrappos-1] != ' '))
951 _wrappos--;
952
953 // Check if we hit leading spaces
954 int _spacepos = _wrappos;
955 while(_spacepos > 0) {
956 _spacepos--;
957 if(_thisline->txt[_spacepos] != ' ')
958 break;
959 }
960
961 // Did we search all the way back to the beginning of the line?
962 if((_wrappos == _quotelen) or (_thisline->txt[_spacepos] == ' ')) {
963
964 // Case 3: There are no spaces within the margin or we hit leading spaces
965
966 // We have to break it up at the margin
967 _wrappos = _atmargin;
968 }
969 }
970
971 // The wrappos now points to the location to be wrapped or NUL
972
973 // Is the line hard-terminated?
974 if(_thisline->txt.find('\n', _wrappos) != _thisline->txt.npos) {
975
976 // The line is hard-terminated.
977
978 // Copy the quote string, if any, to the new line first
979 std::string _wrapbuf = _quotebuf;
980
981 // Copy/append the wrapped part to the new line
982 _wrapbuf += _thisline->txt.substr(_wrappos);
983
984 // The wrapped part must be placed on a new line below.
985 Line* _wrapline = _lastadded = insertlinebelow(_thisline, _wrapbuf.c_str(), BATCH_MODE);
986
987 // Saves pointer to a line where from the wrapped part was copied, its begining
988 // and length. While in Undo, appends the copied part to previous line and deletes
989 // it on current, moving the rest over deleted.
990 Undo->PushItem(EDIT_UNDO_WRAP_TEXT|BATCH_MODE, _thisline, _quotelen, _wrapbuf.length() - _quotelen);
991
992 _wrapline->type = _thisline->type;
993 // Make sure the type of the line is correct
994 setlinetype(_wrapline);
995
996 if(__display) {
997
998 // Is there at least one line below this one?
999 if(_thisrow < maxrow) {
1000
1001 // Scroll the lines below to make room for the new line
1002 --scroll;
1003
1004 // Display the new line
1005 if(_wrapline->txt.length() <= (maxcol+1))
1006 displine(_wrapline, _thisrow+1);
1007 }
1008 }
1009 }
1010 else {
1011
1012 // The line is not hard-terminated
1013 //
1014 // The wrapped part must be inserted into the next line
1015
1016 // Indicate that one or more lines were wrapped in this way
1017 _wrapped_above = true;
1018
1019 // Pointer to the next line
1020 Line* _nextline = _thisline->next;
1021
1022 // Copy the quote string, if any, to the new line first
1023 std::string _wrapbuf = _quotebuf;
1024
1025 // Copy/append the wrapped part to the new line
1026 _wrapbuf += _thisline->txt.substr(_wrappos);
1027
1028 // Flag to indicate if a new line was added below
1029 bool _line_added_below = false;
1030
1031 // Is there no next line or is the next line quoted?
1032 if((_nextline == NULL) or (_nextline->type & GLINE_QUOT)) {
1033
1034 // The wrapped part must be placed on a new line below
1035 _lastadded = _nextline = insertlinebelow(_thisline, _wrapbuf.c_str(), BATCH_MODE);
1036 _line_added_below = true;
1037
1038 Undo->PushItem(EDIT_UNDO_WRAP_TEXT|BATCH_MODE, _thisline, _quotelen, _wrapbuf.length() - _quotelen);
1039 }
1040 else {
1041
1042 _nextline->txt.insert(0, _wrapbuf);
1043 if(_quotelen)
1044 Undo->PushItem(EDIT_UNDO_INS_TEXT|BATCH_MODE, _nextline, 0, _quotelen);
1045 Undo->PushItem(EDIT_UNDO_WRAP_TEXT|BATCH_MODE, _thisline, _quotelen, _wrapbuf.length() - _quotelen);
1046 }
1047
1048 // Make sure the type of the line is correct
1049 setlinetype(_nextline);
1050
1051 if(__display) {
1052
1053 // Is there at least one line below this one?
1054 if(_line_added_below and (_thisrow < maxrow)) {
1055
1056 // Scroll the lines below to make room for the new line
1057 --scroll;
1058 }
1059
1060 // Display the new/wrapped line
1061 if((_thisrow+1) <= maxrow and _nextline->txt.length() <= (maxcol+1))
1062 displine(_nextline, _thisrow+1);
1063 }
1064 }
1065
1066 // Truncate at the wrapping location
1067 _thisline->txt.erase(_wrappos);
1068
1069 // Was this line quoted?
1070 if (_quotelen)
1071 {
1072 // Trim spaces off the end of the line
1073 int _trimpos = _wrappos - 1;
1074 if (isspace(_thisline->txt[_trimpos]))
1075 {
1076 while (_trimpos > 0 and isspace(_thisline->txt[_trimpos-1]))
1077 _trimpos--;
1078 if(_quotelen and (_trimpos < _quotelen))
1079 _trimpos++;
1080 Undo->PushItem(EDIT_UNDO_OVR_CHAR|BATCH_MODE, _thisline, _trimpos);
1081 _thisline->txt.erase(_trimpos);
1082 }
1083 else
1084 {
1085 Undo->PushItem(EDIT_UNDO_INS_CHAR|BATCH_MODE, _thisline, _trimpos + 1);
1086 }
1087
1088 // Append a new linefeed
1089 _thisline->txt += "\n";
1090 }
1091
1092 // Make sure the line type still is correct
1093 setlinetype(_thisline);
1094
1095 if(__display) {
1096
1097 // Display this line after wrapping
1098 if(_thisrow <= maxrow)
1099 displine(_thisline, _thisrow);
1100 }
1101
1102 _thislen = _thisline->txt.length();
1103
1104 // If we are on the cursor line, check if the cursor char was wrapped
1105 if((_thisrow == _cursrow) and (_thislen <= _curscol)) {
1106 _curscol = _quotelen + ((_curscol > _wrappos) ? _curscol-_wrappos : 0);
1107 _cursrow++;
1108 UndoItem* i = Undo->last_item;
1109 do { i = i->prev; } while(i->action & BATCH_MODE);
1110 if(i->col.num >= i->line->txt.length()) {
1111 i->action |= PREV_LINE;
1112 i->col.sav = i->col.num;
1113 i->col.num = _curscol;
1114 }
1115 }
1116 }
1117
1118 // If this line is hard-terminated, we have finished wrapping
1119 // Unless the next line has grown too large
1120 if(_thisline->txt.find('\n') != _thisline->txt.npos) {
1121 if(_thisline->next == NULL)
1122 break;
1123 _wrapmargin = (_thisline->next->type & GLINE_QUOT) ? marginquotes : margintext;
1124 if(_thisline->next->txt.length() <= _wrapmargin)
1125 break;
1126 }
1127 else if(_thisline->type & GLINE_QUOT) {
1128 Undo->PushItem(EDIT_UNDO_INS_TEXT|BATCH_MODE, _thisline, _thisline->txt.length(), 1);
1129 _thisline->txt += '\n';
1130 }
1131
1132 // Go to the next line
1133 _thisline = _thisline->next;
1134 _thisrow++;
1135 }
1136
1137 if(__display) {
1138
1139 // Display the current line after wrapping
1140 if(*__curr_row <= maxrow)
1141 displine(*__currline, *__curr_row);
1142
1143 // Was the line or lines above wrapped?
1144 if(_wrapped_above) {
1145
1146 // Display the last line in the paragraph
1147 if((_thisrow <= maxrow) and _thisline)
1148 displine(_thisline, _thisrow);
1149 }
1150 }
1151
1152 // Move to the next line if the cursor was wrapped
1153 if(_cursrow != *__curr_row) {
1154 int i = *__curr_row;
1155 while ((i++ != _cursrow) and (__currline != NULL))
1156 *__currline = (*__currline)->next;
1157 if(_cursrow > maxrow) {
1158 _cursrow = maxrow;
1159 if(__display) {
1160 ++scroll;
1161 if(scroll > 0) {
1162 scrollup(mincol, minrow, maxcol, maxrow);
1163 scroll = 0;
1164 }
1165 displine(*__currline, *__curr_row);
1166 }
1167 }
1168 }
1169
1170 if(__display and (scroll != 0)) {
1171 if((*__currline)->next != NULL)
1172 refresh((*__currline)->next, _cursrow+1);
1173 else
1174 refresh(*__currline, _cursrow);
1175 }
1176
1177 // Update cursor position
1178 *__curr_row = _cursrow;
1179 *__curr_col = _curscol;
1180
1181 GFTRK(0);
1182
1183 return _lastadded;
1184 }
1185
1186
1187 // ------------------------------------------------------------------
1188
wrapdel(Line ** __currline,uint * __curr_col,uint * __curr_row,bool __display)1189 Line* IEclass::wrapdel(Line** __currline, uint* __curr_col, uint* __curr_row, bool __display) {
1190
1191 GFTRK("Editwrapdel");
1192
1193 Line *tmp = wrapit(__currline, __curr_col, __curr_row, __display);
1194
1195 GFTRK(0);
1196
1197 return tmp;
1198 }
1199
1200
1201 // ------------------------------------------------------------------
1202
wrapins(Line ** __currline,uint * __curr_col,uint * __curr_row,bool __display)1203 Line* IEclass::wrapins(Line** __currline, uint* __curr_col, uint* __curr_row, bool __display) {
1204
1205 GFTRK("Editwrapins");
1206
1207 Line *tmp = wrapit(__currline, __curr_col, __curr_row, __display);
1208
1209 GFTRK(0);
1210
1211 return tmp;
1212 }
1213
1214
1215 // ------------------------------------------------------------------
1216
insertchar(char __ch)1217 void IEclass::insertchar(char __ch) {
1218
1219 GFTRK("Editinsertchar");
1220
1221 uint _currline_len = currline->txt.length();
1222 #ifndef NDEBUG
1223 _test_haltab(col > _currline_len, col, _currline_len);
1224 #endif
1225 // Insert or overwrite the char, replacing the block if any
1226 if((selecting ? (BlockCut(true), batch_mode = BATCH_MODE) : false) or
1227 (col >= _currline_len) or (currline->txt[col] == '\n') or insert) {
1228 if(not isspace(__ch) and (col == mincol)) {
1229 // if previous line was wrapped on non-space character
1230 if(currline->prev and not currline->prev->txt.empty() and
1231 (currline->prev->txt.find('\n') == currline->prev->txt.npos) and
1232 not isspace(currline->prev->txt[currline->prev->txt.length()-1])) {
1233 if(not batch_mode) {
1234 Undo->PushItem(EDIT_UNDO_VOID);
1235 batch_mode = BATCH_MODE;
1236 }
1237 GoUp();
1238 GoEOL();
1239 }
1240 }
1241 Undo->PushItem(EDIT_UNDO_INS_CHAR|batch_mode);
1242 currline->txt.insert(col, 1, __ch);
1243 } else {
1244 Undo->PushItem(EDIT_UNDO_OVR_CHAR|batch_mode);
1245 currline->txt[col] = __ch;
1246 }
1247 batch_mode = BATCH_MODE;
1248
1249 // Make sure the line type still is correct
1250 setlinetype(currline);
1251
1252 // Move cursor
1253 col++;
1254
1255 wrapins(&currline, &col, &row);
1256
1257 // Adjust cursor position and display if necessary
1258 if(col > maxcol) {
1259 if(currline->next) {
1260 currline = currline->next;
1261 col = mincol;
1262 row++;
1263 if(row > maxrow) {
1264 row = maxrow;
1265 scrollup(mincol, minrow, maxcol, maxrow);
1266 displine(currline, row);
1267 }
1268 }
1269 else {
1270 col = maxcol;
1271 }
1272 }
1273
1274 gotorowcol(col, row);
1275
1276 GFTRK(0);
1277 }
1278
1279
1280 // ------------------------------------------------------------------
1281
DelChar()1282 void IEclass::DelChar()
1283 {
1284 GFTRK("EditDelChar");
1285
1286 Line* _thisline = currline;
1287 Line* _nextline = currline->next;
1288 uint _thislen = _thisline->txt.length();
1289
1290 if (!_nextline && (col == _thislen - 1) && (_thisline->txt[col] == '\n'))
1291 {
1292 GFTRK(0);
1293 return;
1294 }
1295
1296 // Cannot delete at or beyond the nul-terminator
1297 if(col < _thislen) {
1298 Undo->PushItem(EDIT_UNDO_DEL_CHAR|batch_mode);
1299 _thisline->txt.erase(col, 1);
1300 batch_mode = BATCH_MODE;
1301 }
1302 else if(col and (col == _thislen) and _nextline) {
1303 GFTRK(0);
1304 if(not batch_mode) {
1305 Undo->PushItem(EDIT_UNDO_VOID);
1306 batch_mode = BATCH_MODE;
1307 }
1308 GoRight();
1309 DelChar();
1310 return;
1311 }
1312
1313 // Did we delete the last char on the line or
1314 // was the cursor at or beyond the nul-terminator?
1315 // And is there a next line at all?
1316 if(((col+1) >= _thislen) and _nextline) {
1317
1318 // Join the next line to this line
1319
1320 // Is the next line quoted?
1321 // And is the cursor column non-zero?
1322 uint _quotelen = 0;
1323 if((_nextline->type & GLINE_QUOT) and col) {
1324
1325 // Get quote string length
1326 char _quotebuf[MAXQUOTELEN];
1327 GetQuotestr(_nextline->txt.c_str(), _quotebuf, &_quotelen);
1328 }
1329
1330 // Copy the next line's text to this line without quote string
1331 const char *_nexttext = _nextline->txt.c_str()+_quotelen;
1332 _thisline->txt += _nexttext + (col ? strspn(_nexttext, " ") : 0);
1333
1334 Undo->PushItem(EDIT_UNDO_CUT_TEXT|batch_mode, _thisline, col);
1335
1336 // Relink this line
1337 _thisline->next = _nextline->next;
1338 if(_thisline->next)
1339 _thisline->next->prev = _thisline;
1340
1341 Undo->PushItem(EDIT_UNDO_DEL_LINE|BATCH_MODE, _nextline);
1342 }
1343 else if((_thislen > 1) and (_thisline->txt.c_str()[_thislen-2] != '\n') and _nextline and not (_nextline->type & GLINE_QUOT)) {
1344
1345 _thisline->txt += _nextline->txt.c_str();
1346
1347 Undo->PushItem(EDIT_UNDO_CUT_TEXT|batch_mode, _thisline, _thislen-1);
1348
1349 // Relink this line
1350 _thisline->next = _nextline->next;
1351 if(_thisline->next)
1352 _thisline->next->prev = _thisline;
1353
1354 Undo->PushItem(EDIT_UNDO_DEL_LINE|BATCH_MODE, _nextline);
1355 }
1356 batch_mode = BATCH_MODE;
1357
1358 // Make sure the line type still is correct
1359 setlinetype(_thisline);
1360
1361 // Rewrap this line
1362 bool display = make_bool_not(row > maxrow / 2);
1363 wrapdel(&currline, &col, &row, display);
1364 if(display) {
1365 refresh(currline, row);
1366 }
1367 else {
1368 // Refresh the display
1369 Line* _topline = findtopline();
1370 refresh(_topline, minrow);
1371 }
1372
1373 GFTRK(0);
1374 }
1375
1376
1377 // ------------------------------------------------------------------
1378
DelLeft()1379 void IEclass::DelLeft() {
1380
1381 GFTRK("EditDelLeft");
1382
1383 // Cannot backspace from the first column on the first line in the msg
1384 if(currline->prev == NULL)
1385 if(col == mincol) {
1386 GFTRK(0);
1387 return;
1388 }
1389
1390 // Go left(/up) and delete the character there
1391 if(not batch_mode) {
1392 if(col == mincol)
1393 Undo->PushItem(EDIT_UNDO_VOID|PREV_LINE, currline->prev);
1394 else
1395 Undo->PushItem(EDIT_UNDO_VOID);
1396 batch_mode = BATCH_MODE;
1397 }
1398 GoLeft();
1399 DelChar();
1400
1401 GFTRK(0);
1402 }
1403
1404
1405 // ------------------------------------------------------------------
1406
GoWordLeft()1407 void IEclass::GoWordLeft() {
1408
1409 GFTRK("EditGoWordLeft");
1410
1411 if(col == 0) {
1412 if(currline->prev) {
1413 GoUp();
1414 GoEOL();
1415 }
1416 }
1417 else {
1418
1419 col--;
1420
1421 if(not isxalnum(currline->txt[col])) {
1422 while(not isxalnum(currline->txt[col]) and (col > 0))
1423 col--;
1424 while(isxalnum(currline->txt[col]) and (col > 0))
1425 col--;
1426 }
1427 else {
1428 while(isxalnum(currline->txt[col]) and (col > 0))
1429 col--;
1430 }
1431
1432 if(col != 0)
1433 col++;
1434
1435 if(blockcol != -1)
1436 displine(currline, row);
1437 }
1438
1439 GFTRK(0);
1440 }
1441
1442
1443 // ------------------------------------------------------------------
1444
GoWordRight()1445 void IEclass::GoWordRight() {
1446
1447 GFTRK("EditGoWordRight");
1448
1449 if((col >= currline->txt.length()) or (currline->txt[col] == '\n')) {
1450 if(currline->next) {
1451 GoDown();
1452 col = mincol;
1453 }
1454 }
1455 else {
1456 size_t len = currline->txt.length();
1457 if(not isxalnum(currline->txt[col])) {
1458 while(not isxalnum(currline->txt[col]) and ((col+1) <= len))
1459 col++;
1460 }
1461 else {
1462 while(isxalnum(currline->txt[col]) and ((col+1) <= len))
1463 col++;
1464 while(not isxalnum(currline->txt[col]) and ((col+1) <= len))
1465 col++;
1466 }
1467
1468 if(currline->txt[col-1] == '\n')
1469 col--;
1470
1471 if(len == col) {
1472 if(currline->next) {
1473 GoDown();
1474 col = 0;
1475 }
1476 else
1477 col--;
1478 }
1479 }
1480
1481 if (blockcol != -1)
1482 displine(currline, row);
1483
1484 GFTRK(0);
1485 }
1486
1487
1488 // ------------------------------------------------------------------
1489
Newline()1490 void IEclass::Newline() {
1491
1492 GFTRK("EditNewline");
1493
1494 // Pointer to the split position
1495 int _splitpos = col;
1496
1497 // Buffer for the second part of the split line
1498 std::string _splitbuf;
1499
1500 // If the split line was quoted, get the quotestring
1501 // But do not get it if the cursor points to a linefeed or is
1502 uint _quotelen;
1503 char _quotebuf[MAXQUOTELEN];
1504 GetQuotestr(currline->txt.c_str(), _quotebuf, &_quotelen);
1505
1506 // Eliminate the quotestring if
1507 // - the cursor points to a linefeed or
1508 // - the cursor is located inside the quotestring
1509 if(_quotelen and ((currline->txt.length() == col) or (currline->txt[_splitpos] == '\n') or (col < _quotelen))) {
1510 *_quotebuf = NUL;
1511 _quotelen = 0;
1512 }
1513
1514 // Append the second part to the split buffer
1515 _splitbuf = _quotebuf;
1516 _splitbuf += currline->txt.substr(_splitpos);
1517
1518 Undo->PushItem(EDIT_UNDO_INS_TEXT|batch_mode, currline, col, 1);
1519 batch_mode = BATCH_MODE;
1520
1521 // Copy linefeed+nul to the split position
1522 currline->txt.erase(_splitpos);
1523 currline->txt += "\n";
1524
1525 // Re-type and display the split line
1526 setlinetype(currline);
1527 displine(currline, row);
1528
1529 uint _quotelen1 = 0;
1530 char _quotebuf1[MAXQUOTELEN] = "";
1531 if(currline->next != NULL) {
1532 GetQuotestr(currline->next->txt.c_str(), _quotebuf1, &_quotelen1);
1533 }
1534
1535 if((_splitbuf.find('\n') != _splitbuf.npos) or (currline->next == NULL) or not strieql(_quotebuf, _quotebuf1)) {
1536 // Insert a new line below, set the line text to the split-off part
1537 currline = insertlinebelow(currline, _splitbuf.c_str());
1538
1539 // --v--
1540 // This line would be wrapped
1541 // This line would be
1542 // wrapped
1543
1544 Undo->PushItem(EDIT_UNDO_WRAP_TEXT|BATCH_MODE, currline->prev, _quotelen, _splitbuf.length() - _quotelen);
1545 }
1546 else {
1547 currline = currline->next;
1548
1549 currline->txt.insert(_quotelen1, _splitbuf.substr(_quotelen1));
1550
1551 Undo->PushItem(EDIT_UNDO_WRAP_TEXT|BATCH_MODE, currline->prev, _quotelen1, _splitbuf.length() - _quotelen1);
1552 }
1553 setlinetype(currline);
1554
1555 // Move down the cursor
1556 col = 0;
1557 row = downoneline(row);
1558
1559 // Scroll the remaining lines if necessary
1560 if(row < maxrow)
1561 scrolldown(mincol, row, maxcol, maxrow);
1562
1563 // Rewrap the split-off line
1564 wrapdel(&currline, &col, &row, true);
1565 refresh(currline, row);
1566
1567 GFTRK(0);
1568 }
1569
1570
1571 // ------------------------------------------------------------------
1572
CopyAboveChar()1573 void IEclass::CopyAboveChar() {
1574
1575 GFTRK("EditCopyAboveChar");
1576
1577 char _ch = ' ';
1578 if(currline->prev) {
1579 uint _len = currline->prev->txt.length();
1580 if(_len and _len > col)
1581 _ch = currline->prev->txt[col];
1582 if(_ch == '\n')
1583 _ch = ' ';
1584 }
1585 insertchar(_ch);
1586
1587 GFTRK(0);
1588 }
1589
1590
1591 // ------------------------------------------------------------------
1592
DupLine()1593 void IEclass::DupLine() {
1594
1595 GFTRK("EditDupLine");
1596
1597 Undo->PushItem(EDIT_UNDO_VOID);
1598 batch_mode = BATCH_MODE;
1599
1600 Line* _nextline = insertlinebelow(currline, currline->txt.c_str(), batch_mode);
1601 _nextline->type = currline->type & ~GLINE_BLOK;
1602 _nextline->color = currline->color;
1603 _nextline->kludge = currline->kludge;
1604 refresh(currline, row);
1605 GoDown();
1606
1607 GFTRK(0);
1608 }
1609
1610
1611 // ------------------------------------------------------------------
1612 // PageUp behavior:
1613 //
1614 // ... The top line becomes the bottom line.
1615 // ... Always remain at cursor row, except if we can't page up.
1616 // ... If we can't page up, move the cursor row to the top line.
1617
GoPgUp()1618 void IEclass::GoPgUp() {
1619
1620 GFTRK("EditGoPgUp");
1621
1622 // Not at the first line in msg?
1623 if(currline->prev) {
1624
1625 // Count lines
1626 int _count = row;
1627
1628 // Move to the top line currently displayed
1629 Line* _topline = currline;
1630 while(_count and _topline->prev) {
1631 _topline = _topline->prev;
1632 _count--;
1633 }
1634
1635 // The count must be zero at this point!
1636 _test_haltab(_count, _count, _count);
1637
1638 // At we already fully at the top?
1639 if(_topline->prev == NULL) {
1640
1641 // Yes, so move the cursor row to the top line
1642 row = minrow;
1643 currline = _topline;
1644 if(blockcol != -1)
1645 refresh(currline, row);
1646 }
1647 else {
1648
1649 // We are not at the top, so continue with paging
1650
1651 // Move a full page of lines, if possible
1652 _count = maxrow;
1653 while(_count and _topline->prev) {
1654 _topline = _topline->prev;
1655 _count--;
1656 }
1657
1658 // Set the current line
1659 _count = row;
1660 currline = _topline;
1661 while(_count--)
1662 currline = currline->next;
1663
1664 // Refresh display
1665 refresh(_topline, minrow);
1666 }
1667 }
1668 else {
1669 GoTopMsg();
1670 }
1671
1672 if(col+1 > currline->txt.length())
1673 GoEOL();
1674
1675 GFTRK(0);
1676 }
1677
1678
1679 // ------------------------------------------------------------------
1680 // PageDown behavior:
1681 //
1682 // ... The bottom line becomes the top line.
1683 // ... Always remain at cursor row, except if at the last line.
1684 // ... If at the last line, move cursor to the last window line and
1685 // display the lines above.
1686 // ... If there are too few lines to display after the page down, the
1687 // rest are displayed blank.
1688
GoPgDn()1689 void IEclass::GoPgDn() {
1690
1691 GFTRK("EditGoPgDn");
1692
1693 // Not at the last line in the msg?
1694 if(currline->next) {
1695
1696 // Go down to the last displayed line in the window
1697 uint _newrow = row, _oldrow = row;
1698 Line *oldcurrline = currline;
1699 while((_newrow < maxrow) and currline->next) {
1700 currline = currline->next;
1701 _newrow++;
1702 }
1703
1704 // If there are more lines after the last line, start displaying from the top
1705 if(currline->next) {
1706 Line *_topline = currline;
1707
1708 // Set current line
1709 _newrow = 0;
1710 while((_newrow < row) and currline->next) {
1711 currline = currline->next;
1712 _newrow++;
1713 }
1714
1715 // Move cursor row if necessary
1716 if(_newrow < row)
1717 row = _newrow;
1718
1719 refresh(_topline, minrow);
1720 }
1721 else {
1722 row = _newrow;
1723 refresh(oldcurrline, _oldrow);
1724 }
1725 }
1726 else {
1727 GoBotMsg();
1728 }
1729
1730 if(col+1 > currline->txt.length())
1731 GoEOL();
1732
1733 GFTRK(0);
1734 }
1735
1736
1737 // ------------------------------------------------------------------
1738
Tab()1739 void IEclass::Tab() {
1740
1741 GFTRK("EditTab");
1742
1743 int tabsz = CFG->disptabsize ? CFG->disptabsize : 1;
1744
1745 // Move to the next tab position
1746 do {
1747 if(insert)
1748 insertchar(' ');
1749 else if(currline->txt[col] != '\n')
1750 GoRight();
1751 else
1752 break;
1753 } while(col % tabsz);
1754
1755 GFTRK(0);
1756 }
1757
1758
1759 // ------------------------------------------------------------------
1760
ReTab()1761 void IEclass::ReTab() {
1762
1763 GFTRK("EditTabReverse")
1764
1765 int tabsz = CFG->disptabsize ? CFG->disptabsize : 1;
1766
1767 // Move to the next tab position
1768 do {
1769 if(not col)
1770 break;
1771
1772 if(insert)
1773 DelLeft();
1774 else
1775 GoLeft();
1776 } while(col % tabsz);
1777
1778 GFTRK(0);
1779 }
1780
1781
1782 // ------------------------------------------------------------------
1783
DeleteEOL()1784 void IEclass::DeleteEOL() {
1785
1786 GFTRK("EditDeleteEOL");
1787
1788 bool _has_linefeed = (currline->txt.find('\n') != currline->txt.npos);
1789
1790 Undo->PushItem(EDIT_UNDO_DEL_TEXT, currline);
1791
1792 currline->txt.erase(col);
1793
1794 if(_has_linefeed) {
1795 Undo->PushItem(EDIT_UNDO_INS_CHAR|BATCH_MODE);
1796 currline->txt += "\n";
1797 }
1798
1799 clreol();
1800
1801 GFTRK(0);
1802 }
1803
1804
1805 // ------------------------------------------------------------------
1806
DeleteSOL()1807 void IEclass::DeleteSOL() {
1808
1809 GFTRK("EditDeleteSOL");
1810
1811 if(col == 0) {
1812 GFTRK(0);
1813 return;
1814 }
1815
1816 int _oldcol = col;
1817
1818 col = 0;
1819
1820 Undo->PushItem(EDIT_UNDO_DEL_TEXT, currline, col, _oldcol);
1821 currline->txt.erase(col, _oldcol);
1822
1823 wrapdel(&currline, &col, &row);
1824
1825 GFTRK(0);
1826 }
1827
1828
1829 // ------------------------------------------------------------------
1830
deleteline(bool zapquotesbelow)1831 void IEclass::deleteline(bool zapquotesbelow) {
1832
1833 GFTRK("Editdeleteline");
1834
1835 bool done = false;
1836
1837 do {
1838
1839 // Break if need to zap quotes, but the current line is not quote and is not empty
1840 if(zapquotesbelow and not ((currline->type & GLINE_QUOT) or isempty(currline)))
1841 break;
1842
1843 // Pointer to the deleted line
1844 Line* _deletedline = currline;
1845
1846 // If last line to be deleted delete to EOL and exit
1847 if(currline->next == NULL) {
1848 if(isempty(currline))
1849 break;
1850 insertlinebelow(currline, "", batch_mode);
1851 batch_mode = BATCH_MODE;
1852 done = true;
1853 }
1854
1855 // Pointers to the previous and next lines
1856 Line* _prevline = currline->prev;
1857 Line* _nextline = currline->next;
1858
1859 Undo->PushItem(EDIT_UNDO_PUSH_LINE|batch_mode);
1860
1861 // Set the new current line to the next line (which may be NULL!)
1862 currline = _nextline;
1863
1864 if(currline == NULL) {
1865 currline = _prevline;
1866 currline->next = NULL;
1867 _prevline = _prevline ? _prevline->prev : NULL;
1868 }
1869
1870 // Link the new current line to the previous line
1871 currline->prev = _prevline;
1872
1873 // Link the previous line to this line
1874 if(_prevline)
1875 _prevline->next = currline;
1876
1877 if(_deletedline->type & GLINE_BLOK) {
1878 blockcol = -1;
1879 _deletedline->type &= ~GLINE_BLOK;
1880 }
1881
1882 // Link the deleted line to the killbuffer
1883 if(Edit__killbuf) {
1884 Edit__killbuf->next = _deletedline;
1885 _deletedline->prev = Edit__killbuf;
1886 Edit__killbuf = _deletedline;
1887 }
1888 else {
1889 Edit__killbuf = _deletedline;
1890 Edit__killbuf->prev = NULL;
1891 }
1892 Edit__killbuf->next = NULL;
1893
1894 // Move the cursor to EOL if necessary
1895 if(col+1 > currline->txt.length())
1896 GoEOL();
1897
1898 if(not zapquotesbelow)
1899 break;
1900
1901 batch_mode = BATCH_MODE;
1902
1903 } while(not done);
1904
1905 // Refresh display from cursor row
1906 refresh(currline, row);
1907
1908 GFTRK(0);
1909 }
1910
1911
1912 // ------------------------------------------------------------------
1913
UnDelete(bool before)1914 void IEclass::UnDelete(bool before) {
1915
1916 GFTRK("EditUnDelete");
1917
1918 // If there are deleted lines
1919 if(Edit__killbuf) {
1920
1921 Line* _prevline = Edit__killbuf->prev;
1922 bool down = false;
1923
1924 if(before) {
1925 Edit__killbuf->prev = currline ? currline->prev : NULL;
1926 Edit__killbuf->next = currline;
1927 }
1928 else {
1929 Edit__killbuf->prev = currline;
1930 Edit__killbuf->next = currline ? currline->next : NULL;
1931 if(row == maxrow)
1932 down = true;
1933 else if((row < maxrow) and currline)
1934 row++;
1935 }
1936
1937 if(Edit__killbuf->prev)
1938 Edit__killbuf->prev->next = Edit__killbuf;
1939 if(Edit__killbuf->next)
1940 Edit__killbuf->next->prev = Edit__killbuf;
1941 currline = Edit__killbuf;
1942 Edit__killbuf = _prevline;
1943 if(Edit__killbuf)
1944 Edit__killbuf->next = NULL;
1945
1946 Undo->PushItem(EDIT_UNDO_POP_LINE);
1947
1948 // Move the cursor to EOL if necessary
1949 if(col+1 > currline->txt.length())
1950 GoEOL();
1951
1952 if(down)
1953 GoDown();
1954 else
1955 refresh(currline, row);
1956 }
1957
1958 GFTRK(0);
1959 }
1960
1961
1962 // ------------------------------------------------------------------
1963
ZapQuoteBelow()1964 void IEclass::ZapQuoteBelow() {
1965
1966 GFTRK("ZapQuoteBelow");
1967
1968 if(currline->prev)
1969 Undo->PushItem(EDIT_UNDO_VOID|PREV_LINE|batch_mode, currline->prev);
1970 else
1971 Undo->PushItem(EDIT_UNDO_VOID|batch_mode, currline);
1972 batch_mode = BATCH_MODE;
1973 UndoItem* item_to_fix = Undo->last_item;
1974
1975 deleteline(true);
1976 if(row == 0) {
1977 item_to_fix->line = currline;
1978 }
1979
1980 GFTRK(0);
1981 }
1982
1983
1984 // ------------------------------------------------------------------
1985
findtopline()1986 Line* IEclass::findtopline() {
1987
1988 GFTRK("Editfindtopline");
1989
1990 uint _toprow = row;
1991 Line* _topline = currline;
1992
1993 while(_topline->prev and (_toprow > minrow)) {
1994 _topline = _topline->prev;
1995 _toprow--;
1996 }
1997
1998 GFTRK(0);
1999
2000 return _topline;
2001 }
2002
2003
2004 // ------------------------------------------------------------------
2005
savefile(int __status)2006 void IEclass::savefile(int __status) {
2007
2008 Subj statbak;
2009
2010 GFTRK("Editsavefile");
2011
2012 // Turn off cursor and put up a wait window
2013 #if defined(__USE_NCURSES__) || !defined(__WIN32__) /* Fix after bugfix w9x console bug in gvidbase.cpp */
2014 int wascursoron = not vcurhidden();
2015 cursoroff();
2016 #endif
2017
2018 strcpy(statbak, information);
2019 update_statusline(LNG->Wait+1);
2020
2021 // Open the save file
2022 const char* editorfile = AddPath(CFG->goldpath, EDIT->File());
2023 remove(editorfile);
2024 gfile _fp(editorfile, "wb", CFG->sharemode);
2025 if (_fp.isopen())
2026 {
2027 // Find the first line
2028 Line* _saveline = findfirstline();
2029
2030 // First save the "unfinished" identifier
2031 if (__status == MODE_UPDATE)
2032 {
2033 _fp.Fputs(unfinished);
2034 _fp.Fputs("\r\n");
2035 }
2036
2037 // Save as whole paragraphs
2038 while(_saveline) {
2039
2040 // Copy the line to a buffer
2041 char _buf[EDIT_BUFLEN];
2042 strcpy(_buf, _saveline->txt.c_str());
2043
2044 // If a LF was found, replace it with a CR/LF combo
2045 char* _lfptr = strchr(_buf, '\n');
2046 if(_lfptr)
2047 strcpy(_lfptr, "\r\n");
2048
2049 // Save the line
2050 _fp.Fputs(_buf);
2051
2052 // Continue with the next line
2053 _saveline = _saveline->next;
2054 }
2055 }
2056
2057 update_statusline(statbak);
2058
2059 #if defined(__USE_NCURSES__) || !defined(__WIN32__) /* Fix after bugfix w9x console bug in gvidbase.cpp */
2060 if(wascursoron)
2061 cursoron();
2062 #endif
2063
2064 GFTRK(0);
2065 }
2066
2067
2068 // ------------------------------------------------------------------
2069
SaveFile()2070 void IEclass::SaveFile() {
2071
2072 GFTRK("EditSaveFile");
2073
2074 savefile(MODE_UPDATE);
2075
2076 GFTRK(0);
2077 }
2078
2079
2080 // ------------------------------------------------------------------
2081
SaveMsg()2082 void IEclass::SaveMsg() {
2083
2084 GFTRK("EditSaveMsg");
2085
2086 done = MODE_SAVE;
2087
2088 GFTRK(0);
2089 }
2090
2091
2092 // ------------------------------------------------------------------
2093
2094 #if defined(GCFG_SPELL_INCLUDED)
SCheckerMenu()2095 void IEclass::SCheckerMenu()
2096 {
2097 if (!schecker.IsInited())
2098 return;
2099
2100 const char *txt = currline->txt.c_str();
2101 GMenuSChecker menu;
2102 int finaltag;
2103
2104 if (!isscchar(txt[col]))
2105 finaltag = menu.Run(schecker, "");
2106 else
2107 {
2108 char buff[EDIT_BUFLEN];
2109 size_t beg = col;
2110 size_t end = col;
2111
2112 for (; (beg > 0) && isscchar(txt[beg-1]); beg--);
2113 for (; isscchar(txt[end]); end++);
2114 size_t len = end - beg;
2115
2116 memcpy(buff, &txt[beg], len);
2117 buff[len] = 0;
2118
2119 finaltag = menu.Run(schecker, buff);
2120 if (finaltag >= 0)
2121 {
2122 std::string &str = schecker.GetSuggest()[finaltag].second;
2123 size_t len2 = str.length() - 3;
2124 txt = &str.c_str()[2];
2125
2126 if ((buff[len-1] == '.') && (txt[len2-1] != '.')) len--;
2127 if (col > (beg + len2 - 1)) col = beg + len2 - 1;
2128
2129 Undo->PushItem(EDIT_UNDO_DEL_TEXT, currline, beg, len);
2130 currline->txt.erase(beg, len);
2131
2132 Undo->PushItem(EDIT_UNDO_INS_TEXT|BATCH_MODE, currline, beg, len2);
2133 currline->txt.insert(beg, txt, len2);
2134 wrapit(&currline, &col, &row);
2135 }
2136 }
2137
2138 if (finaltag == -2)
2139 {
2140 Line *line = currline;
2141 uint32_t _row = row;
2142
2143 while (line->prev && (_row > minrow))
2144 {
2145 line = line->prev;
2146 _row--;
2147 }
2148
2149 refresh(line, _row);
2150 }
2151 }
2152 #endif
2153
2154
2155 // ------------------------------------------------------------------
2156
isempty(Line * __line)2157 int IEclass::isempty(Line* __line) {
2158
2159 if(__line == NULL)
2160 __line = currline;
2161
2162 return (__line->txt.empty() or __line->txt[0] == '\n');
2163 }
2164
2165
2166 // ------------------------------------------------------------------
2167
reflowok(char * __qstr)2168 int IEclass::reflowok(char* __qstr) {
2169
2170 // Stop reflow if there is no next line
2171 if(currline->next == NULL)
2172 return false;
2173
2174 // Stop reflow if the next line is empty
2175 if(isempty(currline->next))
2176 return false;
2177
2178 // Stop reflow if the next line is a control line
2179 if(currline->next->type & (GLINE_KLUDGE|GLINE_TEAR|GLINE_ORIG|GLINE_TAGL))
2180 return false;
2181
2182 // Stop reflow if the quotestring on the next line is not the same
2183 uint _qlen2;
2184 char _qstr2[MAXQUOTELEN];
2185 GetQuotestr(currline->next->txt.c_str(), _qstr2, &_qlen2);
2186 if(not cmp_quotes(__qstr, _qstr2))
2187 return false;
2188
2189 return true;
2190 }
2191
2192
2193 // ------------------------------------------------------------------
2194
Reflow()2195 void IEclass::Reflow() {
2196
2197 GFTRK("EditReflow");
2198
2199 // Skip empty lines
2200 while(isempty()) {
2201 if(currline->next) {
2202 if(not batch_mode) {
2203 Undo->PushItem(EDIT_UNDO_VOID);
2204 batch_mode = BATCH_MODE;
2205 }
2206 GoDown();
2207 }
2208 else {
2209 GFTRK(0);
2210 return;
2211 }
2212 }
2213
2214 // Get the first quotestring
2215 uint _qlen1;
2216 char _qstr1[MAXQUOTELEN];
2217 GetQuotestr(currline->txt.c_str(), _qstr1, &_qlen1);
2218 const char* _qlenptr = currline->txt.c_str() + _qlen1;
2219
2220 // Strip leading spaces from the first line
2221 const char* ptr = _qlenptr;
2222 while(*ptr and isspace(*ptr) and (*ptr != '\n')) ptr++;
2223 if(ptr != _qlenptr) {
2224 Undo->PushItem(EDIT_UNDO_DEL_TEXT|batch_mode, currline, _qlen1, ptr-_qlenptr);
2225 currline->txt.erase(_qlen1, ptr-_qlenptr);
2226 }
2227
2228 // Perform the reflow
2229 while(reflowok(_qstr1)) {
2230
2231 // Work on the current line until it is done
2232 Line* _thisline = currline;
2233 while(_thisline == currline) {
2234
2235 // Stop reflow?
2236 if(not reflowok(_qstr1))
2237 break;
2238
2239 // Go to the EOL, insert a space and delete the LF
2240 GoEOL();
2241 if(col+1 < maxcol) {
2242 insertchar(' ');
2243 DelChar();
2244 }
2245 else {
2246 GoDown();
2247 col = mincol;
2248 }
2249 }
2250 }
2251
2252 // Go to the next line
2253 displine(currline,row);
2254 GoDown();
2255 col = mincol;
2256
2257 GFTRK(0);
2258 }
2259
2260
2261 // ------------------------------------------------------------------
2262
ExitMsg()2263 void IEclass::ExitMsg() {
2264
2265 GFTRK("EditExitMsg");
2266
2267 done = MODE_QUIT;
2268
2269 GFTRK(0);
2270 }
2271
2272
2273 // ------------------------------------------------------------------
2274
DelLine()2275 void IEclass::DelLine() {
2276
2277 GFTRK("EditDelLine");
2278
2279 cursoroff();
2280 deleteline();
2281
2282 GFTRK(0);
2283 }
2284
2285
2286 // ------------------------------------------------------------------
2287
ToUpper()2288 void IEclass::ToUpper() {
2289
2290 GFTRK("EditToUpper");
2291
2292 if(col < currline->txt.length()) {
2293 Undo->PushItem(EDIT_UNDO_OVR_CHAR);
2294 currline->txt[col] = g_toupper(currline->txt[col]);
2295 }
2296
2297 GFTRK(0);
2298 }
2299
2300
2301 // ------------------------------------------------------------------
2302
ToLower()2303 void IEclass::ToLower() {
2304
2305 GFTRK("EditToLower");
2306
2307 if(col < currline->txt.length()) {
2308 Undo->PushItem(EDIT_UNDO_OVR_CHAR);
2309 currline->txt[col] = g_tolower(currline->txt[col]);
2310 }
2311
2312 GFTRK(0);
2313 }
2314
2315
2316 // ------------------------------------------------------------------
2317
ToggleCase()2318 void IEclass::ToggleCase() {
2319
2320 GFTRK("EditToggleCase");
2321
2322 if (col < currline->txt.length())
2323 {
2324 Undo->PushItem(EDIT_UNDO_OVR_CHAR);
2325
2326 char chr = currline->txt[col];
2327 if (g_isupper(chr))
2328 currline->txt[col] = g_tolower(chr);
2329 else
2330 currline->txt[col] = g_toupper(chr);
2331 }
2332
2333 GFTRK(0);
2334 }
2335
2336
2337 // ------------------------------------------------------------------
2338
ToggleCaseChar(gkey key,std::string::iterator it,Line * ln,int n)2339 void IEclass::ToggleCaseChar(gkey key,
2340 std::string::iterator it,
2341 Line *ln, int n)
2342 {
2343 int oldchar = *it;
2344 int newchar = 0;
2345
2346 switch (key)
2347 {
2348 case KK_EditToLower:
2349 newchar = g_tolower(oldchar);
2350 break;
2351
2352 case KK_EditToUpper:
2353 newchar = g_toupper(oldchar);
2354 break;
2355
2356 case KK_EditToggleCase:
2357 if (g_isupper(oldchar))
2358 newchar = g_tolower(oldchar);
2359 else
2360 newchar = g_toupper(oldchar);
2361 break;
2362 }
2363
2364 if (newchar != oldchar)
2365 {
2366 currline = ln; pcol = col = n; getthisrow(currline); prow = thisrow;
2367 Undo->PushItem(EDIT_UNDO_OVR_CHAR);
2368 *it = newchar;
2369 }
2370 }
2371
2372
2373 // ------------------------------------------------------------------
2374
ToggleCaseBlock(gkey key)2375 void IEclass::ToggleCaseBlock(gkey key)
2376 {
2377 GFTRK("EditToggleCaseBlock");
2378
2379 // Find the anchor, if any
2380 Line* _anchor = findanchor();
2381
2382 // Did we find the anchor?
2383 if (_anchor)
2384 {
2385 Line* oldline = currline;
2386 int oldcol = col;
2387
2388 Line* _firstline = currline;
2389 Line* _lastline = currline;
2390 int firstcol = col, lastcol = col;
2391
2392 // Search below to find the anchor line
2393 while (_lastline->next and (_lastline != _anchor))
2394 _lastline = _lastline->next;
2395
2396 // Was the anchor line above or on the current line?
2397 if (_lastline != _anchor)
2398 {
2399 // The last copy line is the current line
2400 _lastline = currline;
2401
2402 // Search above to find the anchor line
2403 while (_firstline->prev and (_firstline != _anchor))
2404 _firstline = _firstline->prev;
2405 firstcol = blockcol;
2406 }
2407 else
2408 {
2409 if (currline != _anchor or blockcol > col)
2410 lastcol = blockcol;
2411 else
2412 firstcol = blockcol;
2413 }
2414
2415 // The _firstline and _lastline pointers
2416 // are now pointing where they should
2417
2418 int n;
2419 Line* ln = _firstline;
2420 std::string::iterator it1, it2;
2421
2422 if (ln == _lastline)
2423 {
2424 it1 = ln->txt.begin() + firstcol;
2425 it2 = ln->txt.begin() + lastcol;
2426 for (n = firstcol; it1 < it2; it1++, n++)
2427 ToggleCaseChar(key, it1, ln, n);
2428 }
2429 else
2430 {
2431 it1 = ln->txt.begin() + firstcol;
2432 it2 = ln->txt.end();
2433 for (n = firstcol; it1 < it2; it1++, n++)
2434 ToggleCaseChar(key, it1, ln, n);
2435
2436 for (ln = ln->next; ln != _lastline; ln = ln->next)
2437 {
2438 it1 = ln->txt.begin();
2439 it2 = ln->txt.end();
2440 for (n = 0; it1 < it2; it1++, n++)
2441 ToggleCaseChar(key, it1, ln, n);
2442 }
2443
2444 it1 = ln->txt.begin();
2445 it2 = ln->txt.begin() + lastcol;
2446 for (n = 0; it1 < it2; it1++, n++)
2447 ToggleCaseChar(key, it1, ln, n);
2448 }
2449
2450 currline = oldline;
2451 pcol = col = oldcol;
2452 getthisrow(currline);
2453 prow = thisrow;
2454 }
2455
2456 // Refresh display
2457 Line* _topline = findtopline();
2458 refresh(_topline, minrow);
2459
2460 GFTRK(0);
2461 }
2462
2463
2464 // ------------------------------------------------------------------
2465
SCodeChange(gkey key)2466 void IEclass::SCodeChange(gkey key)
2467 {
2468 GFTRK("EditSCodeChange");
2469
2470 char _ch = ' ';
2471 switch (key)
2472 {
2473 case KK_EditSCodeBold: _ch = '*'; break;
2474 case KK_EditSCodeUnderline: _ch = '_'; break;
2475 case KK_EditSCodeItalic: _ch = '/'; break;
2476 case KK_EditSCodeReverse: _ch = '#'; break;
2477 }
2478
2479 const char *cltxt = currline->txt.c_str();
2480 const char *punct = CFG->stylecodepunct;
2481
2482 if (isspace(cltxt[col]) || strchr(punct, cltxt[col]))
2483 {
2484 if (col && !isspace(cltxt[col-1]) && !strchr(punct, cltxt[col-1]))
2485 GoLeft();
2486 else if (!isspace(cltxt[col+1]) && !strchr(punct, cltxt[col+1]))
2487 GoRight();
2488 else
2489 {
2490 if (isspace(cltxt[col]) && (key != KK_EditSCodeNormal))
2491 {
2492 if (col && !isspace(cltxt[col-1]))
2493 insertchar(' ');
2494
2495 insertchar(_ch);
2496 insertchar(_ch);
2497 GoLeft();
2498 }
2499
2500 GFTRK(0);
2501 return;
2502 }
2503 }
2504
2505 uint beg = col;
2506 uint end = col;
2507
2508 while (beg && !isspace(cltxt[beg-1]) && !strchr(punct, cltxt[beg-1]))
2509 beg--;
2510 while (!isspace(cltxt[end+1]) && !strchr(punct, cltxt[end+1]))
2511 end++;
2512
2513 bool replace = false;
2514 char c1 = cltxt[beg];
2515 char c2 = cltxt[end];
2516
2517 if ((_ch == c1) && (c1 == c2))
2518 {
2519 GFTRK(0);
2520 return;
2521 }
2522
2523 if ((_ch != c1) && (c1 == c2) &&
2524 ((c1 == '*') || (c1 == '/') || (c1 == '_') || (c1 == '#')))
2525 replace = true;
2526
2527
2528 while (col && !isspace(cltxt[col-1]) && !strchr(punct, cltxt[col-1]))
2529 GoLeft();
2530
2531 if (replace) DelChar();
2532 if (_ch != ' ') insertchar(_ch);
2533
2534 cltxt = currline->txt.c_str();
2535 while (!isspace(cltxt[col+1]) && !strchr(punct, cltxt[col+1]))
2536 GoRight();
2537
2538 if (replace) DelChar();
2539 else GoRight();
2540 if (_ch != ' ') insertchar(_ch);
2541
2542 GoLeft();
2543
2544 GFTRK(0);
2545 }
2546
2547
2548 // ------------------------------------------------------------------
2549
LookupCursor()2550 void IEclass::LookupCursor() {
2551
2552 GFTRK("EditLookupCursor");
2553
2554 LookupNode(msgptr, currline->txt.c_str()+col, LOOK_NAME);
2555
2556 GFTRK(0);
2557 }
2558
2559
2560 // ------------------------------------------------------------------
2561
LookupDest()2562 void IEclass::LookupDest() {
2563
2564 GFTRK("EditLookupDest");
2565
2566 LookupNode(msgptr, "", LOOK_DEST);
2567
2568 GFTRK(0);
2569 }
2570
2571
2572 // ------------------------------------------------------------------
2573
LookupOrig()2574 void IEclass::LookupOrig() {
2575
2576 GFTRK("EditLookupOrig");
2577
2578 LookupNode(msgptr, "", LOOK_ORIG);
2579
2580 GFTRK(0);
2581 }
2582
2583
2584 // ------------------------------------------------------------------
2585
Soundkill()2586 void IEclass::Soundkill() {
2587
2588 HandleGEvent(EVTT_STOPVOICE);
2589 }
2590
2591
2592 // ------------------------------------------------------------------
2593
statusline()2594 void IEclass::statusline() {
2595
2596 if(chartyped) {
2597 if(EDIT->Completion.First()) {
2598 do {
2599 const char* trig = EDIT->Completion.Trigger();
2600 uint tlen = strlen(trig);
2601 if(col >= tlen) {
2602 if(strneql(trig, currline->txt.c_str()+col-tlen, tlen)) {
2603 int saved_insert = insert;
2604 insert = true;
2605 batch_mode = BATCH_MODE;
2606 uint n;
2607 for(n=0; n<tlen; n++)
2608 DelLeft();
2609 const char* cptr = EDIT->Completion.Text();
2610 uint clen = strlen(cptr);
2611 for(n=0; n<clen; n++)
2612 insertchar(*cptr++);
2613 HandleGEvent(EVTT_EDITCOMPLETION);
2614 insert = saved_insert;
2615 break;
2616 }
2617 }
2618 } while(EDIT->Completion.Next());
2619 }
2620 }
2621
2622 char _buf[EDIT_BUFLEN];
2623 *_buf = NUL;
2624
2625 if(EDIT->Comment.First()) {
2626 do {
2627 const char* trig = EDIT->Comment.Trigger();
2628 uint tlen = strlen(trig);
2629 if(col >= tlen) {
2630 if(strnieql(trig, currline->txt.c_str()+col-tlen, tlen)) {
2631 strcpy(_buf, EDIT->Comment.Text());
2632 break;
2633 }
2634 }
2635 } while(EDIT->Comment.Next());
2636 }
2637
2638 uint chr = currline->txt[col];
2639 update_statuslinef(LNG->EditStatus, "ST_EDITSTATUS", 1+thisrow, 1+col, chr, _buf);
2640 if(*_buf and CFG->switches.get(beepcomment)) {
2641 HandleGEvent(EVTT_EDITCOMMENT);
2642 }
2643 }
2644
2645
2646 // ------------------------------------------------------------------
2647
handlekey(gkey __key)2648 int IEclass::handlekey(gkey __key) {
2649
2650 int rc = true;
2651
2652 if (drawlines)
2653 {
2654 switch (__key)
2655 {
2656 case KK_EditBlockRight:
2657 case KK_EditBlockLeft:
2658 case KK_EditBlockUp:
2659 case KK_EditBlockDown:
2660 DrawLines(__key);
2661 case KK_EditBlockHome:
2662 case KK_EditBlockEnd:
2663 case KK_EditBlockPgDn:
2664 case KK_EditBlockPgUp:
2665 case KK_EditBlockWordRight:
2666 case KK_EditBlockWordLeft:
2667 undo_ready = NO;
2668 return rc;
2669 default:
2670 drawflag = true;
2671 }
2672 }
2673
2674 switch(__key)
2675 {
2676 case KK_EditBlockRight: __key = KK_EditGoRight; break;
2677 case KK_EditBlockLeft: __key = KK_EditGoLeft; break;
2678 case KK_EditBlockUp: __key = KK_EditGoUp; break;
2679 case KK_EditBlockDown: __key = KK_EditGoDown; break;
2680 case KK_EditBlockHome: __key = KK_EditGoBegLine; break;
2681 case KK_EditBlockEnd: __key = KK_EditGoEOL; break;
2682 case KK_EditBlockPgDn: __key = KK_EditGoPgDn; break;
2683 case KK_EditBlockPgUp: __key = KK_EditGoPgUp; break;
2684
2685 case KK_EditBlockWordRight: __key = KK_EditGoWordRight; break;
2686 case KK_EditBlockWordLeft: __key = KK_EditGoWordLeft; break;
2687
2688 case KK_EditCopy:
2689 case KK_EditCut:
2690 case KK_EditDelete: goto noselecting;
2691
2692 case KK_EditDelChar:
2693 case KK_EditDelLeft:
2694 if(selecting) {
2695 __key = KK_EditUndefine;
2696 }
2697 // fall through
2698
2699 case KK_EditPaste:
2700 case KK_EditNewline:
2701 if(selecting) {
2702 BlockCut(true);
2703 batch_mode = BATCH_MODE;
2704 }
2705 goto noselecting;
2706 break;
2707
2708 case KK_EditToLower:
2709 case KK_EditToUpper:
2710 case KK_EditToggleCase:
2711 if (!selecting) goto noselecting;
2712
2713 ToggleCaseBlock(__key);
2714 undo_ready = NO;
2715 return rc;
2716
2717 default:
2718 rc = PlayMacro(__key, KT_E);
2719 if(rc == true)
2720 return rc;
2721 rc = true;
2722 if(selecting) {
2723 Line *_line;
2724 selecting = NO;
2725 blockcol = -1;
2726 for(_line = findfirstline(); _line; _line = _line->next)
2727 _line->type &= ~GLINE_BLOK;
2728 // refresh screen
2729 int r = row;
2730 for(_line = currline; _line and r; _line = _line->prev)
2731 r--;
2732 refresh(_line, minrow);
2733 }
2734 goto noselecting;
2735 }
2736
2737 if(not selecting) {
2738 Line *_line;
2739 for(_line = findfirstline(); _line; _line = _line->next)
2740 _line->type &= ~GLINE_BLOK;
2741 currline->type |= GLINE_BLOK;
2742 selecting = YES;
2743 blockcol = col;
2744 // refresh screen
2745 int r = row;
2746 for(_line = currline; _line and r; _line = _line->prev)
2747 r--;
2748 refresh(_line, minrow);
2749 }
2750
2751 noselecting:
2752
2753 switch(__key) {
2754 case KK_EditAbort: Abort(); break;
2755 case KK_EditAskExit: AskExit(); break;
2756 case KK_EditClearDeleteBuf: ClearDeleteBuf(); break;
2757 case KK_EditClearPasteBuf: ClearPasteBuf(); break;
2758 case KK_EditCopyAboveChar: CopyAboveChar(); break;
2759 case KK_EditDelChar: DelChar(); break;
2760 case KK_EditDeleteEOL: DeleteEOL(); break;
2761 case KK_EditDeleteSOL: DeleteSOL(); break;
2762 case KK_EditDelLeft: DelLeft(); break;
2763 case KK_EditDelLine: DelLine(); break;
2764 case KK_EditDelLtWord: DelLtWord(); break;
2765 case KK_EditDelRtWord: DelRtWord(); break;
2766 case KK_EditDosShell: DosShell(); break;
2767 case KK_EditDupLine: DupLine(); break;
2768 case KK_EditExitMsg: ExitMsg(); break;
2769 case KK_EditExportText: ExportText(); break;
2770 case KK_EditGoBegLine: GoBegLine(); break;
2771 case KK_EditGoBotLine: GoBotLine(); break;
2772 case KK_EditGoBotMsg: GoBotMsg(); break;
2773 case KK_EditGoDown: GoDown(); break;
2774 case KK_EditGoEOL: GoEOL(); break;
2775 case KK_EditGoLeft: GoLeft(); break;
2776 case KK_EditGoPgDn: GoPgDn(); break;
2777 case KK_EditGoPgUp: GoPgUp(); break;
2778 case KK_EditGoRight: GoRight(); break;
2779 case KK_EditGoTopLine: GoTopLine(); break;
2780 case KK_EditGoTopMsg: GoTopMsg(); break;
2781 case KK_EditGoUp: GoUp(); break;
2782 case KK_EditGoWordLeft: GoWordLeft(); break;
2783 case KK_EditGoWordRight: GoWordRight(); break;
2784 case KK_EditHeader: Header(); break;
2785 case KK_EditImportQuotebuf: ImportQuotebuf(); break;
2786 case KK_EditImportText: ImportText(); break;
2787 case KK_EditLoadFile: LoadFile(); break;
2788 case KK_EditLookupCursor: LookupCursor(); break;
2789 case KK_EditLookupDest: LookupDest(); break;
2790 case KK_EditLookupOrig: LookupOrig(); break;
2791 case KK_EditNewline: Newline(); break;
2792 case KK_EditQuitNow: QuitNow(); break;
2793 case KK_EditReflow: Reflow(); break;
2794 case KK_EditSaveFile: SaveFile(); break;
2795 case KK_EditSaveMsg: SaveMsg(); break;
2796 #if defined(GCFG_SPELL_INCLUDED)
2797 case KK_EditSCheckerMenu: SCheckerMenu(); break;
2798 #endif
2799 case KK_EditSoundkill: Soundkill(); break;
2800 case KK_EditSpellCheck: SpellCheck(); break;
2801 case KK_EditTab: Tab(); break;
2802 case KK_EditTabReverse: ReTab(); break;
2803 case KK_EditToggleCase: ToggleCase(); break;
2804 case KK_EditToggleInsert: ToggleInsert(); break;
2805 case KK_EditToLower: ToLower(); break;
2806 case KK_EditToUpper: ToUpper(); break;
2807 case KK_EditUndefine: break;
2808 case KK_EditUnDelete: UnDelete(); break;
2809 case KK_EditUndo: Undo->PlayItem(); break;
2810 case KK_EditZapQuoteBelow: ZapQuoteBelow(); break;
2811 case KK_EditSCodeNormal:
2812 case KK_EditSCodeBold:
2813 case KK_EditSCodeItalic:
2814 case KK_EditSCodeUnderline:
2815 case KK_EditSCodeReverse: SCodeChange(__key); break;
2816 case KK_EditDrawLines: ToggleDrawLines(); break;
2817
2818 // Block functions
2819 case KK_EditAnchor: BlockAnchor(); break;
2820 case KK_EditCopy: BlockCopy(); break;
2821 case KK_EditCut: BlockCut(); break;
2822 case KK_EditDelete: BlockCut(true); break;
2823 case KK_EditPaste: BlockPaste(); break;
2824
2825 default:
2826 rc = false;
2827 break;
2828 }
2829
2830 if(__key != KK_EditUndo)
2831 undo_ready = NO;
2832
2833 return rc;
2834 }
2835
2836
2837 // ------------------------------------------------------------------
2838
Start(int __mode,uint * __position,GMsg * __msg)2839 int IEclass::Start(int __mode, uint* __position, GMsg* __msg) {
2840
2841 GFTRK("EditStart");
2842
2843 thisrow = 0;
2844 quitnow = NO;
2845 col = mincol;
2846 row = minrow;
2847 msgptr = __msg;
2848 msgmode = __mode;
2849 currline = __msg->lin;
2850
2851 #if defined(GCFG_SPELL_INCLUDED)
2852 if (CFG->scheckerenabled)
2853 {
2854 int save_chartableno = LoadCharset(NULL,NULL,1); // Workaround: internal for LoadCharset() charset table number changed in the schecker.Load()
2855 schecker.Init(CFG->xlatlocalset, CFG->scheckerdicpath);
2856 char *str = strdup(AA->adat->scheckerdeflang);
2857 char *token = strtok(str, " ");
2858 while(token != NULL)
2859 {
2860 schecker.Load(token, CFG->scheckeruserdic);
2861 /* Get next token: */
2862 token = strtok(NULL, " ");
2863 }
2864 free(str);
2865
2866 if(save_chartableno != -1) // restore value of the default chaset table // workaround: internal for LoadCharset() charset table number changed in the schecker.Load()
2867 LoadCharset(CFG->xlatcharset[save_chartableno].imp, CFG->xlatcharset[save_chartableno].exp);
2868 else
2869 LoadCharset("N/A","N/A");
2870 }
2871 #endif
2872
2873 if(AA->isinternet() and (CFG->soupexportmargin <= CFG->dispmargin))
2874 margintext = CFG->soupexportmargin;
2875 else
2876 margintext = CFG->dispmargin;
2877
2878 marginquotes = EDIT->QuoteMargin() + 1; // Add one for CR
2879 if(marginquotes > margintext)
2880 marginquotes = margintext;
2881
2882 if(currline == NULL) {
2883 currline = new Line("\n");
2884 throw_xnew(currline);
2885 }
2886
2887 // Check if there is an unfinished backup message
2888 gfile _fp(AddPath(CFG->goldpath, EDIT->File()), "rt", CFG->sharemode);
2889 if (_fp.isopen())
2890 {
2891 char _buf[EDIT_BUFLEN];
2892 _fp.Fgets(_buf, sizeof(_buf));
2893 _fp.Fclose();
2894
2895 if (striinc(unfinished, _buf))
2896 {
2897 w_info(LNG->UnfinishedMsg);
2898 update_statusline(LNG->LoadUnfinished);
2899 HandleGEvent(EVTT_ATTENTION);
2900 gkey _ch = getxch();
2901 w_info(NULL);
2902 if(_ch != Key_Esc) {
2903 LoadFile();
2904 *__position = 0;
2905 remove(AddPath(CFG->goldpath, EDIT->File()));
2906 }
2907 }
2908 }
2909
2910 if(*__position) {
2911 for(uint _posrow=0; _posrow < *__position; _posrow++) {
2912 if(currline->next) {
2913 currline = currline->next;
2914 row++;
2915 }
2916 }
2917 thisrow = row;
2918 }
2919
2920 done = NO;
2921
2922 // If the starting line is outside the first screenful
2923 if(*__position >= (maxrow+1)) {
2924 refresh(currline->prev, minrow);
2925 row = 1;
2926 }
2927 else {
2928 refresh(findfirstline(), minrow);
2929 }
2930
2931 gotorowcol(mincol, minrow);
2932 dispins();
2933
2934 time32_t _lasttime = gtime(NULL);
2935
2936 while(not done) {
2937
2938 statusline();
2939
2940 gotorowcol(col, row);
2941 batch_mode = 0;
2942
2943 vattr backattr = BLACK_|_BLACK;
2944 if(blockcol == -1) {
2945 backattr = dispchar(currline->txt.c_str()[col], C_READC);
2946 gotorowcol(col, row);
2947 }
2948
2949 cursoron();
2950 if(insert)
2951 vcursmall();
2952 else
2953 vcurlarge();
2954
2955 gkey _ch;
2956 #if defined(__WIN32__)
2957 gkey keystatus = 0;
2958 #endif
2959
2960 do {
2961 _ch = getxchtick();
2962 // TO_PORT_TAG: kbxget_raw(3)
2963 #if defined(__WIN32__)
2964 keystatus = kbxget_raw(3);
2965 #endif
2966
2967 if(EDIT->AutoSave()) {
2968 time32_t _thistime = gtime(NULL);
2969 if(_thistime >= (_lasttime+EDIT->AutoSave())) {
2970 _lasttime = _thistime;
2971 SaveFile();
2972 }
2973 }
2974
2975 if(_ch == Key_Tick)
2976 CheckTick(KK_EditQuitNow);
2977
2978 } while(_ch == Key_Tick);
2979
2980 pcol = col; getthisrow(currline); prow = thisrow;
2981
2982 int ismacro = false;
2983 gkey _kk = SearchKey(_ch, EditKey, EditKeys);
2984 if(_kk) {
2985 _ch = _kk;
2986
2987 // TO_PORT_TAG: kbxget_raw(3)
2988 #if defined(__WIN32__)
2989 if (keystatus & SHIFT_PRESSED)
2990 #else
2991 if (0)
2992 #endif
2993 {
2994 switch(_ch)
2995 {
2996 case KK_EditGoUp: _ch = KK_EditBlockUp; break;
2997 case KK_EditGoDown: _ch = KK_EditBlockDown; break;
2998 case KK_EditGoLeft: _ch = KK_EditBlockLeft; break;
2999 case KK_EditGoWordLeft: _ch = KK_EditBlockWordLeft; break;
3000 case KK_EditGoRight: _ch = KK_EditBlockRight; break;
3001 case KK_EditGoWordRight: _ch = KK_EditBlockWordRight; break;
3002 case KK_EditGoBegLine:
3003 case KK_EditGoTopMsg: _ch = KK_EditBlockHome; break;
3004 case KK_EditGoEOL:
3005 case KK_EditGoBotMsg: _ch = KK_EditBlockEnd; break;
3006 case KK_EditGoPgUp:
3007 case KK_EditGoTopLine: _ch = KK_EditBlockPgUp; break;
3008 case KK_EditGoPgDn:
3009 case KK_EditGoBotLine: _ch = KK_EditBlockPgDn; break;
3010 }
3011 }
3012 }
3013 else {
3014 ismacro = IsMacro(_ch, KT_E);
3015 }
3016
3017 if(blockcol == -1)
3018 dispchar(currline->txt.c_str()[col], backattr);
3019
3020 chartyped = false;
3021 if((_ch < KK_Commands) and (_ch & 0xFF) and not ismacro) {
3022 drawflag = true;
3023 chartyped = true;
3024 _ch &= 0xFF;
3025 insertchar((char)_ch);
3026 undo_ready = YES;
3027 }
3028 else if(handlekey(_ch)) {
3029 getthisrow(currline);
3030 }
3031 }
3032
3033 cursoroff();
3034
3035 msgptr->lin = findfirstline();
3036 savefile(quitnow ? MODE_UPDATE : MODE_SAVE);
3037
3038 // Prune killbuffer
3039 if(Edit__killbuf) {
3040 Line *__line = Edit__killbuf;
3041
3042 int _count = EDIT->UnDelete();
3043 while(__line and _count--)
3044 __line = __line->prev;
3045
3046 if(__line)
3047 if(__line->next)
3048 __line->next->prev = NULL;
3049
3050 while(__line) {
3051 if(Undo->FixPushLine(__line)) {
3052 if(__line->prev) {
3053 __line = __line->prev;
3054 __line->next = NULL;
3055 }
3056 else {
3057 __line = NULL;
3058 }
3059 }
3060 else {
3061 if(__line->prev) {
3062 __line = __line->prev;
3063 throw_xdelete(__line->next);
3064 }
3065 else
3066 throw_xdelete(__line);
3067 }
3068 }
3069 }
3070
3071 *__position = 1 + thisrow;
3072
3073 GFTRK(0);
3074
3075 return done;
3076 }
3077
3078
3079 // ------------------------------------------------------------------
3080
UndoStack(IEclass * this_editor)3081 UndoStack::UndoStack(IEclass* this_editor) :
3082
3083 editor(this_editor),
3084 row(editor->row),
3085 col(editor->col),
3086 pcol(editor->pcol),
3087 prow(editor->prow),
3088 minrow(editor->minrow),
3089 maxrow(editor->maxrow),
3090 thisrow(editor->thisrow),
3091 currline(editor->currline),
3092 undo_ready(editor->undo_ready) {
3093 UndoItem::last_item = &last_item;
3094 last_item = NULL;
3095 undo_enabled = YES;
3096 }
3097
3098
3099 // ------------------------------------------------------------------
3100
~UndoStack()3101 UndoStack::~UndoStack() {
3102
3103 while(last_item) {
3104 switch(last_item->action & EDIT_UNDO_ACTION) {
3105 case EDIT_UNDO_DEL_TEXT:
3106 case EDIT_UNDO_INS_TEXT:
3107 case EDIT_UNDO_WRAP_TEXT:
3108 throw_delete(last_item->data.text_ptr);
3109 break;
3110 case EDIT_UNDO_DEL_LINE:
3111 throw_xdelete(last_item->data.line_ptr);
3112 }
3113 delete last_item;
3114 }
3115 }
3116
3117
3118 // ------------------------------------------------------------------
3119
FixPushLine(Line * __line)3120 bool UndoStack::FixPushLine(Line* __line) {
3121
3122 UndoItem* item = last_item;
3123
3124 while(item) {
3125 if(((item->action & EDIT_UNDO_ACTION) == EDIT_UNDO_PUSH_LINE) and (item->data.line_ptr == __line)) {
3126 item->action &= ~EDIT_UNDO_ACTION;
3127 item->action |= EDIT_UNDO_ORPHAN_LINE;
3128 return true;
3129 }
3130 item = item->prev;
3131 }
3132 return false;
3133 }
3134
3135
3136 // ------------------------------------------------------------------
3137
PushItem(uint action,Line * __line,uint __col,uint __len)3138 void UndoStack::PushItem(uint action, Line* __line, uint __col, uint __len) {
3139
3140 GFTRK("PushItem");
3141
3142 if(undo_enabled) {
3143
3144 throw_new(last_item = new UndoItem);
3145 last_item->col.num = (__col != NO_VALUE) ? __col : col;
3146 last_item->col.sav = 0;
3147 last_item->action = action;
3148 last_item->pcol = pcol;
3149 last_item->prow = prow;
3150
3151 switch(action & EDIT_UNDO_ACTION) {
3152 case EDIT_UNDO_VOID:
3153 case EDIT_UNDO_INS_CHAR:
3154 last_item->line = __line ? __line : currline;
3155 last_item->data.char_int = NUL;
3156 break;
3157 case EDIT_UNDO_DEL_CHAR:
3158 case EDIT_UNDO_OVR_CHAR:
3159 last_item->line = __line ? __line : currline;
3160 last_item->data.char_int = last_item->line->txt[last_item->col.num];
3161 break;
3162 case EDIT_UNDO_DEL_TEXT:
3163 last_item->line = __line;
3164 __col = last_item->col.num;
3165 if(__len == NO_VALUE)
3166 __len = __line->txt.length() - __col;
3167 throw_new(last_item->data.text_ptr = new(__len) text_item(__col, __len));
3168 memcpy(last_item->data.text_ptr->text, __line->txt.c_str() + __col, __len);
3169 break;
3170 case EDIT_UNDO_CUT_TEXT:
3171 last_item->line = __line;
3172 last_item->data.text_ptr = NULL;
3173 break;
3174 case EDIT_UNDO_INS_TEXT:
3175 case EDIT_UNDO_WRAP_TEXT:
3176 last_item->line = __line;
3177 if(__len == NO_VALUE)
3178 __len = __line->txt.length() - __col;
3179 throw_new(last_item->data.text_ptr = new text_item(__col, __len));
3180 break;
3181 case EDIT_UNDO_NEW_LINE:
3182 last_item->line = last_item->data.line_ptr = __line;
3183 break;
3184 case EDIT_UNDO_DEL_LINE:
3185 last_item->line = __line->prev ? __line->prev : __line->next;
3186 last_item->data.line_ptr = __line;
3187 break;
3188 case EDIT_UNDO_PUSH_LINE:
3189 if(currline->next)
3190 last_item->line = currline->next;
3191 else {
3192 last_item->action |= LAST_LINE;
3193 last_item->line = currline->prev;
3194 }
3195 last_item->data.line_ptr = currline;
3196 break;
3197 case EDIT_UNDO_POP_LINE:
3198 last_item->line = currline;
3199 last_item->data.line_ptr = NULL;
3200 break;
3201 }
3202 }
3203
3204 GFTRK(0);
3205 }
3206
3207
3208 // ------------------------------------------------------------------
3209
PlayItem()3210 void UndoStack::PlayItem() {
3211
3212 GFTRK("PlayItem");
3213
3214 if(last_item) {
3215
3216 UndoItem* item;
3217
3218 // Don't save any new items while in Undo function
3219 undo_enabled = NO;
3220
3221 // Find first of the batch items
3222 for(item = last_item; item->action & BATCH_MODE; item = item->prev);
3223
3224 uint curr_row_num = thisrow;
3225 uint curr_col_num = col;
3226 if((item->action & PREV_LINE) and item->line->next)
3227 currline = item->line->next;
3228 else
3229 currline = item->line;
3230 editor->getthisrow(currline);
3231
3232 int _maxrow = maxrow;
3233 if((item->action & PREV_LINE) and not item->line->next)
3234 _maxrow--;
3235
3236 col = item->col.num;
3237
3238 if(curr_row_num != thisrow) {
3239
3240 // Let user to see the position before performing Undo, unless it's a
3241 // neighbour line and the same column.
3242 undo_ready = ((abs(int(curr_row_num - thisrow)) < 2) and ((curr_col_num == col) or (col+1 > currline->txt.length())));
3243
3244 // Move cursor up or down depending on where the undo line is,
3245 // then refresh window if the line is invisible.
3246 do {
3247 if(curr_row_num > thisrow) {
3248 if(row > minrow)
3249 curr_row_num--, row--;
3250 else {
3251 editor->refresh(currline, row);
3252 break;
3253 }
3254 }
3255 else {
3256 if(row < _maxrow)
3257 curr_row_num++, row++;
3258 else {
3259 Line* l = currline;
3260 for(uint r = row; r; l = l->prev, r--) {}
3261 editor->refresh(l, minrow);
3262 break;
3263 }
3264 }
3265 } while(curr_row_num != thisrow);
3266 }
3267 else
3268 undo_ready = ((abs(int(curr_col_num - col)) < 2) or (col+1 > currline->txt.length()));
3269
3270 uint _pcol = item->pcol;
3271 uint _prow = item->prow;
3272
3273 if(undo_ready) {
3274
3275 if((item->action & PREV_LINE) and not item->line->next) {
3276 row++; thisrow++; curr_row_num++;
3277 }
3278
3279 bool in_batch;
3280
3281 // Keep undoing until item with no BATCH_MODE flag is reached.
3282 do {
3283
3284 uint undo_type = last_item->action & EDIT_UNDO_TYPE;
3285 uint undo_action = last_item->action & EDIT_UNDO_ACTION;
3286 in_batch = make_bool(last_item->action & BATCH_MODE);
3287 currline = last_item->line;
3288
3289 if(last_item->action & PREV_LINE) {
3290 col = last_item->col.num = last_item->col.sav;
3291 if(row > minrow)
3292 row--;
3293 }
3294
3295 switch(undo_type) {
3296
3297 case EDIT_UNDO_CHAR:
3298 switch(undo_action) {
3299 case EDIT_UNDO_INS_CHAR:
3300 currline->txt.erase(last_item->col.num, 1);
3301 break;
3302 case EDIT_UNDO_DEL_CHAR:
3303 currline->txt.insert(last_item->col.num, 1, last_item->data.char_int);
3304 break;
3305 case EDIT_UNDO_OVR_CHAR:
3306 currline->txt[last_item->col.num] = last_item->data.char_int;
3307 break;
3308 }
3309 editor->setlinetype(currline);
3310 break;
3311
3312 case EDIT_UNDO_TEXT: {
3313 text_item* text_data = last_item->data.text_ptr;
3314 std::string *txt = &currline->txt;
3315 switch(undo_action) {
3316 case EDIT_UNDO_DEL_TEXT:
3317 txt->insert(text_data->col, text_data->text, text_data->len);
3318 throw_delete(text_data);
3319 break;
3320 case EDIT_UNDO_CUT_TEXT:
3321 txt->erase(last_item->col.num);
3322 break;
3323 case EDIT_UNDO_WRAP_TEXT:
3324 txt->append(currline->next->txt.c_str()+text_data->col, text_data->len);
3325 txt = &currline->next->txt;
3326 // fall through...
3327 case EDIT_UNDO_INS_TEXT:
3328 txt->erase(text_data->col, text_data->len);
3329 throw_delete(text_data);
3330 break;
3331 }
3332 editor->setlinetype((undo_action == EDIT_UNDO_WRAP_TEXT) ? currline->next : currline);
3333 break;
3334 }
3335
3336 case EDIT_UNDO_LINE: {
3337 Line* thisline = last_item->data.line_ptr;
3338 switch(undo_action) {
3339 case EDIT_UNDO_NEW_LINE:
3340 if(thisline->next)
3341 thisline->next->prev = thisline->prev;
3342 if(thisline->prev) {
3343 thisline->prev->next = thisline->next;
3344 currline = thisline->prev;
3345 }
3346 else
3347 currline = thisline->next;
3348 throw_xdelete(thisline);
3349 break;
3350 case EDIT_UNDO_ORPHAN_LINE:
3351 if(last_item->action & LAST_LINE) {
3352 thisline->prev = currline;
3353 thisline->next = currline ? currline->next : NULL;
3354 if((row < maxrow) and currline)
3355 row++;
3356 }
3357 else {
3358 thisline->prev = currline ? currline->prev : NULL;
3359 thisline->next = currline;
3360 }
3361 // fall through...
3362 case EDIT_UNDO_DEL_LINE:
3363 if(thisline->prev)
3364 thisline->prev->next = thisline;
3365 if(thisline->next)
3366 thisline->next->prev = thisline;
3367 currline = thisline;
3368 break;
3369 case EDIT_UNDO_PUSH_LINE:
3370 editor->UnDelete(make_bool_not(last_item->action & LAST_LINE));
3371 break;
3372 case EDIT_UNDO_POP_LINE:
3373 editor->DelLine();
3374 break;
3375 }
3376 }
3377 }
3378
3379 _pcol = last_item->pcol;
3380 _prow = last_item->prow;
3381 delete last_item;
3382
3383 } while(last_item and in_batch);
3384
3385 undo_enabled = YES;
3386
3387 editor->getthisrow(currline);
3388 uint temprow = row;
3389 Line *templine = currline;
3390 Line *topline = editor->findfirstline();
3391
3392 int delta = _prow-thisrow;
3393
3394 if(not in_range(row+delta, minrow, maxrow)) {
3395
3396 // we need to fit thisrow into the screen boundaries
3397 if(delta > 0) {
3398 for(row += delta; row > maxrow; row--) {
3399 if(templine) // cause refresh() issue an error since templine should never be NULL
3400 templine = templine->prev;
3401 }
3402 temprow = maxrow;
3403 }
3404 else {
3405 for(row += delta; (int)row < (int)minrow; row++) {
3406 if(templine) // cause refresh() issue an error since templine should never be NULL
3407 templine = templine->next;
3408 }
3409 temprow = minrow;
3410 }
3411
3412 // move pointer to the top of screen so we refresh scrolled area
3413 while (row != minrow) {
3414 if(templine) // cause refresh() issue an error since templine should never be NULL
3415 templine = templine->prev;
3416 --row;
3417 }
3418 }
3419 else {
3420 if(delta < 0) {
3421 templine = topline;
3422 for(thisrow=0; thisrow < _prow; thisrow++)
3423 if(templine) // cause refresh() issue an error if thisrow != _prow
3424 templine = templine->next;
3425 }
3426 temprow = row+delta;
3427 }
3428
3429 // refresh screen
3430 editor->refresh(templine, row);
3431
3432 // set cursor position
3433 thisrow = _prow;
3434 col = _pcol;
3435 row = temprow;
3436 // set currline according to thisrow
3437 currline = topline;
3438 for(thisrow=0; thisrow < _prow; thisrow++)
3439 if(currline)
3440 currline = currline->next;
3441 }
3442 // Move the cursor to EOL if necessary
3443 else if(col+1 > currline->txt.length())
3444 editor->GoEOL();
3445 undo_ready = YES;
3446 }
3447
3448 GFTRK(0);
3449 }
3450
3451
3452 // ------------------------------------------------------------------
3453