PageRenderTime 52ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Screen.cpp

https://gitlab.com/rmstoi/mtermite
C++ | 1569 lines | 986 code | 266 blank | 317 comment | 216 complexity | 6f11d5a097a697726fcb548b49de23f2 MD5 | raw file
  1. /*
  2. This file is part of Konsole, an X terminal.
  3. Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
  4. Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  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. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  16. 02110-1301 USA.
  17. */
  18. // Own
  19. #include "Screen.h"
  20. // Standard
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <unistd.h>
  24. #include <assert.h>
  25. #include <string.h>
  26. #include <ctype.h>
  27. // Qt
  28. #include <QtCore/QTextStream>
  29. #include <QtCore/QDate>
  30. // Konsole
  31. #include "konsole_wcwidth.h"
  32. #include "TerminalCharacterDecoder.h"
  33. using namespace Konsole;
  34. //FIXME: this is emulation specific. Use false for xterm, true for ANSI.
  35. //FIXME: see if we can get this from terminfo.
  36. #define BS_CLEARS false
  37. //Macro to convert x,y position on screen to position within an image.
  38. //
  39. //Originally the image was stored as one large contiguous block of
  40. //memory, so a position within the image could be represented as an
  41. //offset from the beginning of the block. For efficiency reasons this
  42. //is no longer the case.
  43. //Many internal parts of this class still use this representation for parameters and so on,
  44. //notably moveImage() and clearImage().
  45. //This macro converts from an X,Y position into an image offset.
  46. #ifndef loc
  47. #define loc(X,Y) ((Y)*columns+(X))
  48. #endif
  49. Character Screen::defaultChar = Character(' ',
  50. CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR),
  51. CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR),
  52. DEFAULT_RENDITION);
  53. //#define REVERSE_WRAPPED_LINES // for wrapped line debug
  54. Screen::Screen(int l, int c)
  55. : lines(l),
  56. columns(c),
  57. screenLines(new ImageLine[lines+1] ),
  58. _scrolledLines(0),
  59. _droppedLines(0),
  60. hist(new HistoryScrollNone()),
  61. cuX(0), cuY(0),
  62. cu_re(0),
  63. tmargin(0), bmargin(0),
  64. tabstops(0),
  65. sel_begin(0), sel_TL(0), sel_BR(0),
  66. sel_busy(false),
  67. columnmode(false),
  68. ef_fg(CharacterColor()), ef_bg(CharacterColor()), ef_re(0),
  69. sa_cuX(0), sa_cuY(0),
  70. sa_cu_re(0),
  71. lastPos(-1)
  72. {
  73. lineProperties.resize(lines+1);
  74. for (int i=0;i<lines+1;i++)
  75. lineProperties[i]=LINE_DEFAULT;
  76. initTabStops();
  77. clearSelection();
  78. reset();
  79. }
  80. /*! Destructor
  81. */
  82. Screen::~Screen()
  83. {
  84. delete[] screenLines;
  85. delete[] tabstops;
  86. delete hist;
  87. }
  88. /* ------------------------------------------------------------------------- */
  89. /* */
  90. /* Normalized Screen Operations */
  91. /* */
  92. /* ------------------------------------------------------------------------- */
  93. // Cursor Setting --------------------------------------------------------------
  94. /*! \section Cursor
  95. The `cursor' is a location within the screen that is implicitely used in
  96. many operations. The operations within this section allow to manipulate
  97. the cursor explicitly and to obtain it's value.
  98. The position of the cursor is guarantied to be between (including) 0 and
  99. `columns-1' and `lines-1'.
  100. */
  101. /*!
  102. Move the cursor up.
  103. The cursor will not be moved beyond the top margin.
  104. */
  105. void Screen::cursorUp(int n)
  106. //=CUU
  107. {
  108. if (n == 0) n = 1; // Default
  109. int stop = cuY < tmargin ? 0 : tmargin;
  110. cuX = qMin(columns-1,cuX); // nowrap!
  111. cuY = qMax(stop,cuY-n);
  112. }
  113. /*!
  114. Move the cursor down.
  115. The cursor will not be moved beyond the bottom margin.
  116. */
  117. void Screen::cursorDown(int n)
  118. //=CUD
  119. {
  120. if (n == 0) n = 1; // Default
  121. int stop = cuY > bmargin ? lines-1 : bmargin;
  122. cuX = qMin(columns-1,cuX); // nowrap!
  123. cuY = qMin(stop,cuY+n);
  124. }
  125. /*!
  126. Move the cursor left.
  127. The cursor will not move beyond the first column.
  128. */
  129. void Screen::cursorLeft(int n)
  130. //=CUB
  131. {
  132. if (n == 0) n = 1; // Default
  133. cuX = qMin(columns-1,cuX); // nowrap!
  134. cuX = qMax(0,cuX-n);
  135. }
  136. /*!
  137. Move the cursor left.
  138. The cursor will not move beyond the rightmost column.
  139. */
  140. void Screen::cursorRight(int n)
  141. //=CUF
  142. {
  143. if (n == 0) n = 1; // Default
  144. cuX = qMin(columns-1,cuX+n);
  145. }
  146. void Screen::setMargins(int top, int bot)
  147. //=STBM
  148. {
  149. if (top == 0) top = 1; // Default
  150. if (bot == 0) bot = lines; // Default
  151. top = top - 1; // Adjust to internal lineno
  152. bot = bot - 1; // Adjust to internal lineno
  153. if ( !( 0 <= top && top < bot && bot < lines ) )
  154. { qDebug()<<" setRegion("<<top<<","<<bot<<") : bad range.";
  155. return; // Default error action: ignore
  156. }
  157. tmargin = top;
  158. bmargin = bot;
  159. cuX = 0;
  160. cuY = getMode(MODE_Origin) ? top : 0;
  161. }
  162. int Screen::topMargin() const
  163. {
  164. return tmargin;
  165. }
  166. int Screen::bottomMargin() const
  167. {
  168. return bmargin;
  169. }
  170. void Screen::index()
  171. //=IND
  172. {
  173. if (cuY == bmargin)
  174. {
  175. scrollUp(1);
  176. }
  177. else if (cuY < lines-1)
  178. cuY += 1;
  179. }
  180. void Screen::reverseIndex()
  181. //=RI
  182. {
  183. if (cuY == tmargin)
  184. scrollDown(tmargin,1);
  185. else if (cuY > 0)
  186. cuY -= 1;
  187. }
  188. /*!
  189. Move the cursor to the begin of the next line.
  190. If cursor is on bottom margin, the region between the
  191. actual top and bottom margin is scrolled up.
  192. */
  193. void Screen::NextLine()
  194. //=NEL
  195. {
  196. Return(); index();
  197. }
  198. void Screen::eraseChars(int n)
  199. {
  200. if (n == 0) n = 1; // Default
  201. int p = qMax(0,qMin(cuX+n-1,columns-1));
  202. clearImage(loc(cuX,cuY),loc(p,cuY),' ');
  203. }
  204. void Screen::deleteChars(int n)
  205. {
  206. Q_ASSERT( n >= 0 );
  207. // always delete at least one char
  208. if (n == 0)
  209. n = 1;
  210. // if cursor is beyond the end of the line there is nothing to do
  211. if ( cuX >= screenLines[cuY].count() )
  212. return;
  213. if ( cuX+n >= screenLines[cuY].count() )
  214. n = screenLines[cuY].count() - 1 - cuX;
  215. Q_ASSERT( n >= 0 );
  216. Q_ASSERT( cuX+n < screenLines[cuY].count() );
  217. screenLines[cuY].remove(cuX,n);
  218. }
  219. void Screen::insertChars(int n)
  220. {
  221. if (n == 0) n = 1; // Default
  222. if ( screenLines[cuY].size() < cuX )
  223. screenLines[cuY].resize(cuX);
  224. screenLines[cuY].insert(cuX,n,' ');
  225. if ( screenLines[cuY].count() > columns )
  226. screenLines[cuY].resize(columns);
  227. }
  228. void Screen::deleteLines(int n)
  229. {
  230. if (n == 0) n = 1; // Default
  231. scrollUp(cuY,n);
  232. }
  233. /*! insert `n' lines at the cursor position.
  234. The cursor is not moved by the operation.
  235. */
  236. void Screen::insertLines(int n)
  237. {
  238. if (n == 0) n = 1; // Default
  239. scrollDown(cuY,n);
  240. }
  241. // Mode Operations -----------------------------------------------------------
  242. /*! Set a specific mode. */
  243. void Screen::setMode(int m)
  244. {
  245. currParm.mode[m] = true;
  246. switch(m)
  247. {
  248. case MODE_Origin : cuX = 0; cuY = tmargin; break; //FIXME: home
  249. }
  250. }
  251. /*! Reset a specific mode. */
  252. void Screen::resetMode(int m)
  253. {
  254. currParm.mode[m] = false;
  255. switch(m)
  256. {
  257. case MODE_Origin : cuX = 0; cuY = 0; break; //FIXME: home
  258. }
  259. }
  260. /*! Save a specific mode. */
  261. void Screen::saveMode(int m)
  262. {
  263. saveParm.mode[m] = currParm.mode[m];
  264. }
  265. /*! Restore a specific mode. */
  266. void Screen::restoreMode(int m)
  267. {
  268. currParm.mode[m] = saveParm.mode[m];
  269. }
  270. bool Screen::getMode(int m) const
  271. {
  272. return currParm.mode[m];
  273. }
  274. void Screen::saveCursor()
  275. {
  276. sa_cuX = cuX;
  277. sa_cuY = cuY;
  278. sa_cu_re = cu_re;
  279. sa_cu_fg = cu_fg;
  280. sa_cu_bg = cu_bg;
  281. }
  282. void Screen::restoreCursor()
  283. {
  284. cuX = qMin(sa_cuX,columns-1);
  285. cuY = qMin(sa_cuY,lines-1);
  286. cu_re = sa_cu_re;
  287. cu_fg = sa_cu_fg;
  288. cu_bg = sa_cu_bg;
  289. effectiveRendition();
  290. }
  291. /* ------------------------------------------------------------------------- */
  292. /* */
  293. /* Screen Operations */
  294. /* */
  295. /* ------------------------------------------------------------------------- */
  296. /*! Resize the screen image
  297. The topmost left position is maintained, while lower lines
  298. or right hand side columns might be removed or filled with
  299. spaces to fit the new size.
  300. The region setting is reset to the whole screen and the
  301. tab positions reinitialized.
  302. If the new image is narrower than the old image then text on lines
  303. which extends past the end of the new image is preserved so that it becomes
  304. visible again if the screen is later resized to make it larger.
  305. */
  306. void Screen::resizeImage(int new_lines, int new_columns)
  307. {
  308. if ((new_lines==lines) && (new_columns==columns)) return;
  309. if (cuY > new_lines-1)
  310. { // attempt to preserve focus and lines
  311. bmargin = lines-1; //FIXME: margin lost
  312. for (int i = 0; i < cuY-(new_lines-1); i++)
  313. {
  314. addHistLine(); scrollUp(0,1);
  315. }
  316. }
  317. // create new screen lines and copy from old to new
  318. ImageLine* newScreenLines = new ImageLine[new_lines+1];
  319. for (int i=0; i < qMin(lines-1,new_lines+1) ;i++)
  320. newScreenLines[i]=screenLines[i];
  321. for (int i=lines;(i > 0) && (i<new_lines+1);i++)
  322. newScreenLines[i].resize( new_columns );
  323. lineProperties.resize(new_lines+1);
  324. for (int i=lines;(i > 0) && (i<new_lines+1);i++)
  325. lineProperties[i] = LINE_DEFAULT;
  326. clearSelection();
  327. delete[] screenLines;
  328. screenLines = newScreenLines;
  329. lines = new_lines;
  330. columns = new_columns;
  331. cuX = qMin(cuX,columns-1);
  332. cuY = qMin(cuY,lines-1);
  333. // FIXME: try to keep values, evtl.
  334. tmargin=0;
  335. bmargin=lines-1;
  336. initTabStops();
  337. clearSelection();
  338. }
  339. void Screen::setDefaultMargins()
  340. {
  341. tmargin = 0;
  342. bmargin = lines-1;
  343. }
  344. /*
  345. Clarifying rendition here and in the display.
  346. currently, the display's color table is
  347. 0 1 2 .. 9 10 .. 17
  348. dft_fg, dft_bg, dim 0..7, intensive 0..7
  349. cu_fg, cu_bg contain values 0..8;
  350. - 0 = default color
  351. - 1..8 = ansi specified color
  352. re_fg, re_bg contain values 0..17
  353. due to the TerminalDisplay's color table
  354. rendition attributes are
  355. attr widget screen
  356. -------------- ------ ------
  357. RE_UNDERLINE XX XX affects foreground only
  358. RE_BLINK XX XX affects foreground only
  359. RE_BOLD XX XX affects foreground only
  360. RE_REVERSE -- XX
  361. RE_TRANSPARENT XX -- affects background only
  362. RE_INTENSIVE XX -- affects foreground only
  363. Note that RE_BOLD is used in both widget
  364. and screen rendition. Since xterm/vt102
  365. is to poor to distinguish between bold
  366. (which is a font attribute) and intensive
  367. (which is a color attribute), we translate
  368. this and RE_BOLD in falls eventually appart
  369. into RE_BOLD and RE_INTENSIVE.
  370. */
  371. void Screen::reverseRendition(Character& p) const
  372. {
  373. CharacterColor f = p.foregroundColor;
  374. CharacterColor b = p.backgroundColor;
  375. p.foregroundColor = b;
  376. p.backgroundColor = f; //p->r &= ~RE_TRANSPARENT;
  377. }
  378. void Screen::effectiveRendition()
  379. // calculate rendition
  380. {
  381. //copy "current rendition" straight into "effective rendition", which is then later copied directly
  382. //into the image[] array which holds the characters and their appearance properties.
  383. //- The old version below filtered out all attributes other than underline and blink at this stage,
  384. //so that they would not be copied into the image[] array and hence would not be visible by TerminalDisplay
  385. //which actually paints the screen using the information from the image[] array.
  386. //I don't know why it did this, but I'm fairly sure it was the wrong thing to do. The net result
  387. //was that bold text wasn't printed in bold by Konsole.
  388. ef_re = cu_re;
  389. //OLD VERSION:
  390. //ef_re = cu_re & (RE_UNDERLINE | RE_BLINK);
  391. if (cu_re & RE_REVERSE)
  392. {
  393. ef_fg = cu_bg;
  394. ef_bg = cu_fg;
  395. }
  396. else
  397. {
  398. ef_fg = cu_fg;
  399. ef_bg = cu_bg;
  400. }
  401. if (cu_re & RE_BOLD)
  402. ef_fg.toggleIntensive();
  403. }
  404. /*!
  405. returns the image.
  406. Get the size of the image by \sa getLines and \sa getColumns.
  407. NOTE that the image returned by this function must later be
  408. freed.
  409. */
  410. void Screen::copyFromHistory(Character* dest, int startLine, int count) const
  411. {
  412. Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= hist->getLines() );
  413. for (int line = startLine; line < startLine + count; line++)
  414. {
  415. const int length = qMin(columns,hist->getLineLen(line));
  416. const int destLineOffset = (line-startLine)*columns;
  417. hist->getCells(line,0,length,dest + destLineOffset);
  418. for (int column = length; column < columns; column++)
  419. dest[destLineOffset+column] = defaultChar;
  420. // invert selected text
  421. if (sel_begin !=-1)
  422. {
  423. for (int column = 0; column < columns; column++)
  424. {
  425. if (isSelected(column,line))
  426. {
  427. reverseRendition(dest[destLineOffset + column]);
  428. }
  429. }
  430. }
  431. }
  432. }
  433. void Screen::copyFromScreen(Character* dest , int startLine , int count) const
  434. {
  435. Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= lines );
  436. for (int line = startLine; line < (startLine+count) ; line++)
  437. {
  438. int srcLineStartIndex = line*columns;
  439. int destLineStartIndex = (line-startLine)*columns;
  440. for (int column = 0; column < columns; column++)
  441. {
  442. int srcIndex = srcLineStartIndex + column;
  443. int destIndex = destLineStartIndex + column;
  444. dest[destIndex] = screenLines[srcIndex/columns].value(srcIndex%columns,defaultChar);
  445. // invert selected text
  446. if (sel_begin != -1 && isSelected(column,line + hist->getLines()))
  447. reverseRendition(dest[destIndex]);
  448. }
  449. }
  450. }
  451. void Screen::getImage( Character* dest, int size, int startLine, int endLine ) const
  452. {
  453. Q_ASSERT( startLine >= 0 );
  454. Q_ASSERT( endLine >= startLine && endLine < hist->getLines() + lines );
  455. const int mergedLines = endLine - startLine + 1;
  456. Q_ASSERT( size >= mergedLines * columns );
  457. Q_UNUSED( size );
  458. const int linesInHistoryBuffer = qBound(0,hist->getLines()-startLine,mergedLines);
  459. const int linesInScreenBuffer = mergedLines - linesInHistoryBuffer;
  460. // copy lines from history buffer
  461. if (linesInHistoryBuffer > 0) {
  462. copyFromHistory(dest,startLine,linesInHistoryBuffer);
  463. }
  464. // copy lines from screen buffer
  465. if (linesInScreenBuffer > 0) {
  466. copyFromScreen(dest + linesInHistoryBuffer*columns,
  467. startLine + linesInHistoryBuffer - hist->getLines(),
  468. linesInScreenBuffer);
  469. }
  470. // invert display when in screen mode
  471. if (getMode(MODE_Screen))
  472. {
  473. for (int i = 0; i < mergedLines*columns; i++)
  474. reverseRendition(dest[i]); // for reverse display
  475. }
  476. // mark the character at the current cursor position
  477. int cursorIndex = loc(cuX, cuY + linesInHistoryBuffer);
  478. if(getMode(MODE_Cursor) && cursorIndex < columns*mergedLines)
  479. dest[cursorIndex].rendition |= RE_CURSOR;
  480. }
  481. QVector<LineProperty> Screen::getLineProperties( int startLine , int endLine ) const
  482. {
  483. Q_ASSERT( startLine >= 0 );
  484. Q_ASSERT( endLine >= startLine && endLine < hist->getLines() + lines );
  485. const int mergedLines = endLine-startLine+1;
  486. const int linesInHistory = qBound(0,hist->getLines()-startLine,mergedLines);
  487. const int linesInScreen = mergedLines - linesInHistory;
  488. QVector<LineProperty> result(mergedLines);
  489. int index = 0;
  490. // copy properties for lines in history
  491. for (int line = startLine; line < startLine + linesInHistory; line++)
  492. {
  493. //TODO Support for line properties other than wrapped lines
  494. if (hist->isWrappedLine(line))
  495. {
  496. result[index] = (LineProperty)(result[index] | LINE_WRAPPED);
  497. }
  498. index++;
  499. }
  500. // copy properties for lines in screen buffer
  501. const int firstScreenLine = startLine + linesInHistory - hist->getLines();
  502. for (int line = firstScreenLine; line < firstScreenLine+linesInScreen; line++)
  503. {
  504. result[index]=lineProperties[line];
  505. index++;
  506. }
  507. return result;
  508. }
  509. /*!
  510. */
  511. void Screen::reset(bool clearScreen)
  512. {
  513. setMode(MODE_Wrap ); saveMode(MODE_Wrap ); // wrap at end of margin
  514. resetMode(MODE_Origin); saveMode(MODE_Origin); // position refere to [1,1]
  515. resetMode(MODE_Insert); saveMode(MODE_Insert); // overstroke
  516. setMode(MODE_Cursor); // cursor visible
  517. resetMode(MODE_Screen); // screen not inverse
  518. resetMode(MODE_NewLine);
  519. tmargin=0;
  520. bmargin=lines-1;
  521. setDefaultRendition();
  522. saveCursor();
  523. if ( clearScreen )
  524. clear();
  525. }
  526. /*! Clear the entire screen and home the cursor.
  527. */
  528. void Screen::clear()
  529. {
  530. clearEntireScreen();
  531. home();
  532. }
  533. void Screen::BackSpace()
  534. {
  535. cuX = qMin(columns-1,cuX); // nowrap!
  536. cuX = qMax(0,cuX-1);
  537. // if (BS_CLEARS) image[loc(cuX,cuY)].character = ' ';
  538. if (screenLines[cuY].size() < cuX+1)
  539. screenLines[cuY].resize(cuX+1);
  540. if (BS_CLEARS) screenLines[cuY][cuX].character = ' ';
  541. }
  542. void Screen::Tabulate(int n)
  543. {
  544. // note that TAB is a format effector (does not write ' ');
  545. if (n == 0) n = 1;
  546. while((n > 0) && (cuX < columns-1))
  547. {
  548. cursorRight(1); while((cuX < columns-1) && !tabstops[cuX]) cursorRight(1);
  549. n--;
  550. }
  551. }
  552. void Screen::backTabulate(int n)
  553. {
  554. // note that TAB is a format effector (does not write ' ');
  555. if (n == 0) n = 1;
  556. while((n > 0) && (cuX > 0))
  557. {
  558. cursorLeft(1); while((cuX > 0) && !tabstops[cuX]) cursorLeft(1);
  559. n--;
  560. }
  561. }
  562. void Screen::clearTabStops()
  563. {
  564. for (int i = 0; i < columns; i++) tabstops[i] = false;
  565. }
  566. void Screen::changeTabStop(bool set)
  567. {
  568. if (cuX >= columns) return;
  569. tabstops[cuX] = set;
  570. }
  571. void Screen::initTabStops()
  572. {
  573. delete[] tabstops;
  574. tabstops = new bool[columns];
  575. // Arrg! The 1st tabstop has to be one longer than the other.
  576. // i.e. the kids start counting from 0 instead of 1.
  577. // Other programs might behave correctly. Be aware.
  578. for (int i = 0; i < columns; i++) tabstops[i] = (i%8 == 0 && i != 0);
  579. }
  580. /*!
  581. This behaves either as IND (Screen::Index) or as NEL (Screen::NextLine)
  582. depending on the NewLine Mode (LNM). This mode also
  583. affects the key sequence returned for newline ([CR]LF).
  584. */
  585. void Screen::NewLine()
  586. {
  587. if (getMode(MODE_NewLine)) Return();
  588. index();
  589. }
  590. /*! put `c' literally onto the screen at the current cursor position.
  591. VT100 uses the convention to produce an automatic newline (am)
  592. with the *first* character that would fall onto the next line (xenl).
  593. */
  594. void Screen::checkSelection(int from, int to)
  595. {
  596. if (sel_begin == -1) return;
  597. int scr_TL = loc(0, hist->getLines());
  598. //Clear entire selection if it overlaps region [from, to]
  599. if ( (sel_BR > (from+scr_TL) )&&(sel_TL < (to+scr_TL)) )
  600. {
  601. clearSelection();
  602. }
  603. }
  604. void Screen::ShowCharacter(unsigned short c)
  605. {
  606. // Note that VT100 does wrapping BEFORE putting the character.
  607. // This has impact on the assumption of valid cursor positions.
  608. // We indicate the fact that a newline has to be triggered by
  609. // putting the cursor one right to the last column of the screen.
  610. int w = konsole_wcwidth(c);
  611. if (w <= 0)
  612. return;
  613. if (cuX+w > columns) {
  614. if (getMode(MODE_Wrap)) {
  615. lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | LINE_WRAPPED);
  616. NextLine();
  617. }
  618. else
  619. cuX = columns-w;
  620. }
  621. // ensure current line vector has enough elements
  622. int size = screenLines[cuY].size();
  623. if (size == 0 && cuY > 0)
  624. {
  625. screenLines[cuY].resize( qMax(screenLines[cuY-1].size() , cuX+w) );
  626. }
  627. else
  628. {
  629. if (size < cuX+w)
  630. {
  631. screenLines[cuY].resize(cuX+w);
  632. }
  633. }
  634. if (getMode(MODE_Insert)) insertChars(w);
  635. lastPos = loc(cuX,cuY);
  636. // check if selection is still valid.
  637. checkSelection(lastPos, lastPos);
  638. Character& currentChar = screenLines[cuY][cuX];
  639. currentChar.character = c;
  640. currentChar.foregroundColor = ef_fg;
  641. currentChar.backgroundColor = ef_bg;
  642. currentChar.rendition = ef_re;
  643. int i = 0;
  644. int newCursorX = cuX + w--;
  645. while(w)
  646. {
  647. i++;
  648. if ( screenLines[cuY].size() < cuX + i + 1 )
  649. screenLines[cuY].resize(cuX+i+1);
  650. Character& ch = screenLines[cuY][cuX + i];
  651. ch.character = 0;
  652. ch.foregroundColor = ef_fg;
  653. ch.backgroundColor = ef_bg;
  654. ch.rendition = ef_re;
  655. w--;
  656. }
  657. cuX = newCursorX;
  658. }
  659. void Screen::compose(const QString& /*compose*/)
  660. {
  661. Q_ASSERT( 0 /*Not implemented yet*/ );
  662. /* if (lastPos == -1)
  663. return;
  664. QChar c(image[lastPos].character);
  665. compose.prepend(c);
  666. //compose.compose(); ### FIXME!
  667. image[lastPos].character = compose[0].unicode();*/
  668. }
  669. int Screen::scrolledLines() const
  670. {
  671. return _scrolledLines;
  672. }
  673. int Screen::droppedLines() const
  674. {
  675. return _droppedLines;
  676. }
  677. void Screen::resetDroppedLines()
  678. {
  679. _droppedLines = 0;
  680. }
  681. void Screen::resetScrolledLines()
  682. {
  683. //kDebug() << "scrolled lines reset";
  684. _scrolledLines = 0;
  685. }
  686. // Region commands -------------------------------------------------------------
  687. void Screen::scrollUp(int n)
  688. {
  689. if (n == 0) n = 1; // Default
  690. if (tmargin == 0) addHistLine(); // hist.history
  691. scrollUp(tmargin, n);
  692. }
  693. /*! scroll up `n' lines within current region.
  694. The `n' new lines are cleared.
  695. \sa setRegion \sa scrollDown
  696. */
  697. QRect Screen::lastScrolledRegion() const
  698. {
  699. return _lastScrolledRegion;
  700. }
  701. void Screen::scrollUp(int from, int n)
  702. {
  703. if (n <= 0 || from + n > bmargin) return;
  704. _scrolledLines -= n;
  705. _lastScrolledRegion = QRect(0,tmargin,columns-1,(bmargin-tmargin));
  706. //FIXME: make sure `tmargin', `bmargin', `from', `n' is in bounds.
  707. moveImage(loc(0,from),loc(0,from+n),loc(columns-1,bmargin));
  708. clearImage(loc(0,bmargin-n+1),loc(columns-1,bmargin),' ');
  709. }
  710. void Screen::scrollDown(int n)
  711. {
  712. if (n == 0) n = 1; // Default
  713. scrollDown(tmargin, n);
  714. }
  715. /*! scroll down `n' lines within current region.
  716. The `n' new lines are cleared.
  717. \sa setRegion \sa scrollUp
  718. */
  719. void Screen::scrollDown(int from, int n)
  720. {
  721. //kDebug() << "Screen::scrollDown( from: " << from << " , n: " << n << ")";
  722. _scrolledLines += n;
  723. //FIXME: make sure `tmargin', `bmargin', `from', `n' is in bounds.
  724. if (n <= 0) return;
  725. if (from > bmargin) return;
  726. if (from + n > bmargin) n = bmargin - from;
  727. moveImage(loc(0,from+n),loc(0,from),loc(columns-1,bmargin-n));
  728. clearImage(loc(0,from),loc(columns-1,from+n-1),' ');
  729. }
  730. void Screen::setCursorYX(int y, int x)
  731. {
  732. setCursorY(y); setCursorX(x);
  733. }
  734. void Screen::setCursorX(int x)
  735. {
  736. if (x == 0) x = 1; // Default
  737. x -= 1; // Adjust
  738. cuX = qMax(0,qMin(columns-1, x));
  739. }
  740. void Screen::setCursorY(int y)
  741. {
  742. if (y == 0) y = 1; // Default
  743. y -= 1; // Adjust
  744. cuY = qMax(0,qMin(lines -1, y + (getMode(MODE_Origin) ? tmargin : 0) ));
  745. }
  746. void Screen::home()
  747. {
  748. cuX = 0;
  749. cuY = 0;
  750. }
  751. void Screen::Return()
  752. {
  753. cuX = 0;
  754. }
  755. int Screen::getCursorX() const
  756. {
  757. return cuX;
  758. }
  759. int Screen::getCursorY() const
  760. {
  761. return cuY;
  762. }
  763. // Erasing ---------------------------------------------------------------------
  764. /*! \section Erasing
  765. This group of operations erase parts of the screen contents by filling
  766. it with spaces colored due to the current rendition settings.
  767. Althought the cursor position is involved in most of these operations,
  768. it is never modified by them.
  769. */
  770. /*! fill screen between (including) `loca' (start) and `loce' (end) with spaces.
  771. This is an internal helper functions. The parameter types are internal
  772. addresses of within the screen image and make use of the way how the
  773. screen matrix is mapped to the image vector.
  774. */
  775. void Screen::clearImage(int loca, int loce, char c)
  776. {
  777. int scr_TL=loc(0,hist->getLines());
  778. //FIXME: check positions
  779. //Clear entire selection if it overlaps region to be moved...
  780. if ( (sel_BR > (loca+scr_TL) )&&(sel_TL < (loce+scr_TL)) )
  781. {
  782. clearSelection();
  783. }
  784. int topLine = loca/columns;
  785. int bottomLine = loce/columns;
  786. Character clearCh(c,cu_fg,cu_bg,DEFAULT_RENDITION);
  787. //if the character being used to clear the area is the same as the
  788. //default character, the affected lines can simply be shrunk.
  789. bool isDefaultCh = (clearCh == Character());
  790. for (int y=topLine;y<=bottomLine;y++)
  791. {
  792. lineProperties[y] = 0;
  793. int endCol = ( y == bottomLine) ? loce%columns : columns-1;
  794. int startCol = ( y == topLine ) ? loca%columns : 0;
  795. QVector<Character>& line = screenLines[y];
  796. if ( isDefaultCh && endCol == columns-1 )
  797. {
  798. line.resize(startCol);
  799. }
  800. else
  801. {
  802. if (line.size() < endCol + 1)
  803. line.resize(endCol+1);
  804. Character* data = line.data();
  805. for (int i=startCol;i<=endCol;i++)
  806. data[i]=clearCh;
  807. }
  808. }
  809. }
  810. /*! move image between (including) `sourceBegin' and `sourceEnd' to 'dest'.
  811. The 'dest', 'sourceBegin' and 'sourceEnd' parameters can be generated using
  812. the loc(column,line) macro.
  813. NOTE: moveImage() can only move whole lines.
  814. This is an internal helper functions. The parameter types are internal
  815. addresses of within the screen image and make use of the way how the
  816. screen matrix is mapped to the image vector.
  817. */
  818. void Screen::moveImage(int dest, int sourceBegin, int sourceEnd)
  819. {
  820. //kDebug() << "moving image from (" << (sourceBegin/columns)
  821. // << "," << (sourceEnd/columns) << ") to " <<
  822. // (dest/columns);
  823. Q_ASSERT( sourceBegin <= sourceEnd );
  824. int lines=(sourceEnd-sourceBegin)/columns;
  825. //move screen image and line properties:
  826. //the source and destination areas of the image may overlap,
  827. //so it matters that we do the copy in the right order -
  828. //forwards if dest < sourceBegin or backwards otherwise.
  829. //(search the web for 'memmove implementation' for details)
  830. if (dest < sourceBegin)
  831. {
  832. for (int i=0;i<=lines;i++)
  833. {
  834. screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ];
  835. lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i];
  836. }
  837. }
  838. else
  839. {
  840. for (int i=lines;i>=0;i--)
  841. {
  842. screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ];
  843. lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i];
  844. }
  845. }
  846. if (lastPos != -1)
  847. {
  848. int diff = dest - sourceBegin; // Scroll by this amount
  849. lastPos += diff;
  850. if ((lastPos < 0) || (lastPos >= (lines*columns)))
  851. lastPos = -1;
  852. }
  853. // Adjust selection to follow scroll.
  854. if (sel_begin != -1)
  855. {
  856. bool beginIsTL = (sel_begin == sel_TL);
  857. int diff = dest - sourceBegin; // Scroll by this amount
  858. int scr_TL=loc(0,hist->getLines());
  859. int srca = sourceBegin+scr_TL; // Translate index from screen to global
  860. int srce = sourceEnd+scr_TL; // Translate index from screen to global
  861. int desta = srca+diff;
  862. int deste = srce+diff;
  863. if ((sel_TL >= srca) && (sel_TL <= srce))
  864. sel_TL += diff;
  865. else if ((sel_TL >= desta) && (sel_TL <= deste))
  866. sel_BR = -1; // Clear selection (see below)
  867. if ((sel_BR >= srca) && (sel_BR <= srce))
  868. sel_BR += diff;
  869. else if ((sel_BR >= desta) && (sel_BR <= deste))
  870. sel_BR = -1; // Clear selection (see below)
  871. if (sel_BR < 0)
  872. {
  873. clearSelection();
  874. }
  875. else
  876. {
  877. if (sel_TL < 0)
  878. sel_TL = 0;
  879. }
  880. if (beginIsTL)
  881. sel_begin = sel_TL;
  882. else
  883. sel_begin = sel_BR;
  884. }
  885. }
  886. void Screen::clearToEndOfScreen()
  887. {
  888. clearImage(loc(cuX,cuY),loc(columns-1,lines-1),' ');
  889. }
  890. void Screen::clearToBeginOfScreen()
  891. {
  892. clearImage(loc(0,0),loc(cuX,cuY),' ');
  893. }
  894. void Screen::clearEntireScreen()
  895. {
  896. // Add entire screen to history
  897. for (int i = 0; i < (lines-1); i++)
  898. {
  899. addHistLine(); scrollUp(0,1);
  900. }
  901. clearImage(loc(0,0),loc(columns-1,lines-1),' ');
  902. }
  903. /*! fill screen with 'E'
  904. This is to aid screen alignment
  905. */
  906. void Screen::helpAlign()
  907. {
  908. clearImage(loc(0,0),loc(columns-1,lines-1),'E');
  909. }
  910. void Screen::clearToEndOfLine()
  911. {
  912. clearImage(loc(cuX,cuY),loc(columns-1,cuY),' ');
  913. }
  914. void Screen::clearToBeginOfLine()
  915. {
  916. clearImage(loc(0,cuY),loc(cuX,cuY),' ');
  917. }
  918. void Screen::clearEntireLine()
  919. {
  920. clearImage(loc(0,cuY),loc(columns-1,cuY),' ');
  921. }
  922. void Screen::setRendition(int re)
  923. {
  924. cu_re |= re;
  925. effectiveRendition();
  926. }
  927. void Screen::resetRendition(int re)
  928. {
  929. cu_re &= ~re;
  930. effectiveRendition();
  931. }
  932. void Screen::setDefaultRendition()
  933. {
  934. setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR);
  935. setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR);
  936. cu_re = DEFAULT_RENDITION;
  937. effectiveRendition();
  938. }
  939. void Screen::setForeColor(int space, int color)
  940. {
  941. cu_fg = CharacterColor(space, color);
  942. if ( cu_fg.isValid() )
  943. effectiveRendition();
  944. else
  945. setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR);
  946. }
  947. void Screen::setBackColor(int space, int color)
  948. {
  949. cu_bg = CharacterColor(space, color);
  950. if ( cu_bg.isValid() )
  951. effectiveRendition();
  952. else
  953. setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR);
  954. }
  955. /* ------------------------------------------------------------------------- */
  956. /* */
  957. /* Marking & Selection */
  958. /* */
  959. /* ------------------------------------------------------------------------- */
  960. void Screen::clearSelection()
  961. {
  962. sel_BR = -1;
  963. sel_TL = -1;
  964. sel_begin = -1;
  965. }
  966. void Screen::getSelectionStart(int& column , int& line)
  967. {
  968. if ( sel_TL != -1 )
  969. {
  970. column = sel_TL % columns;
  971. line = sel_TL / columns;
  972. }
  973. else
  974. {
  975. column = cuX + getHistLines();
  976. line = cuY + getHistLines();
  977. }
  978. }
  979. void Screen::getSelectionEnd(int& column , int& line)
  980. {
  981. if ( sel_BR != -1 )
  982. {
  983. column = sel_BR % columns;
  984. line = sel_BR / columns;
  985. }
  986. else
  987. {
  988. column = cuX + getHistLines();
  989. line = cuY + getHistLines();
  990. }
  991. }
  992. void Screen::setSelectionStart(/*const ScreenCursor& viewCursor ,*/ const int x, const int y, const bool mode)
  993. {
  994. // kDebug(1211) << "setSelBeginXY(" << x << "," << y << ")";
  995. sel_begin = loc(x,y); //+histCursor) ;
  996. /* FIXME, HACK to correct for x too far to the right... */
  997. if (x == columns) sel_begin--;
  998. sel_BR = sel_begin;
  999. sel_TL = sel_begin;
  1000. columnmode = mode;
  1001. }
  1002. void Screen::setSelectionEnd( const int x, const int y)
  1003. {
  1004. // kDebug(1211) << "setSelExtentXY(" << x << "," << y << ")";
  1005. if (sel_begin == -1) return;
  1006. int l = loc(x,y); // + histCursor);
  1007. if (l < sel_begin)
  1008. {
  1009. sel_TL = l;
  1010. sel_BR = sel_begin;
  1011. }
  1012. else
  1013. {
  1014. /* FIXME, HACK to correct for x too far to the right... */
  1015. if (x == columns) l--;
  1016. sel_TL = sel_begin;
  1017. sel_BR = l;
  1018. }
  1019. }
  1020. bool Screen::isSelected( const int x,const int y) const
  1021. {
  1022. if (columnmode) {
  1023. int sel_Left,sel_Right;
  1024. if ( sel_TL % columns < sel_BR % columns ) {
  1025. sel_Left = sel_TL; sel_Right = sel_BR;
  1026. } else {
  1027. sel_Left = sel_BR; sel_Right = sel_TL;
  1028. }
  1029. return ( x >= sel_Left % columns ) && ( x <= sel_Right % columns ) &&
  1030. ( y >= sel_TL / columns ) && ( y <= sel_BR / columns );
  1031. //( y+histCursor >= sel_TL / columns ) && ( y+histCursor <= sel_BR / columns );
  1032. }
  1033. else {
  1034. //int pos = loc(x,y+histCursor);
  1035. int pos = loc(x,y);
  1036. return ( pos >= sel_TL && pos <= sel_BR );
  1037. }
  1038. }
  1039. QString Screen::selectedText(bool preserveLineBreaks)
  1040. {
  1041. QString result;
  1042. QTextStream stream(&result, QIODevice::ReadWrite);
  1043. PlainTextDecoder decoder;
  1044. decoder.begin(&stream);
  1045. writeSelectionToStream(&decoder , preserveLineBreaks);
  1046. decoder.end();
  1047. return result;
  1048. }
  1049. bool Screen::isSelectionValid() const
  1050. {
  1051. return ( sel_TL >= 0 && sel_BR >= 0 );
  1052. }
  1053. void Screen::writeSelectionToStream(TerminalCharacterDecoder* decoder ,
  1054. bool preserveLineBreaks)
  1055. {
  1056. // do nothing if selection is invalid
  1057. if ( !isSelectionValid() )
  1058. return;
  1059. int top = sel_TL / columns;
  1060. int left = sel_TL % columns;
  1061. int bottom = sel_BR / columns;
  1062. int right = sel_BR % columns;
  1063. Q_ASSERT( top >= 0 && left >= 0 && bottom >= 0 && right >= 0 );
  1064. //kDebug() << "sel_TL = " << sel_TL;
  1065. //kDebug() << "columns = " << columns;
  1066. for (int y=top;y<=bottom;y++)
  1067. {
  1068. int start = 0;
  1069. if ( y == top || columnmode ) start = left;
  1070. int count = -1;
  1071. if ( y == bottom || columnmode ) count = right - start + 1;
  1072. const bool appendNewLine = ( y != bottom );
  1073. copyLineToStream( y,
  1074. start,
  1075. count,
  1076. decoder,
  1077. appendNewLine,
  1078. preserveLineBreaks );
  1079. }
  1080. }
  1081. void Screen::copyLineToStream(int line ,
  1082. int start,
  1083. int count,
  1084. TerminalCharacterDecoder* decoder,
  1085. bool appendNewLine,
  1086. bool preserveLineBreaks)
  1087. {
  1088. //buffer to hold characters for decoding
  1089. //the buffer is static to avoid initialising every
  1090. //element on each call to copyLineToStream
  1091. //(which is unnecessary since all elements will be overwritten anyway)
  1092. static const int MAX_CHARS = 1024;
  1093. static Character characterBuffer[MAX_CHARS];
  1094. assert( count < MAX_CHARS );
  1095. LineProperty currentLineProperties = 0;
  1096. //determine if the line is in the history buffer or the screen image
  1097. if (line < hist->getLines())
  1098. {
  1099. const int lineLength = hist->getLineLen(line);
  1100. // ensure that start position is before end of line
  1101. start = qMin(start,qMax(0,lineLength-1));
  1102. //retrieve line from history buffer
  1103. if (count == -1)
  1104. {
  1105. count = lineLength-start;
  1106. }
  1107. else
  1108. {
  1109. count = qMin(start+count,lineLength)-start;
  1110. }
  1111. // safety checks
  1112. assert( start >= 0 );
  1113. assert( count >= 0 );
  1114. assert( (start+count) <= hist->getLineLen(line) );
  1115. hist->getCells(line,start,count,characterBuffer);
  1116. if ( hist->isWrappedLine(line) )
  1117. currentLineProperties |= LINE_WRAPPED;
  1118. }
  1119. else
  1120. {
  1121. if ( count == -1 )
  1122. count = columns - start;
  1123. assert( count >= 0 );
  1124. const int screenLine = line-hist->getLines();
  1125. Character* data = screenLines[screenLine].data();
  1126. int length = screenLines[screenLine].count();
  1127. //retrieve line from screen image
  1128. for (int i=start;i < qMin(start+count,length);i++)
  1129. {
  1130. characterBuffer[i-start] = data[i];
  1131. }
  1132. // count cannot be any greater than length
  1133. count = qBound(0,count,length-start);
  1134. Q_ASSERT( screenLine < lineProperties.count() );
  1135. currentLineProperties |= lineProperties[screenLine];
  1136. }
  1137. //do not decode trailing whitespace characters
  1138. for (int i=count-1 ; i >= 0; i--)
  1139. if (QChar(characterBuffer[i].character).isSpace())
  1140. count--;
  1141. else
  1142. break;
  1143. // add new line character at end
  1144. const bool omitLineBreak = (currentLineProperties & LINE_WRAPPED) ||
  1145. !preserveLineBreaks;
  1146. if ( !omitLineBreak && appendNewLine && (count+1 < MAX_CHARS) )
  1147. {
  1148. characterBuffer[count] = '\n';
  1149. count++;
  1150. }
  1151. //decode line and write to text stream
  1152. decoder->decodeLine( (Character*) characterBuffer ,
  1153. count, currentLineProperties );
  1154. }
  1155. // Method below has been removed because of its reliance on 'histCursor'
  1156. // and I want to restrict the methods which have knowledge of the scroll position
  1157. // to just those which deal with selection and supplying final screen images.
  1158. //
  1159. /*void Screen::writeToStream(QTextStream* stream , TerminalCharacterDecoder* decoder) {
  1160. sel_begin = 0;
  1161. sel_BR = sel_begin;
  1162. sel_TL = sel_begin;
  1163. setSelectionEnd(columns-1,lines-1+hist->getLines()-histCursor);
  1164. writeSelectionToStream(stream,decoder);
  1165. clearSelection();
  1166. }*/
  1167. void Screen::writeToStream(TerminalCharacterDecoder* decoder, int from, int to)
  1168. {
  1169. sel_begin = loc(0,from);
  1170. sel_TL = sel_begin;
  1171. sel_BR = loc(columns-1,to);
  1172. writeSelectionToStream(decoder);
  1173. clearSelection();
  1174. }
  1175. QString Screen::getHistoryLine(int no)
  1176. {
  1177. sel_begin = loc(0,no);
  1178. sel_TL = sel_begin;
  1179. sel_BR = loc(columns-1,no);
  1180. return selectedText(false);
  1181. }
  1182. void Screen::addHistLine()
  1183. {
  1184. // add line to history buffer
  1185. // we have to take care about scrolling, too...
  1186. if (hasScroll())
  1187. {
  1188. int oldHistLines = hist->getLines();
  1189. hist->addCellsVector(screenLines[0]);
  1190. hist->addLine( lineProperties[0] & LINE_WRAPPED );
  1191. int newHistLines = hist->getLines();
  1192. bool beginIsTL = (sel_begin == sel_TL);
  1193. // If the history is full, increment the count
  1194. // of dropped lines
  1195. if ( newHistLines == oldHistLines )
  1196. _droppedLines++;
  1197. // Adjust selection for the new point of reference
  1198. if (newHistLines > oldHistLines)
  1199. {
  1200. if (sel_begin != -1)
  1201. {
  1202. sel_TL += columns;
  1203. sel_BR += columns;
  1204. }
  1205. }
  1206. if (sel_begin != -1)
  1207. {
  1208. // Scroll selection in history up
  1209. int top_BR = loc(0, 1+newHistLines);
  1210. if (sel_TL < top_BR)
  1211. sel_TL -= columns;
  1212. if (sel_BR < top_BR)
  1213. sel_BR -= columns;
  1214. if (sel_BR < 0)
  1215. {
  1216. clearSelection();
  1217. }
  1218. else
  1219. {
  1220. if (sel_TL < 0)
  1221. sel_TL = 0;
  1222. }
  1223. if (beginIsTL)
  1224. sel_begin = sel_TL;
  1225. else
  1226. sel_begin = sel_BR;
  1227. }
  1228. }
  1229. }
  1230. int Screen::getHistLines()
  1231. {
  1232. return hist->getLines();
  1233. }
  1234. void Screen::setScroll(const HistoryType& t , bool copyPreviousScroll)
  1235. {
  1236. clearSelection();
  1237. if ( copyPreviousScroll )
  1238. hist = t.scroll(hist);
  1239. else
  1240. {
  1241. HistoryScroll* oldScroll = hist;
  1242. hist = t.scroll(0);
  1243. delete oldScroll;
  1244. }
  1245. }
  1246. bool Screen::hasScroll()
  1247. {
  1248. return hist->hasScroll();
  1249. }
  1250. const HistoryType& Screen::getScroll()
  1251. {
  1252. return hist->getType();
  1253. }
  1254. void Screen::setLineProperty(LineProperty property , bool enable)
  1255. {
  1256. if ( enable )
  1257. {
  1258. lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | property);
  1259. }
  1260. else
  1261. {
  1262. lineProperties[cuY] = (LineProperty)(lineProperties[cuY] & ~property);
  1263. }
  1264. }
  1265. void Screen::fillWithDefaultChar(Character* dest, int count)
  1266. {
  1267. for (int i=0;i<count;i++)
  1268. dest[i] = defaultChar;
  1269. }