1 /**
2  * Copyright (c) 2014 Roy Lu <roymercadian@gmail.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 
20 #ifdef __GNUG__
21   #pragma implementation "editor.h"
22 #endif
23 
24 #include "mainframe.h"
25 
26 #include "editor.h"
27 #include <algorithm>	// for std::replace
28 #include <fstream>
29 
30 #define RECV_BUF_SIZE (4097)
31 
32 
33 typedef struct {
34 	CTermData* pTermData;
35 	string*    text;
36 	int        lines;
37 	const char*      eol;
38 } ReadStatus;
39 
CEditor(CTelnetView * tView,CSite & SiteInfo)40 CEditor::CEditor(CTelnetView* tView, CSite& SiteInfo)
41 	: CTelnetCon(tView, SiteInfo)
42 {
43 	m_Site.m_CRLF = 1;  // \n
44 	m_Site.m_ESCConv = "\x1b";
45 }
46 
~CEditor()47 CEditor::~CEditor()
48 {
49 	// do nothing.
50 }
51 
InitAnsiEditor()52 void CEditor::InitAnsiEditor()
53 {
54 	m_EditorText.resize(1);
55 
56 	m_AnsiBright = false;
57 	m_AnsiBlink = false;
58 	m_AnsiFg = 37;
59 	m_AnsiBg = 40;
60 
61 	m_DisplayStart = m_DisplayEnd = 0;
62 
63 	m_SelectStartRow = m_SelectStartCol = m_SelectEndRow = m_SelectEndCol = 0;
64 
65 	string str = "\x1b[m";
66 	for(int r = 0; r < 24; ++r){
67 		m_EditorText.push_back(str);
68 	}
69 
70 	SetDisplayFrame();
71 }
72 
73 /**
74  * @brief Deal with user input. Store the text in TermData.
75  */
Send(void * buf,int length)76 int CEditor::Send( void* buf, int length )
77 {
78 	// save text into data
79 	string text = (char*)buf;
80 	DoInsertText(m_EditorText[m_DisplayStart + m_CaretPos.y], text, m_CaretPos.x);
81 
82 	GetEditorView()->GetParentFrame()->GetCurEditorView()->UpdateEditor();
83 
84 	SetTextColor(m_CaretPos.y, m_CaretPos.x, m_CaretPos.x + length - 1);
85 
86 	//save back to vector
87 	string newText = GetLineText(m_CaretPos.y, false);
88 	m_EditorText[m_DisplayStart + m_CaretPos.y] = newText;
89 
90 	m_Sel->Unselect();
91 
92 	// Move caret to next position
93 	m_CaretPos.x += length;
94 	if(m_CaretPos.x >= m_ColsPerPage - 1){
95 		m_CaretPos.x = m_ColsPerPage - 1;
96 	}
97 
98 	GetEditorView()->GetParentFrame()->GetCurEditorView()->UpdateEditor();
99 
100 	return 0;
101 }
102 
DeleteText()103 void CEditor::DeleteText()
104 {
105 
106 	DoDeleteText( m_EditorText[m_DisplayStart + m_CaretPos.y], m_CaretPos.x);
107 
108 	GetEditorView()->GetParentFrame()->GetCurEditorView()->UpdateEditor();
109 }
110 
BackspaceText()111 void CEditor::BackspaceText()
112 {
113 	DoBackspaceText();
114 
115 	GetEditorView()->GetParentFrame()->GetCurEditorView()->UpdateEditor();
116 }
117 
DoBackspaceText()118 void CEditor::DoBackspaceText()
119 {
120 	if(m_CaretPos.x < 0){
121 		return;
122 	}
123 
124 	if(m_CaretPos.x == 0){
125 		if(m_DisplayStart + m_CaretPos.y > 0){
126 			MoveUp();
127 			MoveToEnd();
128 			//append current line text to end of previous line
129 			stringstream ss;
130 			ss << m_EditorText[m_DisplayStart + m_CaretPos.y];
131 			ss << m_EditorText[m_DisplayStart + m_CaretPos.y + 1];
132 
133 			m_EditorText[m_DisplayStart + m_CaretPos.y] = ss.str();
134 
135 			//remove (m_DisplayStart + m_CaretPos.y + 1)
136 			vector<string>::iterator itor = m_EditorText.begin();
137 
138 			m_EditorText.erase(itor + m_DisplayStart + m_CaretPos.y + 1);
139 
140 			SetDisplayFrame();
141 		}
142 		return;
143 	}
144 
145 	//move caret to left.
146 	MoveLeft();
147 
148 	DoDeleteText( m_EditorText[m_DisplayStart + m_CaretPos.y], m_CaretPos.x);
149 }
150 
151 /**
152  * @brief delete char from text of ansi editor.
153  */
DoDeleteText(string & line,int col)154 void CEditor::DoDeleteText(string &line, int col)
155 {
156 	int pos = ParseToRawPos(line, col);
157 	if(pos >= static_cast<int>(line.length()) || pos < 0){
158 		return;		//do nothing
159 	}
160 
161 	stringstream ss;
162 	// Deal with DBCS
163 	if(line[pos] < 0){		//it's a DBCS
164 		ss << line.substr(0, pos);
165 		ss << line.substr(pos + 2, -1);
166 	}else{
167 		ss << line.substr(0, pos);
168 		ss << line.substr(pos + 1, -1);
169 	}
170 	line = ss.str();
171 
172 	if(line.length() == 0)
173 		line = " ";
174 }
175 
LoadEditorText()176 void CEditor::LoadEditorText()
177 {
178 	//load text block from data to screem
179 	stringstream ss;
180 	for(int r = m_DisplayStart; r <= m_DisplayEnd; ++r){
181 		ss.clear();
182 		ss << m_EditorText[r];
183 		if(r < m_DisplayEnd)		// Don't add \r\n at last row
184 			ss << "\r\n";
185 	}
186 
187 	static unsigned char recv_buf[RECV_BUF_SIZE];
188 	strcpy( (char *)recv_buf, ss.str().c_str());
189 	m_pRecvBuf = recv_buf;
190 	gsize rlen = ss.str().size();
191 
192 	m_pRecvBuf[rlen] = '\0';
193 	m_pBuf = m_pRecvBuf;
194 	m_pLastByte = m_pRecvBuf + rlen;
195 
196 	ParseReceivedData();
197 
198 	UpdateDisplay();
199 	//GetView()->UpdateEditor();
200 }
201 
202 
EditorActions(int action,string arg)203 void CEditor::EditorActions(int action, string arg)
204 {
205 	switch (action)
206 	{
207 	case Init_Ansi_Editor:
208 		InitAnsiEditor();
209 		break;
210 	case Move_Up:
211 		MoveUp();
212 		break;
213 	case Move_Down:
214 		MoveDown();
215 		break;
216 	case Move_Left:
217 		MoveLeft();
218 		break;
219 	case Move_Right:
220 		MoveRight();
221 		break;
222 	case Move_To_Home:
223 		MoveToHome();
224 		break;
225 	case Move_To_End:
226 		MoveToEnd();
227 		break;
228 	case Move_To_Prev_Page:
229 		MoveToPrevPage();
230 		break;
231 	case Move_To_Next_Page:
232 		MoveToNextPage();
233 		break;
234 	case New_Line:
235 		NewLine();
236 		break;
237 	case Delete_Text:
238 		DeleteText();
239 		break;
240 	case Backspace_Text:
241 		BackspaceText();
242 		break;
243 	case Set_Display_Frame_Plus:
244 		SetDisplayFrame(1);
245 		break;
246 	case Set_Display_Frame_Minus:
247 		SetDisplayFrame(-1);
248 		break;
249 	case Set_Caret_Pos_X:
250 		SetCaretPosX();
251 		break;
252 	case Set_Caret_Pos_Y:
253 		SetCaretPosY();
254 		break;
255 	case Load_Editor_Text:
256 		LoadEditorText();
257 		break;
258 	case Load_Ansi_File:
259 		LoadAnsiFile(arg);
260 		break;
261 	case Save_Ansi_File:
262 		SaveAnsiFile(arg);
263 		break;
264 	case Paste_To_Editor:
265 		PasteToEditor(arg);
266 		break;
267 	case Clear_Screen:
268 		ClearScreen();
269 		break;
270 	default:
271 		cout << "error: EditorActions" << endl;
272 		break;
273 	}
274 }
275 
MoveUp()276 void CEditor::MoveUp()
277 {
278 	if(m_DisplayStart + m_CaretPos.y <= 0){
279 		return;
280 	}
281 
282 	m_CaretPos.y--;
283 
284 	// Move display frame up if m_CaretPos.y < 0
285 	if(m_CaretPos.y < 0){
286 		m_CaretPos.y = 0;
287 		SetDisplayFrame(-1);
288 
289 	}
290 
291 	// Detect dbcs and set caret position X
292 	SetCaretPosX();
293 
294 	GetEditorView()->GetParentFrame()->GetCurEditorView()->UpdateEditor();
295 }
296 
MoveDown()297 void CEditor::MoveDown()
298 {
299 	if( (m_DisplayStart + m_CaretPos.y) >= static_cast<int>(m_EditorText.size()) - 1){
300 		//already in the last line, do nothing
301 		return;
302 	}
303 
304 	m_CaretPos.y++;
305 
306 	// Move screen pointers down if m_CaretPos.y > (m_RowsPerPage - 1)
307 	if( m_CaretPos.y > (m_RowsPerPage - 1) ){
308 		m_CaretPos.y = m_RowsPerPage - 1;
309 		SetDisplayFrame(1);
310 	}
311 
312 	//detect dbcs and set caret position X
313 	SetCaretPosX();
314 
315 	GetEditorView()->GetParentFrame()->GetCurEditorView()->UpdateEditor();
316 }
317 
MoveLeft()318 void CEditor::MoveLeft()
319 {
320 	CTermCharAttr* pAttr = GetLineAttr( m_Screen[m_CaretPos.y] );
321 	int x = m_CaretPos.x;
322 
323 	if(DetectDBChar() && x > 0 && pAttr[x - 1].GetCharSet() == CTermCharAttr::CS_MBCS2){
324 		m_CaretPos.x -= 2;	//move 2 chars
325 	}else{
326 		m_CaretPos.x--;		//move 1 char
327 	}
328 
329 	if(m_CaretPos.x < 0)
330 		m_CaretPos.x = 0;
331 
332 	GetEditorView()->GetParentFrame()->GetCurEditorView()->UpdateEditorCaretPos();
333 }
334 
MoveRight()335 void CEditor::MoveRight()
336 {
337 	CTermCharAttr* pAttr = GetLineAttr( m_Screen[m_CaretPos.y] );
338 	int x = m_CaretPos.x;
339 
340 
341 	if(DetectDBChar() && pAttr[x].GetCharSet() == CTermCharAttr::CS_MBCS1){
342 		m_CaretPos.x += 2;	//move 2 chars
343 	}else{
344 		m_CaretPos.x++;		//move 1 char
345 	}
346 
347 	if(m_CaretPos.x > m_ColsPerPage - 1){
348 		m_CaretPos.x = m_ColsPerPage - 1;
349 	}
350 
351 	GetEditorView()->GetParentFrame()->GetCurEditorView()->UpdateEditorCaretPos();
352 }
353 
MoveToHome()354 void CEditor::MoveToHome()
355 {
356 	m_CaretPos.x = 0;
357 
358 	GetEditorView()->GetParentFrame()->GetCurEditorView()->UpdateEditor();
359 }
360 
MoveToEnd()361 void CEditor::MoveToEnd()
362 {
363 	m_CaretPos.x = GetTextCharCount( m_EditorText[m_DisplayStart + m_CaretPos.y]);
364 
365 	if(m_CaretPos.x > m_ColsPerPage - 1)
366 		m_CaretPos.x = m_ColsPerPage -1;
367 
368 	GetEditorView()->GetParentFrame()->GetCurEditorView()->UpdateEditor();
369 }
370 
MoveToPrevPage()371 void CEditor::MoveToPrevPage()
372 {
373 	SetDisplayFrame(-m_RowsPerPage);
374 
375 	//bound check
376 	if(m_DisplayStart + m_CaretPos.y < 0){
377 		m_CaretPos.y = 0;
378 	}
379 
380 	GetEditorView()->GetParentFrame()->GetCurEditorView()->UpdateEditor();
381 
382 	//detect dbcs and set caret position x
383 	CTermCharAttr* pAttr = GetLineAttr( m_Screen[m_CaretPos.y] );
384 	int x = m_CaretPos.x;
385 
386 	if(DetectDBChar() && pAttr[x].GetCharSet() == CTermCharAttr::CS_MBCS2){
387 		m_CaretPos.x--;
388 	}
389 
390 	GetEditorView()->GetParentFrame()->GetCurEditorView()->UpdateEditor();
391 }
392 
MoveToNextPage()393 void CEditor::MoveToNextPage()
394 {
395 	SetDisplayFrame(m_RowsPerPage);
396 
397 	//bound check
398 	if(m_DisplayStart + m_CaretPos.y >= static_cast<int>(m_EditorText.size())){
399 		m_CaretPos.y = m_EditorText.size() - 1;
400 		if(m_CaretPos.y > m_RowsPerPage - 1)
401 			m_CaretPos.y = m_RowsPerPage - 1;
402 	}
403 
404 	GetEditorView()->GetParentFrame()->GetCurEditorView()->UpdateEditor();		//update screen first time
405 
406 	//detect dbcs
407 	CTermCharAttr* pAttr = GetLineAttr( m_Screen[m_CaretPos.y] );
408 	int x = m_CaretPos.x;
409 
410 	if(DetectDBChar() && pAttr[x].GetCharSet() == CTermCharAttr::CS_MBCS2){
411 		m_CaretPos.x--;
412 	}
413 
414 	GetEditorView()->GetParentFrame()->GetCurEditorView()->UpdateEditor();		//update screen second time
415 }
416 
417 /**
418  * @brief every time when user press enter-key, the m_EditorText add a new string data.
419  */
NewLine()420 void CEditor::NewLine()
421 {
422 	//partition the string behind cursor to the next line
423 	string str0, str1;
424 	int pos = ParseToRawPos(m_EditorText[m_DisplayStart + m_CaretPos.y], m_CaretPos.x);
425 	if(pos < static_cast<int>(m_EditorText[m_DisplayStart + m_CaretPos.y].size())){
426 		str0 = m_EditorText[m_DisplayStart + m_CaretPos.y].substr(0, pos);
427 		str1 = m_EditorText[m_DisplayStart + m_CaretPos.y].substr(pos, -1);
428 		if(str0.size() == 0)
429 			str0 = "\0";
430 		if(str1.size() == 0)
431 			str1 = "\0";
432 	}else{
433 		str0 = m_EditorText[m_DisplayStart + m_CaretPos.y];
434 		str1 = "\0";
435 	}
436 
437 	m_EditorText[m_DisplayStart + m_CaretPos.y] = str0;
438 
439 	if(m_DisplayStart + m_CaretPos.y < static_cast<int>(m_EditorText.size()) - 1){
440 		vector<string>::iterator itor = m_EditorText.begin();
441 		m_EditorText.insert(itor + m_DisplayStart + m_CaretPos.y + 1, str1);
442 	}else{
443 		m_EditorText.push_back(str1);
444 	}
445 
446 	//set new caret position
447 	m_CaretPos.x = 0;
448 
449 	if(m_CaretPos.y == m_RowsPerPage - 1){
450 		SetDisplayFrame(1);
451 	}else{
452 		m_CaretPos.y++;
453 		SetDisplayFrame();
454 	}
455 	SetCaretPosX();
456 
457 	GetEditorView()->GetParentFrame()->GetCurEditorView()->UpdateEditor();
458 }
459 
GetTextCharCount(const string & str)460 int CEditor::GetTextCharCount(const string &str)
461 {
462 	int count = 0;
463 	int i = 0;
464 	while(i < static_cast<int>(str.length())){
465 		if(str[i] == '\x1b'){
466 			while(str[i] != 'm')	++i;
467 			++i;
468 			continue;
469 		}
470 		if(str[i] < 0){	//it's a DBCS
471 			i += 2;
472 			count += 2;
473 		}else{			//SBCS
474 			i++;
475 			count++;
476 		}
477 	}
478 	return count;
479 }
480 
ApplyAnsiColor(int bright,int blink,int fg,int bg)481 void CEditor::ApplyAnsiColor(int bright, int blink, int fg, int bg)
482 {
483 	SetAnsiColor(bright, blink, fg, bg);
484 
485 	SetSelection();
486 
487 	if(m_SelectStartRow < 0 || m_SelectEndRow < 0){
488 		return;
489 	}
490 
491 	SetColorToSelection(bright, blink, fg, bg);	// method of parent class CTermData
492 
493 	GetEditorView()->GetParentFrame()->GetCurEditorView()->UpdateEditor();
494 }
495 
LoadAnsiFile(string filename)496 void CEditor::LoadAnsiFile(string filename)
497 {
498 	m_EditorText.clear();
499 	fstream fs;
500 	fs.open(filename.c_str());
501 	if(fs.is_open()){
502 		string line;
503 		while(std::getline(fs, line)){
504 			// Remove carriage return
505 			std::replace(line.begin(), line.end(), '\r', ' ');
506 			m_EditorText.push_back(line);
507 		}
508 
509 		SetDisplayFrame();
510 
511 		fs.close();
512 	}else{
513 		printf("fail to open file.\n");
514 	}
515 }
516 
SaveAnsiFile(string filename)517 void CEditor::SaveAnsiFile(string filename)
518 {
519 	stringstream ss;
520 	ss << filename << ".ans";
521 	filename = ss.str();
522 
523 	ofstream fs(filename.c_str());
524 	if(fs.is_open()){
525 
526 		for(unsigned int i = 0; i < m_EditorText.size(); ++i){
527 			fs << m_EditorText[i] << "\r\n";
528 		}
529 
530 		fs.close();
531 	}else{
532 		printf("fail to save file.\n");
533 	}
534 }
535 
SetSelection()536 void CEditor::SetSelection()
537 {
538 	m_Sel->GetCanonicalMarks(m_SelectStartRow, m_SelectStartCol, m_SelectEndRow, m_SelectEndCol);
539 
540 	//check range
541 	if(m_EditorText.size() > m_RowsPerPage){
542 		//return;
543 	}else{
544 		if(m_SelectEndRow > static_cast<int>(m_EditorText.size()) - 1){
545 			m_SelectEndRow = m_EditorText.size() - 1;
546 			m_SelectEndCol = m_ColsPerPage - 1;
547 		}
548 	}
549 }
550 
SetDisplayFrame(int offset)551 void CEditor::SetDisplayFrame(int offset)
552 {
553 	if(m_EditorText.size() <= m_RowsPerPage){
554 		m_DisplayStart = 0;
555 		m_DisplayEnd = m_EditorText.size() - 1;
556 	}else{
557 		m_DisplayStart += offset;
558 
559 		if(m_DisplayStart < 0){
560 			m_DisplayStart = 0;
561 			m_DisplayEnd = m_RowsPerPage - 1;
562 		}else{
563 			m_DisplayEnd = m_DisplayStart + m_RowsPerPage - 1;
564 
565 			if(m_DisplayEnd > static_cast<int>(m_EditorText.size()) - 1){
566 				m_DisplayEnd = m_EditorText.size() - 1;
567 				m_DisplayStart = m_DisplayEnd - m_RowsPerPage + 1;
568 			}
569 		}
570 	}
571 }
572 
SetCaretPosX()573 void CEditor::SetCaretPosX()
574 {
575 	CTermCharAttr* pAttr = GetLineAttr( m_Screen[m_CaretPos.y] );
576 
577 	//DetectDBChar() should be added here.
578 	if( DetectDBChar() && pAttr[m_CaretPos.x].GetCharSet() == CTermCharAttr::CS_MBCS2){
579 		m_CaretPos.x--;
580 	}
581 }
582 
SetCaretPosY()583 void CEditor::SetCaretPosY()
584 {
585 	int lineCount = m_DisplayEnd - m_DisplayStart + 1;
586 
587 	if(m_CaretPos.y >= lineCount){
588 		m_CaretPos.y = lineCount - 1;
589 	}
590 }
591 
592 /**
593  * Paste text to AnsiEditor.
594  */
PasteToEditor(const string & text)595 void CEditor::PasteToEditor(const string &text)
596 {
597 	string temp = text;
598 	int lineCount = 0;
599 	int found = -1;
600 	do{
601 		lineCount++;
602 		found = temp.find("\n");
603 
604 		string line = temp.substr(0, found);
605 
606 		if(lineCount == 1){
607 			int insertPos = ParseToRawPos(m_EditorText[m_DisplayStart + m_CaretPos.y], m_CaretPos.x);
608 			int length = m_EditorText[m_DisplayStart + m_CaretPos.y].length();
609 			if( insertPos > length){
610 				int spaceCount = insertPos - length;
611 				for(int i = 0; i < spaceCount; ++i){
612 					m_EditorText[m_DisplayStart + m_CaretPos.y] += " ";
613 				}
614 				m_EditorText[m_DisplayStart + m_CaretPos.y] += line;
615 			}else{
616 				string result = m_EditorText[m_DisplayStart + m_CaretPos.y].substr(0, insertPos);
617 				result += line;
618 				result += m_EditorText[m_DisplayStart + m_CaretPos.y].substr(insertPos, -1);
619 				m_EditorText[m_DisplayStart + m_CaretPos.y] = result;
620 			}
621 		}else{
622 			vector<string>::iterator itor;
623 			itor = m_EditorText.begin();
624 			m_EditorText.insert(itor + m_DisplayStart + m_CaretPos.y + lineCount -1, line);
625 		}
626 
627 		if(found != static_cast<int>(string::npos))
628 			temp = temp.substr(found + 1, -1);
629 	}while(found != static_cast<int>(string::npos));
630 }
631 
ParseToRawPos(const string & text,int col,bool checkDBCS)632 int CEditor::ParseToRawPos(const string &text, int col, bool checkDBCS)
633 {
634 	if(col == -1)	return -1;
635 
636 	int t = 0;	//index
637 	int i = 0;
638 	while(i < static_cast<int>(text.length())){
639 		if(text[i] == '\x1b'){
640 			while(text[i] != 'm')	++i;
641 			++i;
642 			continue;
643 		}
644 		if(t == col){
645 			return i;
646 		}
647 
648 		if(checkDBCS){
649 			if(text[i] < 0){	//it's a DBCS
650 				i += 2;
651 				t += 2;
652 			}else{			//it's a SBCS
653 				i++;
654 				t++;
655 			}
656 		}else{
657 			i++;
658 			t++;
659 		}
660 	}
661 
662 	while(t != col){
663 		++i;
664 		++t;
665 	}
666 	return i;
667 }
668 
669 /**
670  * @brief Insert string into text at insertCol position.
671  * @param insertPos -1: insert to the end of text.
672  */
DoInsertText(string & text,const string & newText,int insertCol)673 void CEditor::DoInsertText(string &text, const string &newText, int insertCol)
674 {
675 	int insertPos = ParseToRawPos(text, insertCol);
676 
677 	stringstream ss("");
678 	if(insertPos < static_cast<int>(text.length())){
679 		if(insertPos == -1){		//insert newText to the end of text
680 			ss << text << newText;
681 		}else{
682 			ss << text.substr(0, insertPos);
683 			ss << newText;
684 			ss << text.substr(insertPos, -1);
685 		}
686 	}else{
687 		//need to add spaces
688 		ss << text;
689 		int spaceCount = insertPos - text.length();
690 		for(int i = 0; i < spaceCount; ++i){
691 			ss << " ";
692 		}
693 		ss << newText;
694 	}
695 
696 	text = ss.str();
697 }
698 
SetAnsiColor(int bright,int blink,int fg,int bg)699 void CEditor::SetAnsiColor(int bright, int blink, int fg, int bg)
700 {
701 	if(bright == 0 || bright == 1)
702 		m_AnsiBright = bright;
703 
704 	if(blink == 0 || blink == 1)
705 		m_AnsiBlink = blink;
706 
707 	if (fg >= 0 && fg <= 7)
708 		m_AnsiFg = fg;
709 
710 	if (bg >= 0 && bg <= 7)
711 		m_AnsiBg = bg;
712 }
713 
SetTextColor(int row,int startCol,int endCol)714 void CEditor::SetTextColor(int row, int startCol, int endCol)
715 {
716 	SetTextColor(row, startCol, endCol, m_AnsiBright, m_AnsiBlink, m_AnsiFg, m_AnsiBg);
717 }
718 
719 /**
720  * Set color attributes for each char between [row, startCol] and [row, endCol]
721  */
SetTextColor(int row,int startCol,int endCol,int bright,int blink,int fg,int bg)722 void CEditor::SetTextColor(int row, int startCol, int endCol, int bright, int blink, int fg, int bg)
723 {
724 	CTermCharAttr *pAttr = GetLineAttr( m_Screen[row] );
725 	for(int c = startCol; c <= endCol; ++c){
726 		if(bright != -1)
727 			pAttr[c].SetBright(bright);
728 
729 		if(blink != -1)
730 			pAttr[c].SetBlink(blink);
731 
732 		if(fg != -1)
733 			pAttr[c].SetForeground(fg);
734 
735 		if(bg != -1)
736 			pAttr[c].SetBackground(bg);
737 
738 		pAttr[c].SetNeedUpdate(true);
739 	}
740 }
741 
742 /**
743  * Set ANSI Color to current selection.
744  */
SetColorToSelection(int bright,int blink,int fg,int bg)745 void CEditor::SetColorToSelection(int bright, int blink, int fg, int bg)
746 {
747 	if(m_SelectStartRow == m_SelectEndRow && m_SelectStartCol > m_SelectEndCol){
748 		return;
749 	}
750 
751 	for(int row = m_SelectStartRow; row <= m_SelectEndRow; ++row){
752 		int textStart = (row == m_SelectStartRow)? m_SelectStartCol: 0;
753 		int textEnd = (row == m_SelectEndRow)? m_SelectEndCol: m_ColsPerPage - 1;
754 
755 		int rawEnd = ParseToRawPos(m_EditorText[m_DisplayStart + row], textEnd, false);
756 
757 		// add space if rawEnd >= m_EditorText[m_DisplayStart + row].length()
758 		if(rawEnd >= static_cast<int>(m_EditorText[m_DisplayStart + row].length())){
759 			int spaceCount = rawEnd - m_EditorText[m_DisplayStart + row].length() + 1;
760 			for(int i = 0; i < spaceCount; ++i){
761 				m_EditorText[m_DisplayStart + row] += " ";
762 			}
763 		}
764 		SetTextColor(row, textStart, textEnd, bright, blink, fg, bg);
765 
766 		//save back to vector
767 		m_Sel->m_Start.row = row;
768 		m_Sel->m_Start.col = 0;
769 		m_Sel->m_End.row = row;
770 		m_Sel->m_End.col = m_ColsPerPage - 1;
771 
772 		m_EditorText[m_DisplayStart + row] = GetLineText(row, false);
773 
774 		m_Sel->Unselect();
775 	}
776 }
777 
778 
779 /**
780  * Get text with Ansi color codes.
781  */
GetLineText(int row,bool trim)782 string CEditor::GetLineText(int row, bool trim)
783 {
784 	m_Sel->m_Start.row = row;
785 	m_Sel->m_Start.col = 0;
786 	m_Sel->m_Start.left = true;
787 	m_Sel->m_End.row = row;
788 	m_Sel->m_End.col = m_ColsPerPage;
789 	m_Sel->m_End.left = true;
790 	string text = GetSelectedTextWithColor(trim);
791 	return text;
792 }
793 
ClearScreen()794 void CEditor::ClearScreen()
795 {
796 	m_EditorText.clear();
797 	m_EditorText.resize(1);
798 	m_DisplayStart = m_DisplayEnd = 0;
799 	m_SelectStartRow = m_SelectStartCol = m_SelectEndRow = m_SelectEndCol = 0;
800 
801 	string str = "\x1b[m";
802 	for(int r = 0; r < 24; ++r){
803 		m_EditorText.push_back(str);
804 	}
805 
806 	SetDisplayFrame();
807 }
808