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