1 /**
2 * termdata.cpp - Store terminal screen data and parse
3 * ANSI escape sequence.
4 *
5 * Copyright (c) 2004-2005 PCMan <pcman.tw@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (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
15 * GNU 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 Foundation,
19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #ifdef __GNUG__
23 #pragma implementation "termdata.h"
24 #endif
25
26
27 #include "termdata.h" // class's header file
28 #include "termview.h" // class's header file
29 #include "termsel.h" // class's header file
30 #include <cstring>
31 #include <stdint.h>
32
33 #include <algorithm>
34
35 using std::swap;
36
37 /////////////////////////////////////////////////////////////////////////////
38 //The functions section of CTermAttr class.
39 //
40 /////////////////////////////////////////////////////////////////////////////
41
42 //GdkColor = (red, green, blue)
43
44 GdkColor
45 CTermCharAttr::m_DefaultColorTable[SIZE_OF_COLOR_TABLE] = {
46 //Darker color
47 {0,0,0,0}, //0;30m Black
48 {0,65536/2,0,0}, //0;31m Dark red
49 {0,0,65536/2,0}, //0;32m Dark green
50 {0,65536/2,65536/2,0}, //0;33m Brown
51 {0,0,0,65536/2}, //0;34m Dark blue
52 {0,65536/2,0,65536/2}, //0;35m Dark magenta
53 {0,0,65536/2,65536/2}, //0;36m Dark cyan
54 {0,65536*192/256,65536*192/256,65536*192/256}, //0;37m Light gray
55 //Bright color
56 {0,65536/2,65536/2,65536/2}, //1;30m Gray
57 {0,65535,0,0}, //1;31m Red
58 {0,0,65535,0}, //1;32m Green
59 {0,65535,65535,0}, //1;33m Yellow
60 {0,0,0,65535}, //1;34m Blue
61 {0,65535,0,65535}, //1;35m Magenta
62 {0,0,65535,65535}, //1;36m Cyan
63 {0,65535,65535,65535} //1;37m White
64 };
65
66 //Update this old attribute of character with new one which filter by [flags].
67 //e.g., when [flags] = STA_FG | STA_BRIGHT, the old attribute updates
68 //attributes Foreground and Bright only.
69 //After updating, set Need Update flag as true.
70 void
SetTextAttr(CTermCharAttr attr,int flags)71 CTermCharAttr::SetTextAttr( CTermCharAttr attr, int flags )
72 {
73 if( flags & STA_FG )
74 m_Fg = attr.m_Fg;
75 if( flags & STA_BG )
76 m_Bg = attr.m_Bg;
77 if( flags & STA_BRIGHT )
78 m_Bright = attr.m_Bright;
79 if( flags & STA_BLINK )
80 m_Blink = attr.m_Blink;
81 if( flags & STA_UNDERLINE )
82 m_UnderLine = attr.m_UnderLine;
83 if( flags & STA_INVERSE )
84 m_Inverse = attr.m_Inverse;
85 if( flags & STA_INVISIBLE )
86 m_Invisible = attr.m_Invisible;
87 m_NeedUpdate = 1;
88 }
89
90 // I don't know whether assign an 'short' to this 'object' directly will cause
91 // problems or not hence preventing using it. Otherwise I can return 7 directly;
92 CTermCharAttr::AttrType
GetDefVal()93 CTermCharAttr::GetDefVal(){
94 CTermCharAttr attr;
95 *(AttrType*)&attr=0;
96 attr.m_Fg = 7;
97 return *(AttrType*)&attr;
98 }
99
100
101 void
SetToDefault()102 CTermCharAttr::SetToDefault(){ *(AttrType*)this = 0; m_Fg=7;}
103 bool
operator ==(CTermCharAttr & attr)104 CTermCharAttr::operator==(CTermCharAttr& attr){
105 return ((m_Fg == attr.m_Fg) &&
106 (m_Bg == attr.m_Bg) &&
107 (m_Bright == attr.m_Bright) &&
108 (m_Blink == attr.m_Blink) &&
109 (m_UnderLine == attr.m_UnderLine) &&
110 (m_Inverse == attr.m_Inverse) &&
111 (m_Invisible == attr.m_Invisible));
112 }
113
114
115 /////////////////////////////////////////////////////////////////////////////
116 //The functions section of CTermData class.
117 //
118 /////////////////////////////////////////////////////////////////////////////
119
120 // class constructor
CTermData(CTermView * pView)121 CTermData::CTermData(CTermView* pView) :
122 m_pView(pView),
123 m_pCmdLine(NULL),
124 m_Screen(NULL)
125 {
126 m_CaretPos.x = m_CaretPos.y = 0;
127 m_OldCaretPos = m_CaretPos;
128 m_FirstLine = 0;
129 m_RowCount = 0;
130 m_RowsPerPage = m_ColsPerPage = 0;
131 m_ScrollRegionBottom = m_ScrollRegionTop = 0;
132 m_CurAttr.SetToDefault();
133 m_SavedAttr.SetToDefault();
134 m_CmdLine[0] = '\0';
135 m_WaitUpdateDisplay = false;
136 m_NeedDelayedUpdate = false;
137 m_DelayedUpdateTimeout = 0;
138 m_Sel = new CTermSelection(this);
139 #ifdef USE_IPLOOKUP
140 regcomp( &m_RegIp, "([0-9]{1,3}\\.){3}([0-9]{1,3}|\\*)", REG_EXTENDED );
141 #endif
142 m_LineCounter = 0;
143 }
144
145 // class destructor
~CTermData()146 CTermData::~CTermData()
147 {
148 delete m_Sel;
149
150 if( m_DelayedUpdateTimeout )
151 g_source_remove(m_DelayedUpdateTimeout);
152
153 if( m_Screen )
154 {
155 for(int i=0; i<m_RowCount; i++)
156 delete []m_Screen[i];
157 delete []m_Screen;
158 }
159
160 #ifdef USE_IPLOOKUP
161 regfree(&m_RegIp);
162 #endif
163 }
164
165
166 // Set sizes of screen buffer, reallocate buffer automatically when needed.
SetScreenSize(int RowCount,unsigned short RowsPerPage,unsigned short ColsPerPage)167 void CTermData::SetScreenSize( int RowCount, unsigned short RowsPerPage,
168 unsigned short ColsPerPage)
169 {
170 m_RowsPerPage = RowsPerPage;
171 // if cols per page change, reallocate all existing rows.
172 if( m_ColsPerPage != ColsPerPage )
173 {
174 for(int i=0; i < m_RowCount; i++)
175 {
176 char* NewLine = AllocNewLine(ColsPerPage);
177 unsigned short Cols = (ColsPerPage < m_ColsPerPage)?ColsPerPage:m_ColsPerPage;
178 //Copy context of old into new one.
179 memcpy(NewLine, m_Screen[i], Cols);
180 memcpy(GetLineAttr(NewLine, ColsPerPage), GetLineAttr(m_Screen[i]), sizeof(CTermCharAttr::AttrType)*Cols);
181 delete []m_Screen[i];
182 m_Screen[i] = NewLine;
183 }
184 m_ColsPerPage = ColsPerPage;
185 }
186 SetRowCount(RowCount);
187 }
188
189 // Change row count of screen buffer
SetRowCount(int RowCount)190 void CTermData::SetRowCount(int RowCount)
191 {
192 if( RowCount == m_RowCount )
193 return;
194
195 char** NewScreen = new char* [RowCount];
196 if( RowCount > m_RowCount ) // increase row count
197 {
198 memcpy(NewScreen, m_Screen, sizeof(char**)*m_RowCount);
199 for( int i = m_RowCount; i < RowCount; i++ )
200 NewScreen[i] = AllocNewLine(m_ColsPerPage);
201 }
202 else // decrease row count
203 {
204 memcpy(NewScreen, m_Screen, sizeof(char**)*RowCount);
205 for( int i = RowCount; i < m_RowCount; i++ )
206 delete []m_Screen[i];
207 }
208 delete []m_Screen;
209 m_Screen = NewScreen;
210 m_RowCount = RowCount;
211 }
212
213 // Allocate screen buffer.
AllocScreenBuf(int RowCount,unsigned short RowsPerPage,unsigned short ColsPerPage)214 void CTermData::AllocScreenBuf(int RowCount, unsigned short RowsPerPage,unsigned short ColsPerPage){
215 m_RowCount = RowCount;
216 m_RowsPerPage = RowsPerPage;
217 m_ColsPerPage = ColsPerPage;
218
219 m_Screen = new char* [m_RowCount];
220 for(int i=0; i < m_RowCount; i++)
221 m_Screen[i] = AllocNewLine(m_ColsPerPage);
222
223 m_FirstLine = m_RowCount - m_RowsPerPage;
224 m_ScrollRegionTop = 0;
225 m_ScrollRegionBottom = m_RowsPerPage-1;
226 }
227
228 // Initialize new lines
InitNewLine(char * NewLine,const int ColsPerPage)229 void CTermData::InitNewLine(char* NewLine, const int ColsPerPage){
230 memset( NewLine, ' ', ColsPerPage);
231 NewLine[ColsPerPage] = '\0';
232 CTermCharAttr DefAttr; DefAttr.SetToDefault(); DefAttr.SetNeedUpdate(true);
233 memset16( GetLineAttr(NewLine, ColsPerPage), DefAttr.AsType(), ColsPerPage);
234 }
235
236 // LF handler
LineFeed()237 void CTermData::LineFeed()
238 {
239 int top;
240 int bottom = m_FirstLine + m_ScrollRegionBottom;
241
242 m_LineCounter++;
243
244 if(m_CaretPos.y < bottom)
245 {
246 m_CaretPos.y++;
247 return;
248 }
249 else if( m_ScrollRegionBottom != (m_RowsPerPage-1) || m_ScrollRegionTop != 0 )
250 {
251 top = m_FirstLine + m_ScrollRegionTop;
252 // bottom = m_FirsLine + m_ScrollRegionBottom-1;
253 }
254 else
255 {
256 top = 0;
257 bottom = m_RowCount-1;
258 }
259
260 char* tmp = m_Screen[top];
261 InitNewLine(tmp, m_ColsPerPage);
262 for( int i = top; i < bottom; i++ )
263 {
264 m_Screen[i] = m_Screen[i+1];
265 SetWholeLineUpdate(m_Screen[i]);
266 }
267 m_Screen[bottom] = tmp;
268
269 m_NeedDelayedUpdate = true;
270 }
271
272 // BS handler
Back()273 void CTermData::Back()
274 {
275 if(m_CaretPos.x >0 )
276 m_CaretPos.x-- ;
277 }
278
279 // BEL handler
Bell()280 void CTermData::Bell() // virtual
281 {
282 // Call virtual function in dirived class to determine
283 // whether to beep or show a visual indication instead.
284 }
285
286 // CR handler
CarriageReturn()287 void CTermData::CarriageReturn()
288 {
289 m_CaretPos.x = 0;
290 }
291
292 // TAB handler
Tab()293 void CTermData::Tab()
294 {
295 // m_CaretPos.x += ((m_CaretPos.x/8)*8)+8;
296 m_CaretPos.x += ((m_CaretPos.x/4)*4)+4;
297 }
298
PutChar(unsigned char ch)299 void CTermData::PutChar(unsigned char ch)
300 {
301 // C0 control charcters have higher precedence than ANSI escape sequences.
302 // interpret them first whether in ANSI escape sequence or not
303 if( ch < ' ' ) // if this is a control character
304 {
305 switch( ch )
306 {
307 case '\x1b': // ESC
308 m_CmdLine[0] = '\x1b';
309 m_pCmdLine = &m_CmdLine[1];
310 break;
311 case '\n': // LF, line feed
312 LineFeed();
313 break;
314 case '\r': // CR, carriage return
315 CarriageReturn();
316 break;
317 case '\b': // BS, backspace
318 Back();
319 break;
320 case '\a': // BEL, bell
321 Bell();
322 break;
323 case '\t': // HT, horizontal tab
324 Tab();
325 break;
326 }
327 }
328 else // not C0 control characters, check if we're in control sequence.
329 {
330 switch( m_CmdLine[0] )
331 {
332 // m_CmdLine[0] == '\0' means "not in control sequence," and *m_pBuf
333 // is normal text, write to screen buffer directly.
334 case '\0':
335 {
336 if( m_CaretPos.x >= m_ColsPerPage ) // if we are at the bottom of screen
337 {
338 //LineFeed();
339 //CarriageReturn(); // scroll up and move to a new line
340 break;
341 }
342
343 m_Screen[m_CaretPos.y][m_CaretPos.x] = ch;
344
345 CTermCharAttr* pAttr = GetLineAttr( m_Screen[m_CaretPos.y] );
346
347 // Check if we've changed a character which is part of a URL.
348 bool bHyperLink = pAttr[m_CaretPos.x].IsHyperLink();
349 pAttr[m_CaretPos.x] = m_CurAttr;
350 // Set char attributes of current character out put to screen
351
352 // Important:
353 // Set the update flag to indicate "redrawing needed."
354 pAttr[m_CaretPos.x].SetNeedUpdate(true);
355 // 2004.08.07 Added by PCMan:
356 // If we've changed a character which is originally part of a URL,
357 // whole URL must be redrawn or underline won't be updated correctly.
358 if( bHyperLink )
359 {
360 int col;
361 for( col = m_CaretPos.x-1; col > 0 && pAttr[col].IsHyperLink(); col-- )
362 pAttr[col].SetNeedUpdate(true);
363 for( col = m_CaretPos.x+1; col < m_ColsPerPage && pAttr[col].IsHyperLink(); col++ )
364 pAttr[col].SetNeedUpdate(true);
365 }
366 // Advance the caret after character input.
367 m_CaretPos.x++;
368 break;
369 }
370 // m_CmdLine[0] == '\0' means we're currently in ANSI control sequence.
371 // Store ch to CmdLine, and parse ANSI escape sequence when ready.
372 case '\x1b': // ESC, in ANSI escape mode
373 if( m_pCmdLine < (m_CmdLine +sizeof(m_CmdLine)) )
374 {
375 *m_pCmdLine = ch;
376 m_pCmdLine++;
377 }
378
379 if( m_CmdLine[1] == '[' )
380 {
381 if( ch < '@' || ch == '[' || ch > '~' )
382 break;
383 }
384 else
385 {
386 if( ch < '0' || ch > '_' )
387 break;
388 }
389
390 if( m_pCmdLine < (m_CmdLine +sizeof(m_CmdLine)) )
391 *m_pCmdLine = '\0';
392 // Current ANSI escape type is stored in *m_pBuf.
393 ParseAnsiEscapeSequence( (const char*)m_CmdLine, ch);
394 m_CmdLine[0] = '\0';
395 m_pCmdLine = m_CmdLine;
396 } // end switch( m_CmdLine[0] )
397 }
398 }
399
InsertNewLine(int y,int count)400 void CTermData::InsertNewLine(int y, int count)
401 {
402 short tmp = m_ScrollRegionTop;
403 m_ScrollRegionTop = y;
404 ScrollDown( count );
405 m_ScrollRegionTop = tmp;
406 }
407
ScrollUp(int n)408 void CTermData::ScrollUp(int n /*=1*/)
409 {
410 int maxn = m_ScrollRegionBottom - m_ScrollRegionTop +1;
411 if( n > maxn )
412 n = maxn;
413
414 int start = m_FirstLine + m_ScrollRegionTop;
415 int end = m_FirstLine + m_ScrollRegionBottom - n;
416 int i;
417 for( i = start; i <= end; i++ )
418 {
419 // Swap two lines to prevent memmory reallocation.
420 swap(m_Screen[i], m_Screen[i + n]);
421 SetWholeLineUpdate(m_Screen[i]);
422 }
423 for( i = 1; i <= n; i++ )
424 {
425 memset( m_Screen[end+i], ' ', m_ColsPerPage-1 );
426 memset16( GetLineAttr(m_Screen[end+i]), m_CurAttr.AsType(), m_ColsPerPage-1 );
427 SetWholeLineUpdate(m_Screen[end+i]);
428 }
429 }
430
ScrollDown(int n)431 void CTermData::ScrollDown(int n /*=1*/)
432 {
433 int maxn = m_ScrollRegionBottom - m_ScrollRegionTop +1;
434 if( n > maxn )
435 n = maxn;
436
437 int start = m_FirstLine + m_ScrollRegionBottom;
438 int end = m_FirstLine + m_ScrollRegionTop + n;
439 int i;
440 for( i = start; i >= end; i-- )
441 {
442 // Swap two lines to prevent memmory reallocation.
443 swap(m_Screen[i], m_Screen[i - n]);
444 SetWholeLineUpdate(m_Screen[i]);
445 }
446 for( i = 1; i <= n; i++ )
447 {
448 memset( m_Screen[end-i], ' ', m_ColsPerPage-1 );
449 memset16( GetLineAttr(m_Screen[end-i]), m_CurAttr.AsType(), m_ColsPerPage-1 );
450 SetWholeLineUpdate(m_Screen[end-i]);
451 }
452 }
453
454 // Parse ANSI escape sequence
ParseAnsiEscapeSequence(const char * CmdLine,char type)455 void CTermData::ParseAnsiEscapeSequence(const char* CmdLine, char type)
456 {
457 // Current ANSI escape type is stored in *m_pBuf.
458 if( m_CmdLine[1] == '[' ) // ESC[, CSI: control sequence introducer
459 {
460 // CmdLine[] = {'\x1b', '[', ...'\0'};
461 const char* pParam = &CmdLine[2];
462 if(type == 'm' ) // multiple parameters, view as a special case.
463 ParseAnsiColor(pParam);
464 else
465 {
466 int p1=0, p2=0;
467 int n = sscanf(pParam, "%d;%d",&p1,&p2);
468 if( p1 < 0 ) p1 = 0;
469 if( p2 < 0 ) p2 = 0;
470 switch(type)
471 {
472 case 'K': // Clear Line
473 EraseLine(p1);
474 break;
475 case 'H': // Set Caret Pos
476 case 'f':
477 GoToXY(p2-1, p1-1);
478 break;
479 case 'J': // Clear Screen
480 ClearScreen(p1);
481 break;
482 case 'E':
483 break;
484 case 'L':
485 InsertNewLine(m_CaretPos.y, p1);
486 break;
487 case 'A':
488 if(p1 <= 0)
489 p1=1;
490 GoToXY( m_CaretPos.x, m_CaretPos.y - p1 );
491 break;
492 case 'B':
493 if( p1<=0 )
494 p1=1;
495 GoToXY( m_CaretPos.x, m_CaretPos.y + p1 );
496 break;
497 case 'C':
498 if( p1<=0 )
499 p1=1;
500 GoToXY( m_CaretPos.x + p1, m_CaretPos.y );
501 break;
502 case 'D':
503 if( p1<=0 )
504 p1=1;
505 GoToXY( m_CaretPos.x - p1, m_CaretPos.y );
506 break;
507 case 'r': // Set Scroll Region
508 switch(n)
509 {
510 case 0: // Turn off scroll region
511 m_ScrollRegionTop = 0; m_ScrollRegionBottom = m_RowsPerPage-1;
512 break;
513 case 2:
514 p2--;
515 if( p2 > 0 && p2 < m_RowsPerPage && p2 >= m_ScrollRegionTop )
516 m_ScrollRegionBottom = p2;
517 case 1:
518 p1--;
519 if( p1 <= m_ScrollRegionBottom )
520 m_ScrollRegionTop = p1;
521 break;
522 }
523 // printf("scroll region: %d, %d\n", m_ScrollRegionTop, m_ScrollRegionBottom );
524 break;
525 case 's': //save cursor pos
526 break;
527 case 'u': //restore cursor pos
528 break;
529 case '@': //insert char
530 break;
531 case 'M': //delete n line
532 break;
533 case 'P': //delete char
534 break;
535 /*
536 case 'U': //next n page
537 break;
538 case 'V': //previous n page
539 break;
540 */
541 case 'Z': //cursor back tab
542 break;
543 case 'h': //set mode
544 break;
545 case 'l': //reset mode
546 break;
547 case 'n': //Device Status Report
548 break;
549 }
550 }
551 }
552 else
553 {
554 switch(type)
555 {
556 case 'D': // scroll up
557 ScrollUp();
558 break;
559 case 'M': // scroll down
560 ScrollDown();
561 break;
562 case 'E':
563 break;
564 case '7':
565 m_OldCaretPos = m_CaretPos;
566 // printf("save cursor: %d, %d\n", (int)m_CaretPos.x, (int)m_CaretPos.y );
567 m_SavedAttr = m_CurAttr;
568 break;
569 case '8':
570 m_CaretPos = m_OldCaretPos;
571 // printf("restored cursor: %d, %d\n", m_CaretPos.x, m_CaretPos.y );
572 m_CurAttr = m_SavedAttr;
573 m_pView->UpdateCaretPos();
574 break;
575 } //end switch
576 }
577 }
578
579
GoToXY(int x,int y)580 void CTermData::GoToXY(int x, int y)
581 {
582 if( x < 0)
583 x = 0;
584 else if( x >= m_ColsPerPage )
585 x= m_ColsPerPage-1;
586
587 if( y < 0 )
588 y = 0;
589 else if( y >= m_RowsPerPage )
590 y= m_RowsPerPage-1;
591
592 m_CaretPos.x = x;
593 m_CaretPos.y = m_FirstLine + y;
594 }
595
ClearScreen(int p)596 void CTermData::ClearScreen(int p)
597 {
598 m_NeedDelayedUpdate = true;
599
600 // Scroll down a page
601 int bottom = m_RowCount-m_RowsPerPage;
602 int i;
603 char* tmp;
604 for( i = 0; i < bottom; i++ )
605 {
606 int src = i+m_RowsPerPage;
607 swap(m_Screen[i], m_Screen[src]);
608 }
609 for( i = bottom; i< m_RowCount; i++ )
610 InitNewLine( m_Screen[i], m_ColsPerPage);
611
612 switch(p)
613 {
614 // case 2: // Erase entire display
615 // break;
616 case 1: // Erase from beginning to current position (inclusive)
617 tmp = m_Screen[m_CaretPos.y];
618 if( m_CaretPos.x < m_ColsPerPage && m_CaretPos.y > m_RowsPerPage )
619 {
620 memcpy( &tmp[m_CaretPos.x],
621 &m_Screen[m_CaretPos.y-m_RowsPerPage][m_CaretPos.x],
622 m_ColsPerPage-m_CaretPos.x);
623 memcpy( &GetLineAttr(tmp)[m_CaretPos.x],
624 &GetLineAttr(m_Screen[m_CaretPos.y-m_RowsPerPage])[m_CaretPos.x],
625 m_ColsPerPage-m_CaretPos.x);
626 }
627 for( i = m_CaretPos.y + 1; i < m_RowCount; i++)
628 {
629 tmp = m_Screen[i];
630 if( i < m_RowsPerPage)
631 break;
632 memcpy( tmp, m_Screen[i-m_RowsPerPage],m_ColsPerPage);
633 memcpy( GetLineAttr(tmp),GetLineAttr(m_Screen[i-m_RowsPerPage]),m_ColsPerPage); }
634 break;
635 case 0: // Erase from current position to end (inclusive)
636 default:
637 tmp = m_Screen[m_CaretPos.y];
638 if( m_CaretPos.x > 0 && m_CaretPos.y > m_RowsPerPage )
639 {
640 memcpy( tmp, &m_Screen[m_CaretPos.y-m_RowsPerPage],m_CaretPos.x-1);
641 memcpy( GetLineAttr(tmp), GetLineAttr(m_Screen[m_CaretPos.y-m_RowsPerPage]),
642 m_CaretPos.x-1);
643 }
644 for( i = bottom; i < m_CaretPos.y; i++)
645 {
646 tmp = m_Screen[i];
647 if( i < m_RowsPerPage)
648 break;
649 memcpy( tmp, m_Screen[i-m_RowsPerPage],m_ColsPerPage);
650 memcpy( GetLineAttr(tmp),GetLineAttr(m_Screen[i-m_RowsPerPage]),m_ColsPerPage); }
651 break;
652 }
653 }
654
EraseLine(int p)655 void CTermData::EraseLine(int p)
656 {
657 char* pLine = m_Screen[m_CaretPos.y];
658 CTermCharAttr* pAttr = GetLineAttr(pLine);
659 switch(p)
660 {
661 case 0: // Clear from current position to end of line.
662 memset(&pLine[m_CaretPos.x],' ',m_ColsPerPage-m_CaretPos.x);
663 //memset16(&pAttr[m_CaretPos.x],CTermCharAttr::GetDefVal(),m_ColsPerPage-m_CaretPos.x);
664 memset16(&pAttr[m_CaretPos.x],*(short*)&m_CurAttr,m_ColsPerPage-m_CaretPos.x);
665 SetLineUpdate(pLine, m_CaretPos.x, m_ColsPerPage );
666 break;
667 case 1: // Clear from head of line to current position.
668 memset(pLine, ' ',m_CaretPos.x);
669 memset16(pAttr ,CTermCharAttr::GetDefVal(),m_CaretPos.x);
670 SetLineUpdate(pLine, 0, m_CaretPos.x+1);
671 break;
672 default:
673 case 2: // Clear whole line.
674 InitNewLine( pLine, m_ColsPerPage);
675 break;
676 }
677 }
678
ParseAnsiColor(const char * pParam)679 void CTermData::ParseAnsiColor(const char *pParam)
680 {
681 while(*pParam)
682 {
683 int param = 0;
684 while( isdigit(*pParam) )
685 {
686 param *= 10;
687 param += *pParam - '0';
688 pParam++;
689 }
690 if( param < 30 ) // property code
691 {
692 switch(param)
693 {
694 case 0: // normal
695 m_CurAttr.SetToDefault();
696 break;
697 case 1: // bright foreground
698 m_CurAttr.SetBright(true);
699 break;
700 case 4: // underscore
701 m_CurAttr.SetUnderLine(true);
702 break;
703 case 5: // blink
704 case 6:
705 m_CurAttr.SetBlink(true);
706 break;
707 case 7: // reverse
708 m_CurAttr.SetInverse(true);
709 break;
710 case 8: // invisible text (fore=back)
711 m_CurAttr.SetInvisible(true);
712 break;
713 }
714 }
715 else // color code
716 {
717 if( param >= 40 && param <= 47) // background
718 m_CurAttr.SetBackground(param-40);
719 else if(param <= 37) // foreground
720 m_CurAttr.SetForeground(param-30);
721 // else
722 // Undefined parameter!
723 }
724 pParam++;
725 }
726 }
727
memset16(void * dest,short val,size_t n)728 void CTermData::memset16(void *dest, short val, size_t n)
729 {
730 uint16_t *dest16 = (uint16_t *) dest;
731 while (n--)
732 *dest16++ = val;
733 }
734
735
update_view(CTermData * _this)736 static gboolean update_view(CTermData* _this)
737 {
738 if(_this->m_pView)
739 _this->DoUpdateDisplay();
740 INFO("do update");
741 _this->m_DelayedUpdateTimeout = 0; // Simply returning false will remove the source.
742 return false; // remove timeout source
743 }
744
UpdateDisplay()745 void CTermData::UpdateDisplay()
746 {
747 DetectCharSets();
748 DetectHyperLinks();
749 #ifdef USE_IPLOOKUP
750 DetectIpAddrs();
751 #endif
752
753 if( m_pView && m_pView->IsVisible() && !m_WaitUpdateDisplay )
754 {
755 INFO("waiting update");
756 m_WaitUpdateDisplay = true;
757
758 if( m_NeedDelayedUpdate )
759 {
760 if( m_DelayedUpdateTimeout )
761 g_source_remove(m_DelayedUpdateTimeout);
762 m_DelayedUpdateTimeout = g_timeout_add( 80, (GSourceFunc)&update_view, this);
763 }
764 else
765 DoUpdateDisplay();
766 }
767 m_NeedDelayedUpdate = false;
768 }
769
DoUpdateDisplay()770 void CTermData::DoUpdateDisplay()
771 {
772 m_WaitUpdateDisplay = false;
773
774 m_pView->m_Caret.Hide();
775 for( int row = 0; row < m_RowsPerPage; row++ )
776 {
777 int col = 0;
778 CTermCharAttr* attr = GetLineAttr( m_Screen[m_FirstLine + row] );
779 bool callback_has_been_called = false;
780 for( ; col < m_ColsPerPage; col++ )
781 {
782 if( attr[col].IsNeedUpdate() )
783 {
784 if( ! callback_has_been_called )
785 {
786 OnLineModified( m_FirstLine + row );
787 callback_has_been_called = true;
788 }
789
790 if( col>0 && attr[col].GetCharSet()==CTermCharAttr::CS_MBCS2 )
791 col--;
792 m_pView->DrawChar( row, col );
793 attr[col].SetNeedUpdate(false);
794 // Check if this is a MBCS char.
795 if( attr[col].GetCharSet()==CTermCharAttr::CS_MBCS1 )
796 {
797 attr[col+1].SetNeedUpdate(false);
798 col ++;
799 }
800 }
801 }
802 }
803 m_pView->UpdateCaretPos();
804 m_pView->m_Caret.Show();
805 }
806
807 // Detect character sets here. This is for MBCS support.
DetectCharSets()808 void CTermData::DetectCharSets()
809 {
810 int iline = m_FirstLine;
811 int ilast_line = iline + m_RowsPerPage;
812 for( ; iline < ilast_line; iline++ )
813 {
814 char* line = m_Screen[iline];
815 CTermCharAttr* attr = GetLineAttr( line );
816 int col = 0;
817 while( col < m_ColsPerPage )
818 {
819 if( ((unsigned char)line[col]) > 128 && (col+1)< m_ColsPerPage)
820 {
821 if( attr[col].IsNeedUpdate() != attr[col+1].IsNeedUpdate() ) {
822 attr[col].SetNeedUpdate(true);
823 attr[col+1].SetNeedUpdate(true);
824 }
825
826 attr[col].SetCharSet(CTermCharAttr::CS_MBCS1);
827 col++;
828 attr[col].SetCharSet(CTermCharAttr::CS_MBCS2);
829 }
830 else
831 attr[col].SetCharSet(CTermCharAttr::CS_ASCII);
832 col++;
833 }
834 }
835 }
836
837
838 // 2004/08/03 modified by PCMan
839 // Check if 'ch' is an valid character in URL.
isurl(int ch)840 inline bool isurl(int ch)
841 { return isalnum(ch) || strchr("!$&'*+,-./:;=?@_|~%#", ch); }
842 // Though '(' ,')', '<', and '>' are legal characters in URLs, I ignore them because
843 // they are frequently used to enclose URLs. ex: (http://code.google.com/p/pcmanx-gtk2/)
isurlscheme(int ch)844 inline bool isurlscheme(int ch)
845 { return isalnum(ch) || strchr("+-.", ch); }
846
847
848 #define ARRAY_SIZE(arr) \
849 (sizeof(arr) / sizeof(arr[0]))
850 static const char *valid_protocol[] = {
851 "http",
852 "telnet",
853 "https",
854 "ftp",
855 };
isValidURLScheme(const char * line,int schemeStart,int schemeEnd)856 static inline bool isValidURLScheme(const char *line, int schemeStart, int schemeEnd)
857 {
858 char protocol_buffer[16];
859 int strLen = schemeEnd - schemeStart + 1; // index starts from 0
860
861 for (unsigned int i = 0; i < ARRAY_SIZE(valid_protocol); i++) {
862 strcpy(protocol_buffer, valid_protocol[i]);
863 if (!strncmp(line + schemeStart,
864 strcat(protocol_buffer, "://"), strLen)) {
865 return true;
866 }
867 }
868 return false;
869 }
870
871 // 2004/08/06 modified by PCMan
872 // This function is used to detect E-mails and called from UpdateDisplay().
DetectEMails(const char * line,CTermCharAttr * attr,int len)873 inline void DetectEMails( const char *line, CTermCharAttr *attr, int len )
874 {
875 int ilink = 0, stage = 0;
876 for( int col = 0; col < len; col += (CTermCharAttr::CS_ASCII==attr[col].GetCharSet()?1:2) )
877 {
878 unsigned char ch = line[col];
879 switch( stage )
880 {
881 case 0: // a URL character is found, beginning of URL.
882 if( isurl(ch) )
883 {
884 stage = 1;
885 ilink = col;
886 }
887 break;
888 case 1: // '@' is found.
889 if( !isurl(ch) )
890 stage = 0;
891 else if( '@' == ch )
892 stage = 2;
893 break;
894 case 2: // URL characters are found after '@'.
895 if( !isurl(ch) )
896 stage = 0;
897 else if( '.' == ch )
898 stage = 3;
899 break;
900 case 3: // This is a valid URL.
901 if( !isurl(ch) )
902 {
903 for( ; ilink < col; ilink++ )
904 {
905 attr[ilink].SetHyperLink(true);
906 attr[ilink].SetNeedUpdate(true);
907 }
908 stage = 0;
909 }
910 }
911 }
912 }
913
914 // 2004/08/06 added by PCMan
915 // This function is used to detect URLs other than E-mails and called from UpdateDisplay().
DetectCommonURLs(const char * line,CTermCharAttr * attr,int len)916 inline void DetectCommonURLs( const char *line, CTermCharAttr *attr, int len )
917 {
918 int ilink = 0, stage = 0;
919 for( int col = 0; col < len; col += (CTermCharAttr::CS_ASCII==attr[col].GetCharSet()?1:2) )
920 {
921 unsigned char ch = line[col];
922 switch( stage )
923 {
924 case 0: // a URL scheme character is found, beginning of URL.
925 if( isurlscheme(ch) )
926 {
927 stage = 1;
928 ilink = col;
929 }
930 break;
931 case 1: // "://" is found.
932 if (((col + 3) <= len) &&
933 (0 == strncmp(line + col, "://", 3)) &&
934 isurl(line[col + 3]) &&
935 (isValidURLScheme(line, ilink, col + 2))) {
936 stage = 2;
937 col += 3;
938 }
939 else if( !isurlscheme(ch) )
940 stage = 0;
941 break;
942 case 2: // This is a valid URL.
943 if( !isurl(ch) )
944 {
945 for( ; ilink < col; ilink++ )
946 {
947 attr[ilink].SetHyperLink(true);
948 attr[ilink].SetNeedUpdate(true);
949 }
950 stage = 0;
951 }
952 }
953 }
954 }
955
956 // 2004/08/03 modified by PCMan
957 // This function is used to detect hyperlinks and called from UpdateDisplay().
DetectHyperLinks()958 void CTermData::DetectHyperLinks()
959 {
960 int iline = m_FirstLine;
961 int ilast_line = iline + m_RowsPerPage;
962 for( ; iline < ilast_line; iline++ )
963 {
964 char* line = m_Screen[iline];
965 CTermCharAttr* attr = GetLineAttr( line );
966 // Clear all marks.
967 for( int col = 0; col < m_ColsPerPage; col ++ )
968 attr[col].SetHyperLink(false);
969 DetectEMails( line, attr, m_ColsPerPage ); // Search for E-mails.
970 DetectCommonURLs( line, attr, m_ColsPerPage ); // Search for URLs other than E-mail.
971 }
972 }
973
974 #ifdef USE_IPLOOKUP
975
976 /* detect ipv4 addresses. */
DetectIpPatterns(const char * line,CTermCharAttr * attr,int len,const regex_t * regip)977 inline void DetectIpPatterns( const char *line, CTermCharAttr *attr, int len, const regex_t *regip)
978 {
979 regmatch_t match;
980 const char *p = line;
981
982 while ( p < line + len && regexec( regip, p, 1, &match, 0 ) == 0 )
983 {
984 int offset = p - line;
985 if ( CTermCharAttr::CS_ASCII == attr[offset + match.rm_so].GetCharSet()
986 && CTermCharAttr::CS_ASCII == attr[offset + match.rm_eo - 1].GetCharSet() )
987 for ( int i = match.rm_so; i < match.rm_eo; i++ )
988 {
989 attr[offset + i].SetIpAddr(true);
990 attr[offset + i].SetNeedUpdate(true);
991 }
992 p += match.rm_eo + 1;
993 }
994 }
995
996 /* Detect IP addresses (called from UpdateDisplay()) */
DetectIpAddrs()997 void CTermData::DetectIpAddrs()
998 {
999 int iline = m_FirstLine;
1000 int ilast_line = iline + m_RowsPerPage;
1001 for( ; iline < ilast_line; iline++ )
1002 {
1003 char* line = m_Screen[iline];
1004 CTermCharAttr* attr = GetLineAttr( line );
1005 // Clear all marks.
1006 for( int col = 0; col < m_ColsPerPage; col ++ )
1007 attr[col].SetIpAddr(false);
1008 DetectIpPatterns( line, attr, m_ColsPerPage, &m_RegIp );
1009 }
1010 }
1011
1012 #endif
1013
1014 typedef struct {
1015 CTermData* pTermData;
1016 string* text;
1017 int lines;
1018 const char* eol;
1019 } ReadStatus;
1020
GetChangedAttrStr(CTermCharAttr oldattr,CTermCharAttr newattr)1021 string GetChangedAttrStr(CTermCharAttr oldattr, CTermCharAttr newattr)
1022 {
1023 string text = "\x1b["; // Control sequence introducer.
1024 bool reset = false;
1025 // If we want to cancel bright attribute, we must reset all attributes.
1026 bool bright_changed = (newattr.IsBright() != oldattr.IsBright());
1027 if( bright_changed && 1 == oldattr.IsBright() )
1028 reset = true;
1029 // Blink attribute changed.
1030 // We must reset all attributes to remove 'blink' attribute.
1031 bool blink_changed = (newattr.IsBlink() != oldattr.IsBlink());
1032 if( blink_changed && 1 == oldattr.IsBlink() )
1033 reset = true;
1034 // Underline attribute changed.
1035 // We must reset all attributes to remove 'underline' attribute.
1036 bool underline_changed = (newattr.IsUnderLine() != oldattr.IsUnderLine());
1037 if( underline_changed && 1 == oldattr.IsUnderLine() )
1038 reset = true;
1039 // Inverse attribute changed.
1040 // We must reset all attributes to remove 'inverse' attribute.
1041 bool inverse_changed = (newattr.IsInverse() != oldattr.IsInverse());
1042 if( inverse_changed && 1 == oldattr.IsInverse() )
1043 reset = true;
1044
1045 if(reset)
1046 text += ';'; // remove all attributes
1047 if( bright_changed && newattr.IsBright() ) // Add bright attribute
1048 text += "1;";
1049 if( blink_changed && newattr.IsBlink() ) // Add blink attribute
1050 text += "5;";
1051 if( underline_changed && newattr.IsUnderLine() ) // Add underline attributes.
1052 text += "4;";
1053 if( inverse_changed && newattr.IsInverse() ) // Add inverse attributes.
1054 text += "7;";
1055 if( reset || newattr.GetBackground() != oldattr.GetBackground()) // If reset or color changed.
1056 {
1057 char color[] = {'4', static_cast<char>('0'+ newattr.GetBackground()), ';', '\0' };
1058 text += color;
1059 }
1060 if( reset || newattr.GetForeground() != oldattr.GetForeground() )
1061 {
1062 char color[] = {'3', static_cast<char>('0' + newattr.GetForeground()), ';', '\0' };
1063 text += color;
1064 }
1065 if( ';' == text[ text.length()-1 ] ) // Don't worry about access violation because text.Len() always > 1.
1066 text = text.substr(0, text.length()-1);
1067 text += 'm'; // Terminate ANSI escape sequence
1068 return text;
1069 }
1070
read_line_with_color(int row,int col1,int col2,void * data)1071 static void read_line_with_color( int row, int col1, int col2, void* data )
1072 {
1073 ReadStatus* rs = (ReadStatus*)data;
1074 string* text = rs->text;
1075
1076 if ( rs->lines )
1077 {
1078 *text += rs->eol;
1079
1080 if ( col1 == col2 )
1081 return;
1082 }
1083
1084 CTermData* td = rs->pTermData;
1085 char* pLine = td->m_Screen[row];
1086 CTermCharAttr* pAttr = td->GetLineAttr( pLine );
1087 CTermCharAttr attr;
1088 attr.SetToDefault();
1089
1090 // GetLineWithColor
1091 {
1092 string line;
1093 for ( int i = col1; i < col2; i++ )
1094 {
1095 if( !(attr == pAttr[i]) )
1096 {
1097 // Here we've got a characters with different attributes.
1098 line += GetChangedAttrStr( attr, pAttr[i] );
1099 attr = pAttr[i];
1100 }
1101 // Append characters with the same attributes.
1102 if ( pLine[i] )
1103 line += pLine[i];
1104 }
1105
1106 // if current background is black,
1107 if ( attr.GetBackground() == 0 && line.length() )
1108 {
1109 size_t n = line.find_last_not_of( ' ' );
1110 if( n != text->npos )
1111 line = line.substr( 0, n + 1 );
1112 }
1113
1114 *text += line;
1115 }
1116
1117
1118 rs->lines++;
1119 }
1120
read_line(int row,int col1,int col2,void * data)1121 static void read_line( int row, int col1, int col2, void* data )
1122 {
1123 ReadStatus* rs = (ReadStatus*)data;
1124 string* text = rs->text;
1125
1126 CTermData* td = rs->pTermData;
1127
1128 if ( rs->lines )
1129 {
1130 if ( rs->lines == 1 && text->length() > 0 )
1131 {
1132 // the old code does this
1133 //if ( !td->m_Sel->m_BlockMode )
1134 //*text = text->substr( 0, text->length() - 1 );
1135
1136 size_t n = text->find_last_not_of( ' ' );
1137 if ( n != text->npos )
1138 *text = text->substr( 0, n + 1 );
1139 }
1140
1141 *text += rs->eol;
1142 }
1143
1144 string line( td->m_Screen[row] + col1, col2 - col1 );
1145
1146 if ( line.length() )
1147 {
1148 // first line is handled in next call
1149 if ( rs->lines )
1150 {
1151 size_t n = line.find_last_not_of( ' ' );
1152 if ( n != line.npos )
1153 line = line.substr( 0, n + 1 );
1154 else if ( !td->m_Sel->m_BlockMode )
1155 line = "";
1156 }
1157
1158 *text += line;
1159 }
1160 rs->lines++;
1161 }
1162
GetText(CTermSelection * sel,bool trim,bool color)1163 string CTermData::GetText( CTermSelection* sel, bool trim, bool color )
1164 {
1165 string text;
1166 int endrow;
1167 ReadStatus rs = { this, &text, 0, "\n" };
1168
1169 if ( trim )
1170 {
1171 endrow = sel->m_End.row;
1172
1173 for ( ; sel->m_End.row > sel->m_Start.row; sel->m_End.row-- )
1174 {
1175 if( !IsLineEmpty( sel->m_End.row ) )
1176 break;
1177 }
1178 }
1179
1180 if ( color )
1181 {
1182 text = "\x1b[m";
1183 sel->ForEachLine( read_line_with_color, (void*)&rs );
1184 if ( rs.lines > 1 && m_Sel->m_BlockMode )
1185 text += rs.eol;
1186 text += "\x1b[m";
1187 }
1188 else
1189 {
1190 sel->ForEachLine( read_line, (void*)&rs );
1191 if ( rs.lines == 1 )
1192 {
1193 size_t n = text.find_last_not_of( ' ' );
1194 if ( n != text.npos )
1195 text = text.substr( 0, n + 1 );
1196 }
1197 else if ( rs.lines > 1 && m_Sel->m_BlockMode )
1198 text += rs.eol;
1199 }
1200
1201 if ( trim )
1202 sel->m_End.row = endrow;
1203
1204 return text;
1205 }
1206
GetSelectedText(bool trim)1207 string CTermData::GetSelectedText(bool trim)
1208 {
1209 return GetText( m_Sel, trim, false );
1210 }
1211
GetSelectedTextWithColor(bool trim)1212 string CTermData::GetSelectedTextWithColor(bool trim)
1213 {
1214 return GetText( m_Sel, trim, true );
1215 }
1216
1217 // 2004/08/03 Modified by PCMan
1218 // If line[col] is a character in a hyperlink, return the start index of whole hyperlink,
1219 // and store its length in 'len' which is optional and can be NULL.
1220 // If there is no hyperlink found at specified index 'col', the return value will be -1.
HyperLinkHitTest(const char * line,int col,int * len)1221 int CTermData::HyperLinkHitTest(const char *line, int col, int *len/*= NULL*/ )
1222 {
1223 CTermCharAttr* pattr = GetLineAttr( line );
1224 if( !pattr[col].IsHyperLink() )
1225 return -1;
1226 int start = col;
1227 while( pattr[start].IsHyperLink() && start > 0 )
1228 start--;
1229 if( len )
1230 {
1231 while( pattr[col].IsHyperLink() && col < m_ColsPerPage )
1232 col++;
1233 *len = col-start;
1234 }
1235 return start;
1236 }
1237
1238 // 2004/08/12 Modified by PCMan
1239 // Delete n characters at specified position (line, col).
DeleteChar(int line,int col,int n)1240 void CTermData::DeleteChar(int line, int col, int n)
1241 {
1242 if( col > m_ColsPerPage || col < 0 ) return;
1243 if( line < 0 || line >= m_RowCount ) return;
1244 if( (col + n) > m_ColsPerPage )
1245 n = (m_ColsPerPage - col);
1246
1247 char* pline = m_Screen[line];
1248 CTermCharAttr* pattr = GetLineAttr( pline );
1249 int col2 = m_ColsPerPage - n;
1250 for( ; col < col2; col++ )
1251 {
1252 pline[ col ] = pline[ col + n ];
1253 pattr[ col ] = pattr[ col + n ];
1254 pattr[ col ].SetNeedUpdate(true);
1255 }
1256 for( ; col < m_ColsPerPage; col++ )
1257 {
1258 pline[ col ] = ' ';
1259 pattr[ col ].SetToDefault();
1260 pattr[ col ].SetNeedUpdate(true);
1261 }
1262 }
1263
1264 // 2004/08/12 Modified by PCMan
1265 // Insert n space characters at specified position (line, col).
InsertChar(int line,int col,int n)1266 void CTermData::InsertChar( int line, int col, int n )
1267 {
1268 if( col > m_ColsPerPage || col < 0 ) return;
1269 if( line < 0 || line >= m_RowCount ) return;
1270 if( (col + n) > m_ColsPerPage )
1271 n = (m_ColsPerPage - col);
1272 char* pline = m_Screen[line];
1273 CTermCharAttr* pattr = GetLineAttr( pline );
1274
1275 int coln = col + n;
1276 for( int end = m_ColsPerPage; end >= coln; end-- )
1277 {
1278 pline[ end ] = pline[ end - n ];
1279 pattr[ end ] = pattr[ end - n ];
1280 pattr[ end ].SetNeedUpdate(true);
1281 }
1282 for( int x = col; x < coln; x++ )
1283 {
1284 pline[ x ] = ' ';
1285 pattr[ x ] = m_CurAttr;
1286 pattr[ x ].SetNeedUpdate(true);
1287 }
1288 }
1289
1290 #define CHAR_CLASS_WORD 0x1
1291 #define CHAR_CLASS_DELIMITER 0x2
1292 #define CHAR_CLASS_ASCII 0x80
GetCharClass(int line,int col)1293 unsigned char CTermData::GetCharClass( int line, int col )
1294 {
1295 if( col >= m_ColsPerPage || col < 0 )
1296 return 0;
1297 if( line >= m_RowCount || line < 0 )
1298 return 0;
1299
1300 const char* pLine = m_Screen[line];
1301 CTermCharAttr* pAttr = GetLineAttr( pLine );
1302 unsigned char ret = 0;
1303 bool ascii;
1304
1305 switch( pAttr[col].GetCharSet() )
1306 {
1307 case CTermCharAttr::CS_MBCS2:
1308 col--;
1309 case CTermCharAttr::CS_MBCS1:
1310 ascii = false;
1311 break;
1312 case CTermCharAttr::CS_ASCII:
1313 ascii = true;
1314 ret |= CHAR_CLASS_ASCII;
1315 break;
1316 }
1317
1318 if( ascii )
1319 {
1320 if( ( 'A' <= pLine[col] && pLine[col] <= 'Z' ) ||
1321 ( 'a' <= pLine[col] && pLine[col] <= 'z' ) ||
1322 ( '0' <= pLine[col] && pLine[col] <= '9' ) )
1323 ret |= CHAR_CLASS_WORD;
1324 else
1325 {
1326 switch(pLine[col])
1327 {
1328 case '#':
1329 case '$':
1330 case '%':
1331 case '-':
1332 case '+':
1333 case '_':
1334 case '.':
1335 case '/':
1336 ret |= CHAR_CLASS_WORD;
1337 break;
1338 case ' ':
1339 ret |= CHAR_CLASS_DELIMITER;
1340 break;
1341 default:
1342 break;
1343 }
1344 }
1345 }
1346 else
1347 {
1348 ret |= CHAR_CLASS_WORD;
1349 }
1350
1351 return ret;
1352 }
1353
1354 // 2004/08/25 Added by PCMan
1355 // Set attributes of all text in specified range.
SetTextAttr(CTermCharAttr attr,int flags,GdkPoint start,GdkPoint end,bool block)1356 void CTermData::SetTextAttr( CTermCharAttr attr, int flags, GdkPoint start, GdkPoint end, bool block)
1357 {
1358 if( block || start.y == end.y )
1359 {
1360 if( end.x < start.x )
1361 swap(end.y, end.x);
1362
1363 for( int iline = start.y; iline <= end.y; iline++ )
1364 {
1365 CTermCharAttr* pattr = GetLineAttr( m_Screen[iline]);
1366 for( int col = start.x; col < end.x; col++ )
1367 pattr[col].SetTextAttr(attr, flags);
1368 }
1369 }
1370 else
1371 {
1372 CTermCharAttr* pattr = GetLineAttr( m_Screen[start.y]);
1373 for( int col = start.x; col < m_ColsPerPage; col++ )
1374 pattr[col].SetTextAttr(attr, flags);
1375 for( int iline = start.y+1; iline < end.y; iline++ )
1376 {
1377 pattr = GetLineAttr( m_Screen[iline]);
1378 for( int col = 0; col < m_ColsPerPage; col++ )
1379 pattr[col].SetTextAttr(attr, flags);
1380 }
1381 if( start.y != end.y )
1382 {
1383 pattr = GetLineAttr( m_Screen[end.y]);
1384 for( int col = 0; col < end.x; col++ )
1385 pattr[col].SetTextAttr(attr, flags);
1386 }
1387 }
1388 }
1389
1390
1391 //If the specified line on screen has a non space and
1392 //a non '\0' character or its background is not color 0
1393 //(usually black), return false.
1394 //Question: how about selected block which color is inversed?
IsLineEmpty(int iLine)1395 bool CTermData::IsLineEmpty(int iLine)
1396 {
1397 const char* pLine = m_Screen[iLine];
1398 CTermCharAttr* pAttr = GetLineAttr( pLine );
1399 for( int col = 0; col < m_ColsPerPage; col++ )
1400 if( (pLine[col] && pLine[col] != ' ') || pAttr[col].GetBackground() )
1401 return false;
1402 return true;
1403 }
1404
1405 // This is a callback function called from CTermData::DoUpdateDisplay().
1406 // When new characters are written to a line in the screen buffer,
1407 // this function will be called with the line number passed in 'row'.
OnLineModified(int row UNUSED)1408 void CTermData::OnLineModified(int row UNUSED)
1409 {
1410 // This function can be overriden in derived class.
1411 }
1412
1413