1 /*
2 *
3 * A class for diplaying and operating an advanced text editor with
4 * syntax highlighting, scrolling and other useful features.
5 *
6 * Copyright (C) 1999-2001 by Konstantin Klyagin <k@thekonst.net>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or (at
11 * your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 * USA
22 *
23 * History of changes:
24 *
25 * 04.01.2000    development started
26 * 09.01.2000    the first release is ready
27 * 11.01.2000    one-line comments hightlight feature added
28 * 29.01.2000    text selecting and copying feature (cut-n'-paste)
29 * 01.02.2000    setpos() method added
30 * 03.02.2000    eddelword() method added, some minor changes made
31 * 06.02.2000    delmark() method added, hl_comment() minor bug fixed
32 * 18.02.2000    bool wrap variable added
33 * 11.03.2000    full tab support added
34 * 01.04.2000    emacs keys binding option added
35 * 20.04.2000    getline() & putline() methods added
36 * 23.04.2000    colorschemes added
37 * 07.05.2000    setpos() changed
38 * 11.05.2000    whole lines highlighing feature added
39 * 28.06.2000    shiftident() method added
40 * 10.07.2000    undo() implemented
41 * 25.07.2000    tab support improved
42 * 06.09.2000    blocks handling improved, shiftmarkedblock method added
43 * 31.10.2008    mergeline() minor bug fixed
44 *
45 */
46 
47 #ifdef HAVE_CONFIG_H
48 #include <config.h>
49 #endif
50 
51 #ifdef HAVE_STDINT_H
52 #include <stdint.h> /* for intptr_t */
53 #endif
54 
55 #include "texteditor.h"
56 
57 #define CURLINE       (curfile ? (curfile->sy+curfile->y) : 0)
58 #define CURCOL        (curfile ? (curfile->sx+curfile->x) : 0)
59 #define CURSTRING     (char *) curfile->lines->at(CURLINE)
60 #define CSTRLEN       strlen(CURSTRING ? CURSTRING : "")
61 #define UPDATECURRENTLINE       { kgotoxy(x1, y1+curfile->y); showline(CURLINE, curfile->sx, x2-x1); }
62 
63 #define MAX_STRLEN    10240
64 #define ALONE_DELIM   " ;(){}[].,:-+*/^?!=<>"
65 #define NONCHAR_DELIM " ;(){}[].,:-+*/^?!=<>\"'_"
66 #define WORD_DELIM    " ,"
67 
68 #define EM_TAB        2
69 #define EM_CTRL       4
70 #define EM_MANUAL     8
71 
72 #define CHECKLOADED     if(!getfcount()) return;
73 
texteditor()74 texteditor::texteditor():
75 otherkeys(0), fn(-1), wrap(false), abscol(0), idle(0),
76 insertmode(true), undolog(true), show(true), curfile(0),
77 prevshift(false), smarttab(true) {
78 
79     files = new linkedlist;
80     files->freeitem = &editfilefree;
81 }
82 
~texteditor()83 texteditor::~texteditor() {
84     delete files;
85 }
86 
load(const string abuf,const string id)87 int texteditor::load(const string abuf, const string id) {
88     int newfn = addwindow(strdup(id.c_str()));
89     string buf = abuf;
90     vector<string> lst;
91     vector<string>::iterator i;
92 
93     setfnum(newfn);
94 
95     breakintolines(buf, lst, wrap ? x2-x1-1 : 0);
96 
97     curfile->lines->empty();
98     for(i = lst.begin(); i != lst.end(); i++) {
99 	curfile->lines->add(strdup(i->c_str()));
100     }
101     curfile->lines->add(strdup(""));
102 
103     return newfn;
104 }
105 
load(FILE * f,const string id)106 int texteditor::load(FILE *f, const string id) {
107     int i = -1;
108     struct stat st;
109     char *p = 0;
110 
111     if(f)
112     if(!fstat(fileno(f), &st)) {
113 	p = new char[st.st_size+1];
114 	fseek(f, 0, SEEK_SET);
115 	fread(p, st.st_size, 1, f);
116 	p[st.st_size] = 0;
117 	i = load(p, strdup(id.c_str()));
118 	delete p;
119     }
120 
121     return i;
122 }
123 
load(ifstream & f,const string id)124 int texteditor::load(ifstream &f, const string id) {
125     int ret, size;
126     char *buf;
127 
128     f.seekg(0, ios::end);
129     size = f.tellg();
130     f.seekg(0, ios::beg);
131 
132     buf = new char[size+1];
133     f.read(buf, size);
134     buf[size] = 0;
135     ret = load(buf, strdup(id.c_str()));
136     delete buf;
137 
138     return ret;
139 }
140 
save(const char * linebreak)141 char *texteditor::save(const char *linebreak) {
142     int i;
143     char *buf, *p, *prev;
144 
145     buf = p = 0;
146 
147     for(i = 0; i < curfile->lines->count; i++) {
148 	prev = p;
149 	p = (char *) curfile->lines->at(i);
150 
151 	if(!buf) {
152 	    buf = (char *) malloc(strlen(p) + strlen(linebreak) + 1);
153 	    buf[0] = 0;
154 	} else {
155 	    buf = (char *) realloc(buf, strlen(buf) + strlen(p) + strlen(linebreak) + 1);
156 	}
157 
158 	if(i) {
159 	    if(!wrap) {
160 		strcat(buf, linebreak);
161 	    } else
162 	    if(!prev[0] ||
163 	    (prev[strlen(prev)-1] != ' ') &&
164 	    (strlen(prev) < x2-x1-1)) {
165 		strcat(buf, linebreak);
166 	    }
167 	}
168 
169 	while(strspn(p, " ") >= TAB_SIZE) {
170 	    p += TAB_SIZE;
171 	    strcat(buf, "\t");
172 	}
173 
174 	if(!((i == curfile->lines->count-1) && !strcmp(p, linebreak)))
175 	    strcat(buf, p);
176     }
177 
178     return buf;
179 }
180 
save(FILE * f,const char * linebreak)181 int texteditor::save(FILE *f, const char *linebreak) {
182     char *buf = save(linebreak);
183     fwrite(buf, strlen(buf), 1, f);
184     delete buf;
185     modified = false;
186     return 0;
187 }
188 
save(ofstream & f,const string linebreak)189 int texteditor::save(ofstream &f, const string linebreak) {
190     char *buf = save(linebreak.c_str());
191     f.write(buf, strlen(buf));
192     delete buf;
193     f.close();
194     modified = false;
195     return 0;
196 }
197 
getfnum()198 int texteditor::getfnum() {
199     return fn;
200 }
201 
getfcount()202 int texteditor::getfcount() {
203     return files->count;
204 }
205 
getfid()206 char *texteditor::getfid() {
207     return getfid(fn);
208 }
209 
getfid(int fnn)210 char *texteditor::getfid(int fnn) {
211     if(files->count) {
212 	editfile *ef = (editfile *) files->at(fnn);
213 	if(ef) return ef->id; else return 0;
214     } else {
215 	return 0;
216     }
217 }
218 
setfid(char * id)219 void texteditor::setfid(char *id) {
220     setfid(fn, id);
221 }
222 
setfid(int fnn,char * id)223 void texteditor::setfid(int fnn, char *id) {
224     if(fnn < files->count) {
225 	editfile *ef = (editfile *) files->at(fnn);
226 	if(ef) ef->id = id;
227     }
228 }
229 
setfnum(int n)230 void texteditor::setfnum(int n) {
231     if(n < files->count && n >= 0 && n != fn) {
232 
233 	// save previous window params
234 
235 	if(curfile && (fn >= 0)) curfile->modified = modified;
236 
237 	// set new file number
238 
239 	fn = n;
240 	curfile = (editfile *) files->at(fn);
241 	modified = curfile->modified;
242 
243 	colors = colorschemes[curfile->ncolorscheme];
244     }
245 }
246 
addwindow(char * id)247 int texteditor::addwindow(char *id) {
248     editfile *ef = new editfile;
249 
250     ef->lines = new linkedlist;
251     ef->blocks = new linkedlist;
252     ef->highlines = new linkedlist;
253     ef->undo = new linkedlist;
254 
255     ef->blocks->freeitem = &textblockfree;
256     ef->lines->freeitem = &charpointerfree;
257     ef->highlines->freeitem = &highlinefree;
258     ef->undo->freeitem = &undorecordfree;
259 
260     ef->markblock = new textblock;
261     memset(ef->markblock, 0, sizeof(textblock));
262 
263     ef->sx = ef->x = ef->sy = ef->y = 0;
264     ef->modified = ef->markmode = ef->showmarked = false;
265     ef->id = id;
266     ef->ncolorscheme = 0;
267 
268     files->add(ef);
269     return files->count-1;
270 }
271 
modification(tundoaction action,const string & data,bool connected,int curx,int cury)272 void texteditor::modification(tundoaction action, const string &data, bool connected, int curx, int cury) {
273     if(undolog && !data.empty()) {
274 	undorecord *ur = new undorecord;
275 	ur->x = curx < 0 ? CURCOL  : curx;
276 	ur->y = cury < 0 ? CURLINE : cury;
277 	ur->action = action;
278 	ur->data = data;
279 	ur->prevconnected = connected;
280 	curfile->undo->add(ur);
281     }
282 
283     abscol = CURCOL;
284     modified = true;
285     scancomments(true);
286 }
287 
setcoords(int nx1,int ny1,int nx2,int ny2)288 void texteditor::setcoords(int nx1, int ny1, int nx2, int ny2) {
289     x1 = nx1; x2 = nx2;
290     y1 = ny1; y2 = ny2;
291     if(curfile) setpos(CURCOL, CURLINE);
292 }
293 
addscheme(int nc,int bc,int fbold,...)294 int texteditor::addscheme(int nc, int bc, int fbold, ...) {
295     va_list ap;
296     int p, nscheme = colorschemes.size();
297 
298     va_start(ap, fbold);
299     colorscheme s;
300 
301     s.ncolor = nc;
302     s.blockcolor = bc;
303     s.bold = fbold;
304 
305     while((p = va_arg(ap, int)) != 0) s.difcolors.push_back(p);
306 
307     colorschemes.push_back(s);
308     return nscheme;
309 }
310 
addhighlight(int nscheme,string text,int color,hl_kind kind)311 void texteditor::addhighlight(int nscheme, string text, int color, hl_kind kind) {
312     int i;
313     hlight h;
314 
315     if(nscheme >= 0 && nscheme < colorschemes.size()) {
316 	colorscheme &s = colorschemes[nscheme];
317 
318 	if(kind == h_quotes) {
319 	    s.synt_quote = text;
320 	    s.qcolor = color;
321 
322 	    if((i = text.find(" ")) != -1) {
323 		s.synt_qescape = text.substr(i+1);
324 		s.synt_quote.resize(i);
325 	    }
326 	} else {
327 	    h.text = text;
328 	    h.color = color;
329 	    h.kind = kind;
330 	    s.hl.push_back(h);
331 	    sort(s.hl.begin(), s.hl.end());
332 	}
333     }
334 }
335 
addcolordif(int nscheme,int pairno)336 void texteditor::addcolordif(int nscheme, int pairno) {
337     if(nscheme >= 0 && nscheme < colorschemes.size()) {
338 	colorscheme &s = colorschemes[nscheme];
339 	s.difcolors.push_back(pairno);
340     }
341 }
342 
setcolorscheme(int nscheme)343 void texteditor::setcolorscheme(int nscheme) {
344     setcolorscheme(getfnum(), nscheme);
345 }
346 
setcolorscheme(int nfn,int nscheme)347 void texteditor::setcolorscheme(int nfn, int nscheme) {
348     editfile *f = (editfile *) files->at(nfn);
349 
350     if(f) {
351 	f->ncolorscheme = nscheme;
352 	if(nfn == getfnum()) {
353 	    colors = colorschemes[f->ncolorscheme];
354 	}
355     }
356 }
357 
addblock(int x1,int y1,int x2,int y2,int color)358 void texteditor::addblock(int x1, int y1, int x2, int y2, int color) {
359     textblock *tb = new textblock;
360     tb->x1 = x1;
361     tb->y1 = y1;
362     tb->x2 = x2;
363     tb->y2 = y2;
364     tb->color = color;
365     curfile->blocks->add(tb);
366 }
367 
startmark()368 void texteditor::startmark() {
369     CHECKLOADED;
370 
371     if(!curfile->markmode) {
372 	curfile->markmode = curfile->showmarked = true;
373 	curfile->markreverse = false;
374 	curfile->markblock->x1 = curfile->markblock->x2 = CURCOL;
375 	curfile->markblock->y1 = curfile->markblock->y2 = CURLINE;
376 	curfile->markblock->color = colors.blockcolor;
377 	draw();
378     }
379 }
380 
endmark()381 void texteditor::endmark() {
382     CHECKLOADED;
383 
384     if(curfile->markmode) {
385 	marktext();
386 	curfile->markmode = false;
387     }
388 }
389 
marktext()390 void texteditor::marktext() {
391     bool corrx, corry, sameline;
392     CHECKLOADED;
393 
394     if(curfile->markreverse) {
395 	corrx = curfile->markblock->x2 >= CURCOL;
396 	corry = curfile->markblock->y2 >= CURLINE;
397 	sameline = curfile->markblock->y2 == CURLINE;
398     } else {
399 	corrx = curfile->markblock->x1 <= CURCOL;
400 	corry = curfile->markblock->y1 <= CURLINE;
401 	sameline = curfile->markblock->y1 == CURLINE;
402     }
403 
404     if((corry && !corrx && sameline) || !corry) {
405 	curfile->markreverse = !curfile->markreverse;
406     }
407 
408     if(curfile->markreverse) {
409 	curfile->markblock->x1 = CURCOL;
410 	curfile->markblock->y1 = CURLINE;
411     } else {
412 	curfile->markblock->x2 = CURCOL;
413 	curfile->markblock->y2 = CURLINE;
414     }
415 
416     draw();
417     updatecursor();
418 }
419 
copymark(FILE * f)420 void texteditor::copymark(FILE *f) {
421     int i;
422     CHECKLOADED;
423 
424     for(i = curfile->markblock->y1; i <= curfile->markblock->y2; i++) {
425 
426 	if(i == curfile->markblock->y1 && curfile->markblock->y1 == curfile->markblock->y2) {
427 
428 	    char *p = strdup((char *) curfile->lines->at(i) + curfile->markblock->x1);
429 	    p[curfile->markblock->x2-curfile->markblock->x1] = 0;
430 	    fprintf(f, "%s", p);
431 	    free(p);
432 
433 	} else if(i == curfile->markblock->y1) {
434 
435 	    fprintf(f, "%s\n", (char *) curfile->lines->at(i) + curfile->markblock->x1);
436 
437 	} else if(i == curfile->markblock->y2) {
438 
439 	    if(curfile->markblock->x2) {
440 		char *p = strdup((char *) curfile->lines->at(i));
441 		p[curfile->markblock->x2] = 0;
442 		fprintf(f, "%s", p);
443 		if(!CURCOL) fprintf(f, "\n");
444 		free(p);
445 	    }
446 
447 	} else {
448 
449 	    fprintf(f, "%s\n", (char *) curfile->lines->at(i));
450 
451 	}
452     }
453 }
454 
copymark(char * p,int maxlen)455 void texteditor::copymark(char *p, int maxlen) {
456 }
457 
delmark()458 void texteditor::delmark() {
459     CHECKLOADED;
460 
461     int i, newcol, newrow, line = 0;
462     char *c, *p, *sl, *el;
463     string deltext;
464     textblock *mb = curfile->markblock;
465 
466     if(!mb->x1 && !mb->x2 && !mb->y1 && !mb->y2) return;
467 
468     for(i = mb->y1; i <= mb->y2; i++) {
469 	c = (char *) curfile->lines->at(i-line);
470 
471 	if((i == mb->y1) && (i == mb->y2)) {
472 
473 	    deltext = c+mb->x1;
474 	    deltext.resize(mb->x2-mb->x1);
475 
476 	    p = new char[strlen(c)];
477 	    strncpy(p, c, newcol = mb->x1);
478 	    strcpy(p + mb->x1, c + mb->x2);
479 	    p[strlen(c) - mb->x2 + mb->x1] = 0;
480 	    curfile->lines->replace(newrow = i, p);
481 
482 	} else if(i == mb->y1) {
483 
484 	    deltext = c+mb->x1;
485 
486 	    sl = strdup(c);
487 	    sl[mb->x1] = 0;
488 
489 	} else {
490 	    char *lch = (char *) curfile->lines->at(i-line);
491 
492 	    deltext += "\n";
493 	    if(i == mb->y1) deltext += lch+mb->x1; else deltext += lch;
494 
495 	    if(i == mb->y2) {
496 		deltext.resize(deltext.size()-strlen(lch)+mb->x2);
497 		el = strdup(c + mb->x2);
498 		p = new char[strlen(sl)+strlen(el)+1];
499 		strcpy(p, sl);
500 		strcat(p, el);
501 		curfile->lines->remove(i-line);
502 		curfile->lines->replace(newrow = i-line-1, p);
503 		newcol = strlen(sl);
504 		free(sl);
505 		free(el);
506 	    } else {
507 		curfile->lines->remove(i-line);
508 		line++;
509 	    }
510 	}
511     }
512 
513     modification(udelblock, deltext, false, mb->x1, mb->y1);
514     memset(curfile->markblock, 0, sizeof(textblock));
515     setpos(newcol, newrow);
516     draw();
517 }
518 
clearmark()519 void texteditor::clearmark() {
520     CHECKLOADED;
521     memset(curfile->markblock, 0, sizeof(textblock));
522     draw();
523 }
524 
insert(FILE * f)525 void texteditor::insert(FILE *f) {
526     struct stat sb;
527     int fsize;
528     char *buf;
529 
530     if(f) {
531 	fseek(f, 0, SEEK_SET);
532 	fstat(fileno(f), &sb);
533 	buf = new char[(fsize = sb.st_size)+1];
534 	fread(buf, fsize, 1, f);
535 	buf[fsize] = 0;
536 	insert(buf);
537 	delete buf;
538     }
539 }
540 
insert(const string abuf)541 void texteditor::insert(const string abuf) {
542     CHECKLOADED;
543 
544     string sbuf;
545     vector<string> lst;
546     vector<string>::iterator is;
547 
548     sbuf = abuf;
549 
550     if(sbuf.find_first_of("\n\t") != -1) {
551 	breakintolines(sbuf, lst, 0);
552 	for(sbuf = "", is = lst.begin(); is != lst.end(); is++) {
553 	    sbuf += *is + "\n";
554 	}
555     }
556 
557     if(!sbuf.empty()) {
558 	char *sl = strdup(CURSTRING), *el = strdup(CURSTRING+CURCOL), buf[1024], *s;
559 	const char *curpos = sbuf.c_str();
560 	bool firstpass = true;
561 	int line = 0;
562 
563 	sl[CURCOL] = 0;
564 
565 	while(1) {
566 	    if(!firstpass) {
567 		if(curpos = strchr(curpos, '\n')) curpos++;
568 		else break;
569 	    }
570 
571 	    strncpy(buf, curpos, 1024);
572 	    if(s = strchr(buf, '\n')) *s = 0;
573 	    curpos += strlen(buf);
574 
575 	    if(!line++) {
576 		strinsert(buf, 0, sl);
577 		if(!*curpos) strcat(buf, el);
578 		curfile->lines->replace(CURLINE, strdup(buf));
579 	    } else {
580 		if(!*curpos) strcat(buf, el);
581 		curfile->lines->insert(CURLINE+line, strdup(buf));
582 	    }
583 
584 	    firstpass = false;
585 	}
586 
587 	delete el;
588 	delete sl;
589 
590 	modification(uinsblock, sbuf);
591     }
592 }
593 
sethlcolor(int n)594 void texteditor::sethlcolor(int n) {
595     int at = colors.bold;
596 
597     if(!n) n = colors.ncolor;
598 
599     if(::find(colors.difcolors.begin(), colors.difcolors.end(), n) !=
600     colors.difcolors.end())
601 	at = !at;
602 
603     attrset(at ? boldcolor(n) : normalcolor(n));
604 }
605 
draw_print(char * buf,int bcolor,int distance)606 void texteditor::draw_print(char *buf, int bcolor, int distance) {
607     if(outx + strlen(buf) > distance) buf[distance-outx] = 0;
608     if(buf[0]) {
609 	sethlcolor(bcolor);
610 	printw("%s", buf);
611 	outx += strlen(buf);
612 	buf[0] = 0;
613     }
614 }
615 
dstralone(const char * buf,const char * startword,int wordlen,const char * delim)616 int dstralone(const char *buf, const char *startword, int wordlen, const char *delim) {
617     int leftdelim = 0, rightdelim = 0;
618     const char *si;
619 
620     for(si = startword-1; si != buf && *si < 32; si--);
621     if(si >= buf) leftdelim = (strchr(delim, *si) != 0); else leftdelim = 1;
622 
623     for(si = startword + wordlen; *si && *si < 32; si++);
624     if(*si) rightdelim = (strchr(delim, *si) != 0); else rightdelim = 1;
625 
626     return leftdelim && rightdelim;
627 }
628 
scancomments(bool visible)629 void texteditor::scancomments(bool visible) {
630     int sl, el, i;
631     const char *rsub, *lsub;
632 
633     curfile->blocks->empty();
634 
635     if(visible) {
636 	sl = curfile->sy;
637 	el = curfile->sy+y2-y1;
638 	if(el > curfile->lines->count) el = curfile->lines->count;
639     } else {
640 	sl = 0;
641 	el = curfile->lines->count;
642     }
643 
644     vector<hlight>::iterator hi = ::find(colors.hl.begin(), colors.hl.end(), h_comment);
645 
646     if(hi != colors.hl.end()) {
647 	string lc, rc, comment = hi->text;
648 	struct textblock *tb = 0;
649 
650 	lc = getword(comment, " ");
651 	rc = getword(comment, " ");
652 
653 	for(i = sl; i < el; i++) {
654 	    char *p = (char *) curfile->lines->at(i);
655 	    const char *sub = p;
656 
657 	    while(1) {
658 		lsub = strqstr(sub, lc.c_str(), colors.synt_quote.c_str());
659 
660 		if(tb) rsub = strstr(sub, rc.c_str());
661 		else rsub = strqstr(sub, rc.c_str(), colors.synt_quote.c_str());
662 
663 		if(rsub && (((rsub < lsub) && lsub) || !lsub)) {
664 		    sub = rsub;
665 		    if(!tb) {
666 			tb = new textblock;
667 			tb->color = hi->color;
668 			tb->y1 = 0;
669 			tb->x1 = 0;
670 		    }
671 		    tb->y2 = i;
672 		    tb->x2 = sub-p+rc.size();
673 		    curfile->blocks->add(tb);
674 		    tb = 0;
675 		    sub += rc.size();
676 		} else if(lsub) {
677 		    sub = lsub;
678 		    if(!tb) {
679 			tb = new textblock;
680 			tb->color = hi->color;
681 			tb->y1 = i;
682 			tb->x1 = sub-p;
683 		    }
684 		    sub += lc.size();
685 		} else if(lsub && rsub) {
686 		    if(!tb) {
687 			tb = new textblock;
688 			tb->color = hi->color;
689 		    }
690 		    tb->y1 = tb->y2 = i;
691 		    tb->x1 = p-lsub;
692 		    tb->x2 = p-rsub;
693 		    curfile->blocks->add(tb);
694 		    tb = 0;
695 		} else {
696 		    break;
697 		}
698 	    }
699 	}
700 
701 	if(tb) {
702 	    tb->y2 = i+1;
703 	    tb->x2 = 0;
704 	    curfile->blocks->add(tb);
705 	}
706     }
707 }
708 
hl_comment(char * cp,char * txt,int color)709 int texteditor::hl_comment(char *cp, char *txt, int color) {
710     int r;
711     const char *p;
712 
713     r = 0;
714     if(p = strqstr(cp, txt, "\"'"))
715 	r = hl_comment(cp, p-cp, strlen(cp), color);
716 
717     return r;
718 }
719 
hl_comment(char * cp,int st,int pend,int color)720 int texteditor::hl_comment(char *cp, int st, int pend, int color) {
721     int i, delcount, r;
722     char ins[5] = "\001 ";
723     int origclr = -1;
724 
725     delcount = r = 0;
726 
727     if(color && (st <= strlen(cp)) && (pend-st > 0)) {
728 /// !!!         for(i = 0; (i <= pend) && (i < strlen(cp)); i++)
729 
730 	for(i = 0; (i <= pend) && (i < strlen(cp)); i++) {
731 	    switch(cp[i]) {
732 		case 1: origclr = cp[i+++1]; break;
733 		case 2: origclr = -1; break;
734 	    }
735 	}
736 
737 	if(pend > strlen(cp))
738 	    pend = strlen(cp);
739 
740 	if(cp[pend] != 2) {
741 	    strinsert(cp, pend, "\002");
742 	    r++;
743 	}
744 
745 	if(origclr != -1) {
746 	    ins[1] = origclr;
747 	    strinsert(cp, pend+1, ins);
748 	    r += 2;
749 	}
750 
751 	for(i = st; (i < pend) && (i < strlen(cp)); i++) {
752 	    switch(cp[i]) {
753 		case 1:
754 		    strcut(cp, i--, 2);
755 		    delcount += 2;
756 		    pend -= 2;
757 		    break;
758 		case 2:
759 		    strcut(cp, i--, 1);
760 		    delcount++;
761 		    pend--;
762 		    break;
763 	    }
764 	}
765 
766 	ins[1] = color;
767 	strinsert(cp, st, ins);
768 	r += 2;
769     }
770 
771     return r-delcount;
772 }
773 
count_clrcodes(char * cp,int pos)774 int texteditor::count_clrcodes(char *cp, int pos) {
775     int i, j, k;
776     j = k = 0;
777 
778     for(i = 0; i < strlen(cp) && j < pos; i++) {
779 	if(cp[i] == 1) {
780 	    k++;
781 	    if(i++ < strlen(cp)) k++;
782 	} else if(cp[i] == 2) k++; else j++;
783     }
784 
785     return k;
786 }
787 
showline(int ln,int startx,int distance,int extrax)788 void texteditor::showline(int ln, int startx, int distance, int extrax) {
789     if(!show) return;
790 
791     int i, n, inscount, bcolor, sxinscount, printed, j, lastoccur, q, eolstart, npos, offs;
792     char *cs, *sr, *nr, *r, ins[3] = "\001 ";
793 
794     vector<int> layout;
795     vector<int>::iterator iq;
796     vector<hlight>::iterator hi;
797 
798     const char *p;
799 
800     if(!(cs = (char *) curfile->lines->at(ln))) return;
801     char *cp = (char *) malloc(i = ((strlen(cs)+1)*4)*sizeof(char));
802     char *buf = (char *) malloc(i);
803 
804     eolstart = i;
805 
806     strcpy(cp, cs);
807     buf[0] = 0;
808     inscount = sxinscount = bcolor = 0;
809 
810     highline *hline = (highline *) curfile->highlines->find(&(i = ln+1), &findhighline);
811 
812     if(hline) {
813 	ins[1] = hline->color;
814 	strinsert(cp, 0, ins);
815 	strcat(cp, "\002");
816     } else {
817 	if(strlen(cp))
818 	for(hi = colors.hl.begin(); hi != colors.hl.end(); hi++) {
819 	    ins[1] = hi->color;
820 	    p = cp;
821 	    lastoccur = 0;
822 
823 	    switch(hi->kind) {
824 		case h_alone:
825 		    for(sr = r = strdup(hi->text.c_str()); r && r != sr+hi->text.size(); ) {
826 			if(nr = strchr(r, ';')) {
827 			    *nr = 0;
828 			    nr++;
829 			}
830 
831 			if(!strlen(r)) {
832 			    r = nr;
833 			    continue;
834 			}
835 
836 			p = cp;
837 			lastoccur = 0;
838 
839 			while(p = strqstr(p+lastoccur, r, colors.synt_quote.c_str(), colors.synt_qescape.c_str())) {
840 			    if(eolstart) eolstart += lastoccur;
841 			    if(p-cp > eolstart) {
842 				r = 0;
843 				break;
844 			    }
845 
846 			    lastoccur = strlen(r);
847 			    if(dstralone(cp, p, lastoccur, ALONE_DELIM)) {
848 				strinsert(cp, p-cp+lastoccur, "\002");
849 				strinsert(cp, p-cp, ins);
850 				inscount++;
851 				lastoccur += 3;
852 			    }
853 			}
854 
855 			r = nr;
856 		    }
857 
858 		    free(sr);
859 		    break;
860 
861 		case h_symbol:
862 		    layout = getsymbolpositions(string(cp).substr(0, eolstart),
863 			hi->text, colors.synt_quote, colors.synt_qescape);
864 
865 		    for(offs = 0, iq = layout.begin(); iq != layout.end(); iq++) {
866 			offs += hl_comment(cp, *iq+offs, *iq+offs+1, hi->color);
867 		    }
868 		    break;
869 
870 		case h_block:
871 		    while(p = strqpbrk(cp, p-cp+lastoccur, hi->text.c_str(),
872 		    colors.synt_quote.c_str(), colors.synt_qescape.c_str())) {
873 			if(eolstart) eolstart += lastoccur;
874 			if(p-cp > eolstart) break;
875 			lastoccur = strspn(p, hi->text.c_str());
876 			if(dstralone(cp, p, lastoccur, ALONE_DELIM)) {
877 			    strinsert(cp, p-cp+lastoccur, "\002");
878 			    strinsert(cp, p-cp, ins);
879 			    lastoccur += 3;
880 			}
881 		    }
882 		    break;
883 
884 		case h_eol:
885 		    if((npos = find_quoted(p, hi->text, 0,
886 		    colors.synt_quote, colors.synt_qescape)) != -1)
887 			hl_comment(cp, eolstart = npos, strlen(cp),
888 			hi->color);
889 
890 		    break;
891 
892                 case h_quotes:
893                 case h_comment:
894                     /* TODO: should these be handled,
895                      * are they possible to get here at all ?
896                      */
897                     break;
898 	    }
899 	}
900 
901 	// Quotes highlight ...
902 
903 	if(!colors.synt_quote.empty()) {
904 	    bool qst;
905 
906 	    layout = getquotelayout(string(cp).substr(0, eolstart),
907 		colors.synt_quote, colors.synt_qescape);
908 
909 	    for(qst = false, offs = 0, iq = layout.begin(); iq != layout.end(); iq++) {
910 		qst = !qst;
911 
912 		if(!qst) {
913 		    offs += hl_comment(cp, *(iq-1)+offs,
914 			*iq+offs+1, colors.qcolor);
915 		}
916 	    }
917 
918 	    if(qst) {
919 		hl_comment(cp, *(layout.end()-1)+offs, strlen(cp), colors.qcolor);
920 	    }
921 	}
922 
923 	// Blocks ...
924 
925 	for(i = 0; i < curfile->blocks->count + 1; i++) {
926 	    textblock *tb;
927 
928 	    if(i == curfile->blocks->count) {
929 		if(curfile->showmarked) {
930 		    tb = curfile->markblock;
931 		} else break;
932 	    } else {
933 		tb = (textblock *) curfile->blocks->at(i);
934 	    }
935 
936 	    if(ln >= tb->y1 && ln <= tb->y2) {
937 		q = strlen(cp);
938 
939 		if(ln == tb->y1 && tb->y1 == tb->y2) {
940 		    n = count_clrcodes(cp, tb->x1) + tb->x1;
941 		    q = count_clrcodes(cp, tb->x2) + tb->x2;
942 		} else if(ln == tb->y1) {
943 		    n = count_clrcodes(cp, tb->x1) + tb->x1;
944 		} else if(ln == tb->y2) {
945 		    n = j = 0;
946 		    q = count_clrcodes(cp, tb->x2) + tb->x2;
947 		} else n = j = 0;
948 
949 		if(!(tb->x1 == tb->x2 && tb->y1 == tb->y2)) {
950 		    hl_comment(cp, n, q, tb->color);
951 		}
952 	    }
953 	}
954     }
955 
956     // let's count the amount of color codes inserted
957     // before start X position (startx variable)
958 
959     sxinscount = count_clrcodes(cp, startx);
960 
961     for(i = 0; i < startx+sxinscount && i < strlen(cp)+inscount*3; i++) {
962 	if(cp[i] == 1) bcolor = cp[++i]; else
963 	if(cp[i] == 2) bcolor = 0;
964     }
965 
966     for(i = startx+sxinscount, n = 0, outx = 0; i < strlen(cp); i++) {
967 	if(cp[i] == 1) {
968 	    draw_print(buf, bcolor, distance);
969 	    bcolor = cp[++i];
970 	    n = 0;
971 	} else if(cp[i] == 2) {
972 	    draw_print(buf, bcolor, distance);
973 	    n = bcolor = 0;
974 	} else {
975 	    buf[n++] = cp[i];
976 	    buf[n] = 0;
977 	}
978     }
979 
980     draw_print(buf, bcolor, distance);
981     if(!hline) sethlcolor(0);
982 
983     printed = strlen(cs)-startx;
984     if(printed < 0) printed = 0; else
985     if(printed > distance) printed = distance;
986     mvhline(y1+ln-curfile->sy, x1+extrax+printed, ' ', distance-printed);
987 
988     free (buf);
989     free (cp);
990 }
991 
draw(int fromline)992 void texteditor::draw(int fromline) {
993     int k;
994 
995     if(show) {
996 	if(curfile->lines) {
997 	    for(k = curfile->sy+fromline; k < curfile->lines->count && k < y2-y1+curfile->sy; k++) {
998 		kgotoxy(x1, k-curfile->sy+y1);
999 		showline(k, curfile->sx, x2-x1);
1000 	    }
1001 
1002 	    if(k < y2-y1+curfile->sy) {
1003 		sethlcolor(colors.ncolor);
1004 		for(; k < y2-y1+curfile->sy; k++) mvhline(k-curfile->sy+y1, x1, ' ', x2-x1);
1005 	    }
1006 	}
1007 
1008 	refresh();
1009     }
1010 }
1011 
draw()1012 void texteditor::draw() {
1013     if(active && curfile) {
1014 	if(curfile->lines) scancomments(true);
1015 	draw(0);
1016     }
1017 }
1018 
endofline()1019 bool texteditor::endofline() {
1020     return CURCOL == CSTRLEN;
1021 }
1022 
currentchar()1023 const char texteditor::currentchar() {
1024     char *p = CURSTRING;
1025     return p[CURCOL];
1026 }
1027 
updatecursor()1028 void texteditor::updatecursor() {
1029     if(active && curfile) {
1030 	if(curfile->y >= y2-y1) curfile->y = y2-y1-1;
1031 	if(curfile->x >= x2-x1) curfile->x = x2-x1-1;
1032 	kgotoxy(x1+curfile->x, y1+curfile->y);
1033     }
1034 }
1035 
fix_x(bool tab)1036 bool texteditor::fix_x(bool tab) {
1037     int osx = curfile->sx, clen = CSTRLEN;
1038 
1039     if(CURCOL > clen) {
1040 	if(clen-curfile->sx < 0) {
1041 	    curfile->sx = CSTRLEN/(x2-x1-1);
1042 	}
1043 	curfile->x = CSTRLEN-curfile->sx;
1044     } else {
1045 	if(abscol-curfile->sx > x2-x1-1) setpos(abscol, CURLINE);
1046     }
1047 
1048     if(tab) {
1049 	int rm = rtabmargin(true, CURCOL, CURSTRING);
1050 	char *p = CURSTRING;
1051 
1052 	/* if CURCOL is 0 we'll be outside of the array => not good */
1053 	if(CURCOL > 0)
1054 	  if(p[CURCOL-1] == ' ')
1055 	    if(strspn(p+CURCOL, " ") >= rm-CURCOL)
1056 	      if(CURCOL != ltabmargin(true, rm, p)) {
1057 		//if(rm <= curfile->sx+x2-x1) curfile->x = rm-curfile->sx;
1058 		curfile->x += rm-curfile->x;
1059 	      }
1060     }
1061 
1062     return osx != curfile->sx;
1063 }
1064 
eddel(bool usetabs)1065 void texteditor::eddel(bool usetabs) {
1066     char *p = CURSTRING;
1067     int nextlen, todelete = 1, rm;
1068     string deltext;
1069 
1070     if(p) {
1071 	if(CURCOL < strlen(p)) {
1072 	    if(usetabs && ((rm = rtabmargin(true, CURCOL, CURSTRING)) != -1)) {
1073 		deltext.append(todelete = rm-CURCOL, ' ');
1074 	    } else {
1075 		deltext = *(p+CURCOL);
1076 	    }
1077 
1078 	    modification(udelchar, deltext);
1079 	    strcut(p, CURCOL, todelete);
1080 	    int px = CURCOL, py = CURLINE;
1081 	    mergeline(CURLINE-1, false, px, py); // can't we append this one to previous, since we've shrotened it?
1082 	    mergeline(py, false, px, py); // can't we append next one to this?
1083 	    setpos(px, py);
1084 	    updatecursor();
1085 	    draw();
1086 
1087 	} else {
1088 	    int px = CURCOL, py = CURLINE;
1089 	    mergeline(py, true, px, py);
1090 
1091 	    modification(udelchar, "\n");
1092 	    setpos(px, py);
1093 	    draw();
1094 	}
1095 
1096 	updatecursor();
1097     }
1098 }
1099 
edbackspace()1100 void texteditor::edbackspace() {
1101     int i, bc;
1102 
1103     if(CURCOL) {
1104 	bool spacetoend = endofline() && !currentchar();
1105 	bool curspace = isspace(currentchar());
1106 
1107 	edmove(KEY_LEFT);
1108 
1109 	if(spacetoend) {
1110 	    bc = CSTRLEN-CURCOL;
1111 	    for(i = 0; i < bc; i++) eddel(false);
1112 	} else {
1113 	    if(!curspace && isspace(currentchar())) {
1114 		eddelword();
1115 	    } else {
1116 		eddel();
1117 	    }
1118 	}
1119     } else if(CURLINE) {
1120 	edmove(KEY_UP);
1121 	edmove(KEY_END);
1122 	eddel();
1123     }
1124 }
1125 
eddelword()1126 void texteditor::eddelword() {
1127     // This is the kkconsui original version: it does not skip whitespace
1128     char *p = CURSTRING, *e;
1129     string deltext, n;
1130     int count;
1131 
1132     if(!strlen(p)) {
1133 	eddelline();
1134     } else if(CURCOL == strlen(p)) {
1135 	if(CURLINE < curfile->lines->count-1) {
1136 	    eddel();
1137 	    if(currentchar() == ' ') eddelword();
1138 	}
1139     } else {
1140 	n = p;
1141 	deltext = n.substr(CURCOL);
1142 
1143 	if(currentchar() == ' ') {
1144 	    count = strspn(p+CURCOL, " ");
1145 	    n.replace(CURCOL, count, "");
1146 	} else {
1147 	    n = p;
1148 	    if(!(e = strpbrk(&p[CURCOL], NONCHAR_DELIM))) e = p + strlen(p);
1149 
1150 	    if((count = e-p-curfile->sx-curfile->x)) {
1151 		n.replace(CURCOL, count, "");
1152 	    } else {
1153 		count += strspn(n.substr(CURCOL).c_str(), NONCHAR_DELIM);
1154 		n.replace(CURCOL, strspn(n.c_str()+CURCOL, NONCHAR_DELIM), "");
1155 	    }
1156 	}
1157 
1158 	deltext.resize(count);
1159 	curfile->lines->replace(CURLINE, strdup(n.c_str()));
1160 	modification(udelchar, deltext);
1161 	int px = CURCOL, py = CURLINE;
1162 	mergeline(py-1, false, px, py);
1163 	mergeline(py, false, px, py);
1164 	setpos(px, py);
1165 	draw();
1166 	updatecursor();
1167     }
1168 }
1169 
eddelwordemacs()1170 void texteditor::eddelwordemacs() {
1171     // This is the "emacs-compliant" version, it skips all whitespace
1172     char *p = CURSTRING, *e;
1173     string deltext, n;
1174     int count = 0;
1175 
1176     if(!strlen(p)) {
1177 	if(CURLINE < curfile->lines->count-1) {
1178 	    eddelline();
1179 	    eddelwordemacs();
1180 	}
1181     } else if(CURCOL == strlen(p)) {
1182 	if(CURLINE < curfile->lines->count-1) {
1183 	    eddel();
1184 	    if(currentchar() == ' ') eddelword();
1185 	}
1186     } else {
1187 	n = p;
1188 	deltext = n.substr(CURCOL);
1189 
1190 	// skip whitespace
1191 	if(currentchar() == ' ') {
1192 	    count = strspn(p+CURCOL, " ");
1193 	    n.replace(CURCOL, count, "");
1194 	    curfile->lines->replace(CURLINE, strdup(n.c_str()));
1195 	    int px = CURCOL, py = CURLINE;
1196 	    mergeline(py-1, false, px, py);
1197 	    mergeline(py, false, px, py);
1198 	    setpos(px, py);
1199 	}
1200 
1201 	n = p = CURSTRING;
1202 
1203 	if(!(e = strpbrk(&p[CURCOL], NONCHAR_DELIM))) e = p + strlen(p);
1204 
1205 	if((count = e-p-curfile->sx-curfile->x)) {
1206 	    n.replace(CURCOL, count, "");
1207 	} else {
1208 	    count += strspn(n.substr(CURCOL).c_str(), NONCHAR_DELIM);
1209 	    n.replace(CURCOL, strspn(n.c_str()+CURCOL, NONCHAR_DELIM), "");
1210 	}
1211 
1212 	deltext.resize(count);
1213 	curfile->lines->replace(CURLINE, strdup(n.c_str()));
1214 	modification(udelchar, deltext);
1215 
1216 	int px = CURCOL, py = CURLINE;
1217 	mergeline(py-1, false, px, py);
1218 	mergeline(py, false, px, py);
1219 	setpos(px, py);
1220 	draw();
1221 	updatecursor();
1222     }
1223 }
1224 
eddelline()1225 void texteditor::eddelline() {
1226     char *p = (char *) curfile->lines->at(CURLINE);
1227     string deltext = (string) p + "\n";
1228 
1229     if(CURLINE+1 < curfile->lines->count) {
1230 	int px = 0, py = CURLINE;
1231 	curfile->lines->remove(CURLINE);
1232 	mergeline(py-1, false, px, py);
1233 
1234 	if(!curfile->lines->count) {
1235 	    curfile->sy = curfile->sx = curfile->y = curfile->x = 0;
1236 	    char *p = strdup("");
1237 	    curfile->lines->add(p);
1238 	} else if(CURLINE >= curfile->lines->count) {
1239 	    edmove(KEY_UP);
1240 	}
1241     } else {
1242 	char *p = strdup("");
1243 	curfile->lines->replace(curfile->lines->count-1, p);
1244     }
1245 
1246     shiftmarkedblock(-1);
1247     modification(udelchar, deltext, false, 0);
1248     edmove(KEY_HOME);
1249     abscol = 0;
1250     draw();
1251     updatecursor();
1252 }
1253 
eddelbegofline()1254 void texteditor::eddelbegofline() {
1255     char *p = CURSTRING;
1256     string deltext, n;
1257 
1258     if(CURCOL == 0 && CURLINE) { // We're at the beginning of the line
1259 	edmove(KEY_UP);
1260 	edmove(KEY_END);
1261 	eddel();
1262     } else if(CURCOL == strlen(p)) { // We're at the end
1263 	eddelline();
1264     } else {
1265 	n = p;
1266 	deltext = (string) p;
1267 
1268 	n.replace(curfile->sx, CURCOL, "");
1269 
1270 	edmove(KEY_HOME);
1271 
1272 	curfile->lines->replace(CURLINE, strdup(n.c_str()));
1273 	modification(udelchar, deltext);
1274 	int px = 0, py = CURLINE;
1275 	mergeline(py-1, false, px, py);
1276 	mergeline(CURLINE, false, px, py);
1277 	draw();
1278 	updatecursor();
1279     }
1280 
1281 }
1282 
eddelendofline()1283 void texteditor::eddelendofline() {
1284     char *p = CURSTRING;
1285     string deltext, n;
1286     int count = 0;
1287 
1288     if(CURCOL == 0) { // We're at the beginning of the line
1289 	eddelline();
1290     } else if (CURCOL == strlen(p)) { // We're at the end
1291 	eddel();
1292     } else {
1293 	n = p;
1294 	deltext = (string) p + "\n";
1295 
1296 	count = strlen(p) - CURCOL;
1297 	n.replace(CURCOL, count, "");
1298 
1299 	curfile->lines->replace(CURLINE, strdup(n.c_str()));
1300 	modification(udelchar, deltext);
1301 	draw(curfile->y);
1302 	updatecursor();
1303     }
1304 
1305 }
1306 
edtransposechar()1307 void texteditor::edtransposechar() {
1308     char *p = CURSTRING;
1309     string deltext;
1310     char tmp;
1311 
1312     if (CURCOL == 0) return;
1313     else if (CURCOL == strlen(p)) {
1314 	deltext = (string) p;
1315 
1316 	tmp = p[CURCOL-2];
1317 	p[CURCOL-2] = p[CURCOL-1];
1318 	p[CURCOL-1] = tmp;
1319 
1320 	curfile->lines->replace(CURLINE, strdup(p));
1321 	modification(udelchar, deltext);
1322 	draw(curfile->y);
1323 	updatecursor();
1324     } else {
1325 	deltext = (string) p;
1326 
1327 	tmp = p[CURCOL-1];
1328 	p[CURCOL-1] = p[CURCOL];
1329 	p[CURCOL] = tmp;
1330 
1331 	setpos(CURCOL+1, CURLINE);
1332 
1333 	curfile->lines->replace(CURLINE, strdup(p));
1334 	modification(udelchar, deltext);
1335 	draw(curfile->y);
1336 	updatecursor();
1337     }
1338 
1339 }
1340 
edenter(bool countspaces)1341 void texteditor::edenter(bool countspaces) {
1342     char *p = CURSTRING, *r;
1343     string spaceins;
1344 
1345     if(wrap) strimtrail(p);
1346 
1347     int oldsx = curfile->sx;
1348     int spacecount = strspn(p, " ");
1349     int nextlen = CSTRLEN-CURCOL+spacecount+1;
1350 
1351     char *nextstr = (char *) malloc(nextlen < 1 ? 1 : nextlen);
1352     if(CURCOL < spacecount) spacecount = 0;
1353 
1354     if(!countspaces) spacecount = 0;
1355     r = strlen(p) > CURCOL ? p+CURCOL : p+strlen(p);
1356     sprintf(nextstr, "%-*s%s", spacecount, "", r);
1357 
1358     curfile->lines->insert(CURLINE+2, nextstr);
1359 
1360     modification(uinschar, "\n");
1361     spaceins.append(spacecount, ' ');
1362     modification(uinschar, spaceins);
1363 
1364     curfile->sx = p[CURCOL] = 0;
1365     curfile->x = spacecount;
1366 
1367     if(curfile->x > x2-x1) {
1368 	curfile->sx = curfile->x-(x2-x1);
1369 	curfile->x -= curfile->sx;
1370     }
1371 
1372     // Shift the marked block down if ENTER was
1373     // pressed on the line above it
1374 
1375     shiftmarkedblock(1);
1376 
1377     if(curfile->y+1 < y2-y1) {
1378 	if(curfile->sx != oldsx) draw(); else draw(curfile->y);
1379     }
1380 
1381     abscol = CURCOL;
1382     edmove(KEY_DOWN);
1383 }
1384 
edmove(int k,int options)1385 void texteditor::edmove(int k, int options) {
1386     int i, lm;
1387     bool fdraw = false;
1388     bool ctrlpressed = (options & EM_CTRL) && (getctrlkeys() & CONTROL_PRESSED);
1389     bool shiftpressed = (getctrlkeys() & SHIFT_PRESSED);
1390     char *p = CURSTRING;
1391 
1392     if(options & EM_MANUAL) {
1393 	if(shiftpressed != prevshift) {
1394 	    if(shiftpressed && !curfile->markmode) startmark();
1395 	    if(!shiftpressed && curfile->markmode) endmark();
1396 	}
1397 	prevshift = shiftpressed;
1398     }
1399 
1400     if(curfile->lines->count) {
1401 	switch(k) {
1402 	    case KEY_UP:
1403 		if(curfile->y) {
1404 		    curfile->y--;
1405 		} else if(CURLINE) {
1406 		    i = CURLINE-1;
1407 		    // sy -= (y2-y1)/2;
1408 		    curfile->sy--;
1409 		    if(curfile->sy < 0) curfile->sy = 0;
1410 		    curfile->y = i-curfile->sy;
1411 		    fdraw = true;
1412 		}
1413 
1414 		setpos(abscol, CURLINE);
1415 		if(fix_x(options & EM_TAB) || fdraw) draw();
1416 		updatecursor();
1417 		break;
1418 
1419 	    case KEY_DOWN:
1420 		if(CURLINE < curfile->lines->count-1) {
1421 		    if(curfile->y+1 < y2-y1) {
1422 			curfile->y++;
1423 		    } else {
1424 			i = CURLINE+1;
1425 			// sy += (y2-y1)/2;
1426 			curfile->sy++;
1427 			if(curfile->lines->count-curfile->sy < y2-y1) curfile->sy = curfile->lines->count-y2+y1;
1428 			curfile->y = i-curfile->sy;
1429 			fdraw = true;
1430 		    }
1431 
1432 		    setpos(abscol, CURLINE);
1433 		    if(fix_x(options & EM_TAB) || fdraw) draw();
1434 		    updatecursor();
1435 		}
1436 		break;
1437 
1438 	    case KEY_LEFT:
1439 		if(ctrlpressed) {
1440 		    char *p = CURSTRING, *r, *s = p+CURCOL;
1441 
1442 		    if(p == s) r = 0; else {
1443 			for(r = s; (r != p) && !strchr(NONCHAR_DELIM, *r); r--);
1444 			for(; (r != p) && strchr(NONCHAR_DELIM, *r); r--);
1445 			for(; (r != p) && !strchr(NONCHAR_DELIM, *r); r--);
1446 
1447 			if(strchr(NONCHAR_DELIM, *r)) {
1448 			    if(r == p) r = 0; else r++;
1449 			}
1450 		    }
1451 
1452 		    if(r) {
1453 			if((curfile->x -= s-r) < 0) {
1454 			    curfile->sx += curfile->x;
1455 			    curfile->x = 0;
1456 			    draw();
1457 			    updatecursor();
1458 			}
1459 		    } else if(CURLINE) {
1460 			setpos(strlen((char *) curfile->lines->at(CURLINE-1)), CURLINE-1);
1461 		    }
1462 		} else {
1463 		/*
1464 		    if(CURCOL && (options & EM_TAB) && ((lm = ltabmargin(true, -1, CURSTRING)) != -1)) {
1465 			setpos(lm, CURLINE);
1466 		    } else
1467 		*/
1468 		    if(curfile->x) {
1469 			curfile->x--;
1470 
1471 			char *p = CURSTRING;
1472 
1473 			if(CURCOL && (options & EM_TAB))
1474 			if((lm = ltabmargin(true, CURCOL, p)) != -1)
1475 			if(CURCOL+1 != lm)
1476 			if(currentchar() == ' ') setpos(lm, CURLINE);
1477 		    } else if(curfile->sx) {
1478 			i = CURCOL;
1479 			curfile->sx -= (x2-x1)/3;
1480 			if(curfile->sx < 0) curfile->sx = 0;
1481 			curfile->x = i-curfile->sx-1;
1482 			draw();
1483 		    } else if(CURLINE) {
1484 			edmove(KEY_UP);
1485 			edmove(KEY_END);
1486 		    }
1487 		}
1488 		break;
1489 
1490 	    case KEY_RIGHT:
1491 		if(ctrlpressed) {
1492 		    char *p = CURSTRING+CURCOL+1, *r = 0;
1493 
1494 		    if(*(CURSTRING+CURCOL)) {
1495 			if(r = strpbrk(p, NONCHAR_DELIM))
1496 			for(; *r && strchr(NONCHAR_DELIM, *r); r++);
1497 		    }
1498 
1499 		    if(r) {
1500 			curfile->x += r-p+1;
1501 		    } else {
1502 			if(endofline() && (CURLINE < curfile->lines->count-1)) {
1503 			    setpos(0, CURLINE+1);
1504 			} else {
1505 			    curfile->x = CSTRLEN;
1506 			}
1507 		    }
1508 		} else {
1509 		    if(CSTRLEN > CURCOL) {
1510 			curfile->x++;
1511 			fix_x(options & EM_TAB);
1512 		    } else if(CURLINE < curfile->lines->count-1) {
1513 			edmove(KEY_DOWN);
1514 			edmove(KEY_HOME);
1515 		    }
1516 		}
1517 		break;
1518 
1519 	    case KEY_HOME:
1520 		i = curfile->sx;
1521 
1522 		if(ctrlpressed) {
1523 		    curfile->y = 0;
1524 		    curfile->sx = curfile->x = 0;
1525 		} else if(curfile->x) {
1526 		    curfile->sx = curfile->x = 0;
1527 		}
1528 
1529 		if(curfile->sx < i) draw();
1530 		updatecursor();
1531 		break;
1532 
1533 	    case KEY_END:
1534 		if(ctrlpressed) {
1535 		    i = curfile->sx;
1536 		    curfile->y = y2-y1-1;
1537 		    curfile->sx = curfile->x = 0;
1538 
1539 		    if(CURLINE >= curfile->lines->count) {
1540 			curfile->y = curfile->lines->count-curfile->sy-1;
1541 		    }
1542 
1543 		    if(i) draw();
1544 		} else if(CURCOL != CSTRLEN) {
1545 		    setpos(CSTRLEN, CURLINE);
1546 		}
1547 
1548 		updatecursor();
1549 		break;
1550 
1551 	    case KEY_PPAGE:
1552 		if(ctrlpressed) {
1553 		    setpos(0, 0);
1554 		    abscol = 0;
1555 		} else if(CURLINE) {
1556 		    if(curfile->y-y2-y1 < 0 && !curfile->sy) curfile->y = 0; else {
1557 			curfile->sy = curfile->sy-y2+y1;
1558 			if(curfile->sy < 0) curfile->sy = 0;
1559 		    }
1560 
1561 		    fix_x(options & EM_TAB);
1562 		    draw();
1563 		    updatecursor();
1564 		}
1565 		break;
1566 
1567 	    case KEY_NPAGE:
1568 		if(ctrlpressed) {
1569 		    setpos(0, curfile->lines->count-1);
1570 		    abscol = 0;
1571 		} else if(CURLINE != curfile->lines->count) {
1572 		    if(curfile->sy + y2-y1 == curfile->lines->count) curfile->y = y2-y1-1; else {
1573 			curfile->sy += y2-y1;
1574 			if(curfile->sy+y2-y1 > curfile->lines->count) curfile->sy = curfile->lines->count-y2+y1;
1575 			if(curfile->sy < 0) {
1576 			    curfile->sy = 0;
1577 			    curfile->y = curfile->lines->count-1;
1578 			}
1579 		    }
1580 
1581 		    fix_x(options & EM_TAB);
1582 		    draw();
1583 		}
1584 		break;
1585 	    case KEY_EMACS_BEG_OF_BUFFER:
1586 		setpos(0, 0);
1587 		break;
1588 	    case KEY_EMACS_END_OF_BUFFER:
1589 		p = (char *) curfile->lines->at(curfile->lines->count-1);
1590 		i = strlen(p);
1591 		setpos(i, curfile->lines->count-1);
1592 		break;
1593 	    case KEY_EMACS_FORWARD_WORD:
1594 		if(endofline() && CURLINE < curfile->lines->count-1) {
1595 		    setpos(0, CURLINE+1);
1596 		    p = CURSTRING;
1597 		    if(strchr(NONCHAR_DELIM, p[CURCOL])) {
1598 			i = strspn(p+CURCOL, NONCHAR_DELIM);
1599 			setpos(CURCOL+i, CURLINE);
1600 		    }
1601 		} else if(endofline() && CURLINE == curfile->lines->count-1)
1602 		    return;
1603 		i = 0;
1604 		if(strchr(NONCHAR_DELIM, p[CURCOL]))
1605 		    i = strspn(p+CURCOL, NONCHAR_DELIM);
1606 		i += strcspn(p+CURCOL+i, NONCHAR_DELIM);
1607 		setpos(CURCOL+i, CURLINE);
1608 		break;
1609 	    case KEY_EMACS_BACKWARD_WORD:
1610 		if(CURCOL == 0 && CURLINE > 0 && curfile->lines->count) {
1611 		    setpos(strlen((char *)curfile->lines->at(CURLINE-1)),
1612 			   CURLINE-1);
1613 		}
1614 
1615 		if(CURCOL > 0) {
1616 		    char *s;
1617 		    int len, offset = 0;
1618 
1619 		    p = CURSTRING;
1620 		    len = CURCOL;
1621 		    s = (char *)malloc(len+1);
1622 
1623 		    for(i = 0; i < len; i++) {
1624 			s[i] = p[len-i-1];
1625 		    }
1626 		    s[len] = '\0';
1627 
1628 		    if(strchr(NONCHAR_DELIM, p[CURCOL]) ||
1629 		       strchr(NONCHAR_DELIM, p[CURCOL-1]))
1630 			offset = strspn(s, NONCHAR_DELIM);
1631 
1632 		    offset += strcspn(s+offset, NONCHAR_DELIM);
1633 
1634 		    if(offset == CURCOL && strchr(NONCHAR_DELIM, p[0])) {
1635 			setpos(strlen((char *)curfile->lines->at(CURLINE-1)),
1636 			       CURLINE-1);
1637 			edmove(KEY_EMACS_BACKWARD_WORD);
1638 		    } else
1639 			setpos(CURCOL-offset, CURLINE);
1640 
1641 		    if(s) free(s);
1642 		}
1643 		break;
1644 	}
1645     }
1646 
1647     if(curfile->x >= x2-x1)
1648     switch(k) {
1649 
1650     case KEY_RIGHT:
1651     case KEY_LEFT:
1652 
1653 	i = CURCOL;
1654 	curfile->sx += (x2-x1)/3;
1655 	curfile->x = i-curfile->sx;
1656 	draw();
1657 	updatecursor();
1658 	break;
1659     }
1660 
1661     if(options & EM_MANUAL)
1662     switch(k) {
1663 	case KEY_LEFT:
1664 	case KEY_RIGHT:
1665 	case KEY_HOME:
1666 	case KEY_END: abscol = CURCOL; break;
1667 	default: if(abscol <= 0) abscol = CURCOL; break;
1668     }
1669 
1670     if(options & EM_MANUAL) {
1671 	if(curfile->markmode) marktext();
1672     }
1673 }
1674 
inschar(int k)1675 void texteditor::inschar(int k) {
1676     if(k == '\t') {
1677 
1678 	int rm = rtabmargin(true, CURCOL, CURSTRING)-CURCOL, i;
1679 	if(rm < 0) rm = rtabmargin(true, CURCOL)-CURCOL;
1680 	for(i = 0; i < rm; i++) inschar(' ');
1681 
1682     } else {
1683 
1684 	char *p = CURSTRING;
1685 	int len = strlen(p);
1686 	char *n = (char *) malloc(len+5);
1687 	char np[2];
1688 
1689 	strcpy(n, p);
1690 	sprintf(np, "%c", k);
1691 	strinsert(n, CURCOL, np);
1692 
1693 	if(wrap && strlen(n) > x2-x1-1) {
1694 	    int cx = CURCOL+1;
1695 	    int cy = CURLINE;
1696 	    curfile->lines->replace(CURLINE, n);
1697 	    modification(uinschar, np);
1698 	    wrapline(CURLINE, cx, cy); // check for line wrap
1699 	    setpos(cx, cy);
1700 	    draw();
1701 
1702 	} else {
1703 	    curfile->lines->replace(CURLINE, n);
1704 	    modification(uinschar, np);
1705 
1706 	    UPDATECURRENTLINE;
1707 	    edmove(KEY_RIGHT, 0);
1708 	}
1709 
1710 	updatecursor();
1711 	abscol = CURCOL;
1712     }
1713 }
1714 
setpos(int col,int line)1715 void texteditor::setpos(int col, int line) {
1716     bool drawneeded = false;
1717 
1718     CHECKLOADED;
1719 
1720     if(line >= curfile->lines->count)
1721 	line = curfile->lines->count-1;
1722 
1723     if(line < 0)
1724 	line = 0;
1725 
1726     if((line >= curfile->sy) && (line < curfile->sy+y2-y1)) {
1727 	curfile->y = line-curfile->sy;
1728     } else {
1729 	if((curfile->sy = line-(y2-y1)/2) < 0) curfile->sy = 0;
1730 	if(curfile->sy+y2-y1-1 > curfile->lines->count) {
1731 	    curfile->sy = curfile->lines->count-y2+y1;
1732 	}
1733 
1734 	if(curfile->sy < 0) curfile->sy = 0;
1735 	curfile->y = line-curfile->sy;
1736 	drawneeded = true;
1737     }
1738 
1739     if((col >= curfile->sx) && (col < curfile->sx+x2-x1)) {
1740 	curfile->x = col-curfile->sx;
1741     } else {
1742 	char *p = CURSTRING;
1743 	if(col > strlen(p)) col = strlen(p);
1744 	curfile->sx = 0;
1745 
1746 	if((curfile->x = col) > x2-x1-1) {
1747 	    curfile->sx = curfile->x-(x2-x1)/2;
1748 	    curfile->x -= curfile->sx;
1749 	}
1750 	drawneeded = true;
1751     }
1752 
1753     if(drawneeded) draw();
1754     updatecursor();
1755 }
1756 
getpos(int * col,int * line)1757 void texteditor::getpos(int *col, int *line) {
1758     if(col) *col = CURCOL;
1759     if(line) *line = CURLINE;
1760 }
1761 
open()1762 int texteditor::open() {
1763     int k, l, go;
1764 
1765     if((x2-x1 < 2) || (y2-y1 < 2)) {
1766 	return 0;
1767     }
1768 
1769     if(fn < 0 || fn > files->count) setfnum(0);
1770 
1771     active = true;
1772 
1773     if(curfile) if(curfile->lines) {
1774 	if(!curfile->lines->count) {
1775 	    char *p = strdup("");
1776 	    curfile->lines->add(p);
1777 	}
1778 
1779 	if(CURCOL > CSTRLEN) fix_x(true);
1780 	draw();
1781 	updatecursor();
1782     }
1783 
1784     abscol = CURCOL;
1785 
1786     while(active && files->count) {
1787 	refresh();
1788 	if(idle) go = keypressed(); else go = 1;
1789 
1790 	if(go) {
1791 	    k = getkey();
1792 	    if(emacs) k = emacsbind(k);
1793 
1794 	    switch(k) {
1795 		case KEY_UP:
1796 		case KEY_DOWN:
1797 		case KEY_LEFT:
1798 		case KEY_RIGHT:
1799 		case KEY_HOME:
1800 		case KEY_END:
1801 		case KEY_PPAGE:
1802 		case KEY_NPAGE:
1803 		case KEY_EMACS_BEG_OF_BUFFER:
1804 		case KEY_EMACS_END_OF_BUFFER:
1805 		case KEY_EMACS_FORWARD_WORD:
1806 		case KEY_EMACS_BACKWARD_WORD:
1807 		    edmove(k, EM_TAB | EM_CTRL | EM_MANUAL);
1808 		    updatecursor();
1809 		    break;
1810 
1811 		default:
1812 		    if(prevshift) prevshift = curfile->markmode = false;
1813 		switch(k) {
1814 		    case '\r':
1815 			if(otherkeys)
1816 			if((*otherkeys)(*this, k) == -1) return -1;
1817 
1818 			if(insertmode || (curfile->lines->count == CURLINE+1)) {
1819 			    edenter(smarttab);
1820 			} else {
1821 			    edmove(KEY_DOWN);
1822 			    edmove(KEY_HOME);
1823 			}
1824 			break;
1825 		    case KEY_BACKSPACE:
1826 		    case CTRL('h'):
1827 		    case 127:
1828 			edbackspace();
1829 			break;
1830 		    case CTRL('y'):
1831 			eddelline();
1832 			break;
1833 		    case KEY_EMACS_C_U:
1834 			eddelbegofline();
1835 			break;
1836 		    case KEY_EMACS_C_K:
1837 			eddelendofline();
1838 			break;
1839 		    case KEY_EMACS_C_T:
1840 			edtransposechar();
1841 			break;
1842 		    case KEY_EMACS_M_D:
1843 			eddelwordemacs();
1844 			break;
1845 		    case KEY_TAB:
1846 			inschar('\t');
1847 			break;
1848 		    case CTRL('t'):
1849 			eddelword();
1850 			break;
1851 
1852 		    default:
1853 			if(k >= 32 && k <= 255) {
1854 			    if(!insertmode && !endofline()) eddel();
1855 			    inschar(k);
1856 			} else if(otherkeys) {
1857 			    if(!getctrlkeys() && ((k == KEY_IC) || (k == KEY_DC)))
1858 			    switch(k) {
1859 				case KEY_IC: insertmode = !insertmode; break;
1860 				case KEY_DC: eddel(); break;
1861 			    } else {
1862 				l = fn;
1863 				if((*otherkeys)(*this, k) == -1) return -1;
1864 				if(l != fn) {
1865 				    draw();
1866 				    updatecursor();
1867 				}
1868 			    }
1869 			}
1870 		}
1871 	    }
1872 	} else {
1873 	    if(idle) (*idle)(*this);
1874 	}
1875     }
1876 
1877     active = false;
1878     return !files->count ? TEXTEDITOR_NOFILES :
1879 			   0;
1880 }
1881 
close()1882 void texteditor::close() {
1883     files->remove(fn);
1884     curfile = 0;
1885 
1886     if(files->count) {
1887 	int n = fn;
1888 	fn = -1;
1889 	if(n >= files->count) n = files->count-1;
1890 	setfnum(n);
1891     } else {
1892 	active = false;
1893 	fn = -1;
1894     }
1895 }
1896 
find(const char * needle,const char * options,int * col,int * line)1897 bool texteditor::find(const char *needle, const char *options, int *col, int *line) {
1898     const char *f;
1899     char *p;
1900     int i, plus;
1901     enum {fromcur, fromstart, backward} fdirection;
1902     bool casesens = (bool) strchr(options, 's');
1903 
1904     if(strchr(options, 't')) {
1905 	fdirection = fromstart;
1906 	i = 0;
1907     } else if(strchr(options, 'b')) {
1908 	fdirection = backward;
1909 	i = 0;
1910     } else if(strchr(options, 'c')) {
1911 	fdirection = fromcur;
1912 	i = CURLINE;
1913     }
1914 
1915     for(; i < curfile->lines->count; i++) {
1916 	p = (char *) curfile->lines->at(i);
1917 
1918 	if((fdirection == fromcur) && (i == CURLINE)) plus = CURCOL+1;
1919 	else plus = 0;
1920 
1921 	if(!casesens) f = strqcasestr(p+plus, needle, "");
1922 	else f = strstr(p+plus, needle);
1923 
1924 	if(f) {
1925 	    *col = (int) (f-p);
1926 	    *line = i;
1927 	    return true;
1928 	}
1929     }
1930 
1931     return false;
1932 }
1933 
ismark()1934 bool texteditor::ismark() {
1935     return curfile ? curfile->markmode : false;
1936 }
1937 
getline(int ln)1938 char *texteditor::getline(int ln) {
1939     return (char *) (getfcount() ? curfile->lines->at(ln) : 0);
1940 }
1941 
putline(int ln,const char * newline)1942 void texteditor::putline(int ln, const char *newline) {
1943     char *p = (char *) curfile->lines->at(ln);
1944     string deltext, instext;
1945 
1946     deltext = (string) p + "\n";
1947     instext = (string) newline + "\n";
1948 
1949     curfile->lines->replace(ln, (void *) newline);
1950 
1951     modification(udelchar, deltext, false, 0, ln);
1952     modification(uinschar, instext, true, 0, ln);
1953 }
1954 
redraw()1955 void texteditor::redraw() {
1956     active = (bool) curfile;
1957     draw();
1958 }
1959 
highlight(int line,int color)1960 void texteditor::highlight(int line, int color) {
1961     highlight(getfnum(), line, color);
1962 }
1963 
highlight(int fn,int line,int color)1964 void texteditor::highlight(int fn, int line, int color) {
1965     editfile *f = (editfile *) files->at(fn);
1966     int i;
1967 
1968     if(f) {
1969 	if((i = f->highlines->findnum(&line, &findhighline)) != -1) {
1970 	    f->highlines->remove(i);
1971 	}
1972 
1973 	highline *h = new highline;
1974 	h->line = line;
1975 	h->color = color;
1976 	f->highlines->add(h);
1977     }
1978 }
1979 
unlight(int line)1980 void texteditor::unlight(int line) {
1981     unlight(getfnum(), line);
1982 }
1983 
unlight(int fn,int line)1984 void texteditor::unlight(int fn, int line) {
1985     int i;
1986     editfile *f = (editfile *) files->at(fn);
1987 
1988     if(f) {
1989 	if((i = f->highlines->findnum(&line, &findhighline)) != -1) {
1990 	    f->highlines->remove(i);
1991 	}
1992     }
1993 }
1994 
clearlight()1995 void texteditor::clearlight() {
1996     clearlight(getfnum());
1997 }
1998 
clearlight(int fn)1999 void texteditor::clearlight(int fn) {
2000     editfile *f = (editfile *) files->at(fn);
2001     if(f) f->highlines->empty();
2002 }
2003 
switchmark()2004 void texteditor::switchmark() {
2005     CHECKLOADED;
2006 
2007     if(ismark()) endmark();
2008     else startmark();
2009 }
2010 
shiftident(int x1,int y1,int x2,int y2,int delta)2011 void texteditor::shiftident(int x1, int y1, int x2, int y2, int delta) {
2012     int starty = y1, endy = y2, i;
2013     char *p, *newp;
2014     string origtext, repltext;
2015 
2016     CHECKLOADED;
2017     if(!delta) return;
2018     if(x1) starty++;
2019     if(!x2) endy--;
2020 
2021     for(i = starty; i <= endy; i++) {
2022 	p = (char *) curfile->lines->at(i);
2023 
2024 	if(delta > 0) {
2025 	    newp = new char[strlen(p)+delta+1];
2026 	    sprintf(newp, "%*s%s", delta, "", p);
2027 	} else {
2028 	    if(strspn(p, " ") >= -delta) {
2029 		newp = strdup(p-delta);
2030 	    } else {
2031 		newp = strdup(p);
2032 		strimlead(newp);
2033 	    }
2034 	}
2035 
2036 	strimtrail(newp);
2037 
2038 	if(origtext.empty() && repltext.empty()) {
2039 	    origtext = p;
2040 	    repltext = newp;
2041 	} else {
2042 	    origtext += "\n";
2043 	    repltext += "\n";
2044 	    origtext += p;
2045 	    repltext += newp;
2046 	}
2047 
2048 	curfile->lines->replace(i, newp);
2049     }
2050 
2051     modification(udelblock, origtext, true, curfile->markblock->x1, curfile->markblock->y1);
2052     modification(uinsblock, repltext, false, curfile->markblock->x1, curfile->markblock->y1);
2053 }
2054 
shiftident(int delta)2055 void texteditor::shiftident(int delta) {
2056     CHECKLOADED;
2057     shiftident(curfile->markblock->x1, curfile->markblock->y1,
2058     curfile->markblock->x2, curfile->markblock->y2, delta);
2059 }
2060 
undo()2061 void texteditor::undo() {
2062     int i, aline;
2063     undorecord *ur;
2064     tundoaction a;
2065     bool firstpass = true, finished = false;
2066     undolog = show = false;
2067 
2068     CHECKLOADED;
2069     while(curfile->undo->count && !finished) {
2070 	ur = (undorecord *) curfile->undo->at(curfile->undo->count-1);
2071 
2072 	if(!firstpass && ((a != ur->action) || (aline != ur->y))) {
2073 	    if(!ur->prevconnected) break;
2074 	    finished = true;
2075 	} else {
2076 	    a = ur->action;
2077 	    aline = ur->y;
2078 	}
2079 
2080 	firstpass = false;
2081 	setpos(ur->x, ur->y);
2082 
2083 	switch(ur->action) {
2084 	    case uinschar:
2085 	    case uinsblock:
2086 		clearmark();
2087 		for(i = 0; i < ur->data.size(); i++) eddel(false);
2088 		break;
2089 	    case udelchar:
2090 	    case udelblock:
2091 		for(i = 0; i < ur->data.size(); i++)
2092 		switch(ur->data[i]) {
2093 		    case '\n':
2094 			edenter(false);
2095 			edmove(KEY_HOME);
2096 			break;
2097 		    default:
2098 			inschar(ur->data[i]);
2099 			break;
2100 		}
2101 		break;
2102 	}
2103 
2104 	curfile->undo->remove(curfile->undo->count-1);
2105     }
2106 
2107     abscol = CURCOL;
2108     scancomments(true);
2109     undolog = show = true;
2110     redraw();
2111 }
2112 
2113 // --------------------------------------------------------------------------
2114 
textblockfree(void * p)2115 void texteditor::textblockfree(void *p) {
2116     textblock *tb = (textblock *) p;
2117     if(tb) delete tb;
2118 }
2119 
highlinefree(void * p)2120 void texteditor::highlinefree(void *p) {
2121     highline *hl = (highline *) p;
2122     if(hl) delete hl;
2123 }
2124 
editfilefree(void * p)2125 void texteditor::editfilefree(void *p) {
2126     editfile *ef = (editfile *) p;
2127     if(ef) {
2128 	delete ef->lines;
2129 	delete ef->blocks;
2130 	delete ef->highlines;
2131 	delete ef->undo;
2132 	delete ef->markblock;
2133 	free (ef->id) ;	 /* this is allocated by a c-routine => must deallocated with free */
2134 	delete ef;
2135     }
2136 }
2137 
undorecordfree(void * p)2138 void texteditor::undorecordfree(void *p) {
2139     undorecord *ur = (undorecord *) p;
2140     if(ur) delete ur;
2141 }
2142 
findint(void * p1,void * p2)2143 int texteditor::findint(void *p1, void *p2) {
2144     return (intptr_t) p1 != (intptr_t) p2;
2145 }
2146 
findhighline(void * p1,void * p2)2147 int texteditor::findhighline(void *p1, void *p2) {
2148     return (intptr_t) p1 != ((highline *) p2)->line;
2149 }
2150 
shiftmarkedblock(int delta)2151 void texteditor::shiftmarkedblock(int delta) {
2152     CHECKLOADED;
2153     if(CURLINE <= curfile->markblock->y1) {
2154 	curfile->markblock->y1 += delta;
2155 	curfile->markblock->y2 += delta;
2156     } else if((CURLINE > curfile->markblock->y1) && (CURLINE < curfile->markblock->y2)) {
2157 	curfile->markblock->y2 += delta;
2158     }
2159 }
2160 
prepend(char * text,int ln)2161 void texteditor::prepend(char *text, int ln)
2162 {
2163     if (!text)
2164 	return;
2165     char *p = (char *) curfile->lines->at(ln);
2166     if (p) {
2167 	char *n = (char *) malloc(strlen(p)+2+strlen(text));
2168 	strcpy(n, text);
2169 	strcat(n, p);
2170 	curfile->lines->replace(ln, n);
2171     } else {
2172 	curfile->lines->insert(ln+1, strdup(text));
2173     }
2174 }
2175 
wrapline(int ln,int & px,int & py)2176 void texteditor::wrapline(int ln, int &px, int &py)
2177 {
2178     char *p = (char *) curfile->lines->at(ln);
2179 
2180     if(wrap && p && strlen(p) > x2-x1-1) { // it's longer than window width and we want it wrapped
2181 	char *n = (char *) malloc(strlen(p)+5);
2182 	strcpy(n, p);
2183 	char *sub = strpbrk(n, WORD_DELIM), *osub = 0, *sep;
2184 
2185 	if(sub) { // there's some whitespace
2186 	    while((osub = strpbrk(sub+1, WORD_DELIM)) && // find last word on line
2187 	    (strspn(osub, WORD_DELIM) < strlen(osub)) &&
2188 	    ((osub-n)<(x2-x1-1))) sub = osub;
2189 	} else {
2190 	    sub = n+strlen(n)-2;
2191 	}
2192 
2193 	char sins[2] = " ";
2194 	sins[0] = *sub;
2195 	strinsert(n, sub-n, sins); // append space to the end of line
2196 	sub++; // go to previous space position
2197 	*sub = 0; // cut the line
2198 	sep = sub+1; // first character of line remainder
2199 
2200 	curfile->lines->replace(ln, n);
2201 	prepend(sep, ln+1); // prepend wrapped end of line to beginning of the next one
2202 	if (px>=strlen(n)) { // cursor moved to next line
2203 	    px -= strlen(n);
2204 	    py++;
2205 	    wrapline(ln+1, px, py); // wrap again and keep the cursor position updated
2206 	} else {
2207 	    int dx, dy; // dummy
2208 	    wrapline(ln+1, dx, dy); // just wrap the rest
2209 	}
2210     }
2211 }
2212 
mergeline(int ln,bool force,int & px,int & py)2213 void texteditor::mergeline(int ln, bool force, int &px, int &py)
2214 {
2215     char *p = (char *) curfile->lines->at(ln);
2216     if (!p || ((!*p || (p[strlen(p)-1]!=' ')) && !force)) // the line is not mergeable
2217 	return;
2218     char *next = (char *) curfile->lines->at(ln+1);
2219     int able = x2-x1-strlen(p)-1; // how much space do we have on this line
2220 
2221     if(next && (*next || force)) {
2222 	if(wrap && (able < strlen(next))) { // not whole next line fits here
2223 	    char *anext = strdup(next), *asub, *atsub;
2224 	    anext[able] = 0;
2225 
2226 	    if(asub = strpbrk(anext, WORD_DELIM)) {
2227 		int pxdeltamerge;
2228 		for(; atsub = strpbrk(asub+1, WORD_DELIM); asub = atsub);
2229 		char *newline = new char[strlen(p)+asub-anext+2];
2230 		strcpy(newline, p);
2231 		if ((ln==(py-1)) && (px<(asub-anext+1))) { // move to previous line
2232 		    px += strlen(newline);
2233 		    pxdeltamerge = px; //accepting that change px even if the line is merged
2234 		    py--;
2235 		}
2236 		else {
2237 		    if (ln==(py-1)) {
2238 			px -= (asub-anext+1);  // move back
2239 			pxdeltamerge = px;
2240 		    }
2241 		    else
2242 			pxdeltamerge = px - (asub-anext+1); //px can't be changed even if the line is merged
2243 		}
2244 		strncat(newline, next, asub-anext+1);
2245 		strcut(next, 0, asub-anext+1);
2246 		curfile->lines->replace(ln, newline);
2247 		//mergeline(ln+1, false, px, py);  // we've merged something from the next line - try to merge it too
2248 		mergeline(ln+1, false, pxdeltamerge, py);  // we've merged something from the next line - try to merge it too
2249 	    }
2250 
2251 	    free(anext);
2252 	} else { // whole next line fits on this one
2253 	    int nextlen;
2254 	    if(next) nextlen = strlen(next); else nextlen = 0;
2255 	    char *newline = new char[nextlen+strlen(p)+1];
2256 
2257 	    if (ln == (py-1)) {
2258 		px += strlen(p);
2259 		py--;
2260 	    }
2261 	    sprintf(newline, "%s%s", p, nextlen ? next : "");
2262 	    curfile->lines->replace(ln, newline);
2263 	    curfile->lines->remove(ln+1);
2264 	    shiftmarkedblock(-1);
2265 
2266 	    if (wrap && *newline && newline[strlen(newline)-1]==' ') // if it wasn't last one in "paragraph", try to merge with another one
2267 		mergeline(ln, false, px, py);
2268 	}
2269     }
2270 }
2271 
2272 // --------------------------------------------------------------------------
2273 
2274 #ifdef __KTOOL_USE_NAMESPACES
2275 
2276 using ktool::hlight;
2277 
2278 #endif
2279 
operator ==(const hl_kind & k) const2280 bool hlight::operator == (const hl_kind &k) const {
2281     return kind == k;
2282 }
2283 
operator !=(const hl_kind & k) const2284 bool hlight::operator != (const hl_kind &k) const {
2285     return kind != k;
2286 }
2287 
operator <(const hlight & ah) const2288 bool hlight::operator < (const hlight &ah) const {
2289     return kind == h_eol;
2290 }
2291 
2292