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

/quassel-0.7.3/src/uisupport/multilineedit.cpp

#
C++ | 721 lines | 602 code | 88 blank | 31 comment | 130 complexity | 326ae5bb506fbf7acf813eed4cd0850b MD5 | raw file
Possible License(s): GPL-2.0, GPL-3.0
  1. /***************************************************************************
  2. * Copyright (C) 2005-2010 by the Quassel Project *
  3. * devel@quassel-irc.org *
  4. * *
  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) version 3. *
  9. * *
  10. * This program is distributed in the hope that it will be useful, *
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  13. * GNU General Public License for more details. *
  14. * *
  15. * You should have received a copy of the GNU General Public License *
  16. * along with this program; if not, write to the *
  17. * Free Software Foundation, Inc., *
  18. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
  19. ***************************************************************************/
  20. #include <QApplication>
  21. #include <QMenu>
  22. #include <QMessageBox>
  23. #include <QScrollBar>
  24. #include "actioncollection.h"
  25. #include "bufferview.h"
  26. #include "graphicalui.h"
  27. #include "multilineedit.h"
  28. #include "tabcompleter.h"
  29. const int leftMargin = 3;
  30. MultiLineEdit::MultiLineEdit(QWidget *parent)
  31. : MultiLineEditParent(parent),
  32. _idx(0),
  33. _mode(SingleLine),
  34. _singleLine(true),
  35. _minHeight(1),
  36. _maxHeight(5),
  37. _scrollBarsEnabled(true),
  38. _pasteProtectionEnabled(true),
  39. _emacsMode(false),
  40. _lastDocumentHeight(-1)
  41. {
  42. #if QT_VERSION >= 0x040500
  43. document()->setDocumentMargin(0); // new in Qt 4.5 and we really don't want it here
  44. #endif
  45. setAcceptRichText(false);
  46. #ifdef HAVE_KDE
  47. enableFindReplace(false);
  48. #endif
  49. setMode(SingleLine);
  50. setWordWrapEnabled(false);
  51. reset();
  52. connect(this, SIGNAL(textChanged()), this, SLOT(on_textChanged()));
  53. _mircColorMap["00"] = "#ffffff";
  54. _mircColorMap["01"] = "#000000";
  55. _mircColorMap["02"] = "#000080";
  56. _mircColorMap["03"] = "#008000";
  57. _mircColorMap["04"] = "#ff0000";
  58. _mircColorMap["05"] = "#800000";
  59. _mircColorMap["06"] = "#800080";
  60. _mircColorMap["07"] = "#ffa500";
  61. _mircColorMap["08"] = "#ffff00";
  62. _mircColorMap["09"] = "#00ff00";
  63. _mircColorMap["10"] = "#008080";
  64. _mircColorMap["11"] = "#00ffff";
  65. _mircColorMap["12"] = "#4169e1";
  66. _mircColorMap["13"] = "#ff00ff";
  67. _mircColorMap["14"] = "#808080";
  68. _mircColorMap["15"] = "#c0c0c0";
  69. }
  70. MultiLineEdit::~MultiLineEdit() {
  71. }
  72. void MultiLineEdit::setCustomFont(const QFont &font) {
  73. setFont(font);
  74. updateSizeHint();
  75. }
  76. void MultiLineEdit::setMode(Mode mode) {
  77. if(mode == _mode)
  78. return;
  79. _mode = mode;
  80. }
  81. void MultiLineEdit::setMinHeight(int lines) {
  82. if(lines == _minHeight)
  83. return;
  84. _minHeight = lines;
  85. updateSizeHint();
  86. }
  87. void MultiLineEdit::setMaxHeight(int lines) {
  88. if(lines == _maxHeight)
  89. return;
  90. _maxHeight = lines;
  91. updateSizeHint();
  92. }
  93. void MultiLineEdit::setScrollBarsEnabled(bool enable) {
  94. if(_scrollBarsEnabled == enable)
  95. return;
  96. _scrollBarsEnabled = enable;
  97. updateScrollBars();
  98. }
  99. void MultiLineEdit::updateScrollBars() {
  100. QFontMetrics fm(font());
  101. int _maxPixelHeight = fm.lineSpacing() * _maxHeight;
  102. if(_scrollBarsEnabled && document()->size().height() > _maxPixelHeight)
  103. setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
  104. else
  105. setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  106. if(!_scrollBarsEnabled || isSingleLine())
  107. setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  108. else
  109. setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
  110. }
  111. void MultiLineEdit::resizeEvent(QResizeEvent *event) {
  112. QTextEdit::resizeEvent(event);
  113. updateSizeHint();
  114. updateScrollBars();
  115. }
  116. void MultiLineEdit::updateSizeHint() {
  117. QFontMetrics fm(font());
  118. int minPixelHeight = fm.lineSpacing() * _minHeight;
  119. int maxPixelHeight = fm.lineSpacing() * _maxHeight;
  120. int scrollBarHeight = horizontalScrollBar()->isVisible() ? horizontalScrollBar()->height() : 0;
  121. // use the style to determine a decent size
  122. int h = qMin(qMax((int)document()->size().height() + scrollBarHeight, minPixelHeight), maxPixelHeight) + 2 * frameWidth();
  123. QStyleOptionFrameV2 opt;
  124. opt.initFrom(this);
  125. opt.rect = QRect(0, 0, 100, h);
  126. opt.lineWidth = lineWidth();
  127. opt.midLineWidth = midLineWidth();
  128. opt.state |= QStyle::State_Sunken;
  129. QSize s = style()->sizeFromContents(QStyle::CT_LineEdit, &opt, QSize(100, h).expandedTo(QApplication::globalStrut()), this);
  130. if(s != _sizeHint) {
  131. _sizeHint = s;
  132. updateGeometry();
  133. }
  134. }
  135. QSize MultiLineEdit::sizeHint() const {
  136. if(!_sizeHint.isValid()) {
  137. MultiLineEdit *that = const_cast<MultiLineEdit *>(this);
  138. that->updateSizeHint();
  139. }
  140. return _sizeHint;
  141. }
  142. QSize MultiLineEdit::minimumSizeHint() const {
  143. return sizeHint();
  144. }
  145. void MultiLineEdit::setEmacsMode(bool enable) {
  146. _emacsMode = enable;
  147. }
  148. void MultiLineEdit::setSpellCheckEnabled(bool enable) {
  149. #ifdef HAVE_KDE
  150. setCheckSpellingEnabled(enable);
  151. #else
  152. Q_UNUSED(enable)
  153. #endif
  154. }
  155. void MultiLineEdit::setWordWrapEnabled(bool enable) {
  156. setLineWrapMode(enable? WidgetWidth : NoWrap);
  157. updateSizeHint();
  158. }
  159. void MultiLineEdit::setPasteProtectionEnabled(bool enable, QWidget *) {
  160. _pasteProtectionEnabled = enable;
  161. }
  162. void MultiLineEdit::historyMoveBack() {
  163. addToHistory(convertRichtextToMircCodes(), true);
  164. if(_idx > 0) {
  165. _idx--;
  166. showHistoryEntry();
  167. }
  168. }
  169. void MultiLineEdit::historyMoveForward() {
  170. addToHistory(convertRichtextToMircCodes(), true);
  171. if(_idx < _history.count()) {
  172. _idx++;
  173. if(_idx < _history.count() || _tempHistory.contains(_idx)) // tempHistory might have an entry for idx == history.count() + 1
  174. showHistoryEntry();
  175. else
  176. reset(); // equals clear() in this case
  177. } else {
  178. addToHistory(convertRichtextToMircCodes());
  179. reset();
  180. }
  181. }
  182. bool MultiLineEdit::addToHistory(const QString &text, bool temporary) {
  183. if(text.isEmpty())
  184. return false;
  185. Q_ASSERT(0 <= _idx && _idx <= _history.count());
  186. if(temporary) {
  187. // if an entry of the history is changed, we remember it and show it again at this
  188. // position until a line was actually sent
  189. // sent lines get appended to the history
  190. if(_history.isEmpty() || text != _history[_idx - (int)(_idx == _history.count())]) {
  191. _tempHistory[_idx] = text;
  192. return true;
  193. }
  194. } else {
  195. if(_history.isEmpty() || text != _history.last()) {
  196. _history << text;
  197. _tempHistory.clear();
  198. return true;
  199. }
  200. }
  201. return false;
  202. }
  203. bool MultiLineEdit::event(QEvent *e) {
  204. // We need to make sure that global shortcuts aren't eaten
  205. if(e->type() == QEvent::ShortcutOverride) {
  206. QKeyEvent* event = static_cast<QKeyEvent *>(e);
  207. QKeySequence key = QKeySequence(event->key() | event->modifiers());
  208. foreach(QAction *action, GraphicalUi::actionCollection()->actions()) {
  209. if(action->shortcuts().contains(key)) {
  210. e->ignore();
  211. return false;
  212. }
  213. }
  214. }
  215. return MultiLineEditParent::event(e);
  216. }
  217. void MultiLineEdit::keyPressEvent(QKeyEvent *event) {
  218. // Workaround the fact that Qt < 4.5 doesn't know InsertLineSeparator yet
  219. #if QT_VERSION >= 0x040500
  220. if(event == QKeySequence::InsertLineSeparator) {
  221. #else
  222. # ifdef Q_WS_MAC
  223. if((event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) && event->modifiers() & Qt::META) {
  224. # else
  225. if((event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) && event->modifiers() & Qt::SHIFT) {
  226. # endif
  227. #endif
  228. if(_mode == SingleLine) {
  229. event->accept();
  230. on_returnPressed();
  231. return;
  232. }
  233. MultiLineEditParent::keyPressEvent(event);
  234. return;
  235. }
  236. switch(event->key()) {
  237. case Qt::Key_Up:
  238. if(event->modifiers() & Qt::ShiftModifier)
  239. break;
  240. {
  241. event->accept();
  242. if(!(event->modifiers() & Qt::ControlModifier)) {
  243. int pos = textCursor().position();
  244. moveCursor(QTextCursor::Up);
  245. if(pos == textCursor().position()) // already on top line -> history
  246. historyMoveBack();
  247. } else
  248. historyMoveBack();
  249. return;
  250. }
  251. case Qt::Key_Down:
  252. if(event->modifiers() & Qt::ShiftModifier)
  253. break;
  254. {
  255. event->accept();
  256. if(!(event->modifiers() & Qt::ControlModifier)) {
  257. int pos = textCursor().position();
  258. moveCursor(QTextCursor::Down);
  259. if(pos == textCursor().position()) // already on bottom line -> history
  260. historyMoveForward();
  261. } else
  262. historyMoveForward();
  263. return;
  264. }
  265. case Qt::Key_Return:
  266. case Qt::Key_Enter:
  267. case Qt::Key_Select:
  268. event->accept();
  269. on_returnPressed();
  270. return;
  271. // We don't want to have the tab key react even if no completer is installed
  272. case Qt::Key_Tab:
  273. event->accept();
  274. return;
  275. default:
  276. ;
  277. }
  278. if(_emacsMode) {
  279. if(event->modifiers() & Qt::ControlModifier) {
  280. switch(event->key()) {
  281. // move
  282. case Qt::Key_A:
  283. moveCursor(QTextCursor::StartOfLine);
  284. return;
  285. case Qt::Key_E:
  286. moveCursor(QTextCursor::EndOfLine);
  287. return;
  288. case Qt::Key_F:
  289. moveCursor(QTextCursor::Right);
  290. return;
  291. case Qt::Key_B:
  292. moveCursor(QTextCursor::Left);
  293. return;
  294. // modify
  295. case Qt::Key_Y:
  296. paste();
  297. return;
  298. case Qt::Key_K:
  299. moveCursor(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
  300. cut();
  301. return;
  302. default:
  303. break;
  304. }
  305. }
  306. else if(event->modifiers() & Qt::MetaModifier ||
  307. event->modifiers() & Qt::AltModifier)
  308. {
  309. switch(event->key()) {
  310. case Qt::Key_Right:
  311. moveCursor(QTextCursor::WordRight);
  312. return;
  313. case Qt::Key_Left:
  314. moveCursor(QTextCursor::WordLeft);
  315. return;
  316. case Qt::Key_F:
  317. moveCursor(QTextCursor::WordRight);
  318. return;
  319. case Qt::Key_B:
  320. moveCursor(QTextCursor::WordLeft);
  321. return;
  322. case Qt::Key_Less:
  323. moveCursor(QTextCursor::Start);
  324. return;
  325. case Qt::Key_Greater:
  326. moveCursor(QTextCursor::End);
  327. return;
  328. // modify
  329. case Qt::Key_D:
  330. moveCursor(QTextCursor::WordRight, QTextCursor::KeepAnchor);
  331. cut();
  332. return;
  333. case Qt::Key_U: // uppercase word
  334. moveCursor(QTextCursor::WordRight, QTextCursor::KeepAnchor);
  335. textCursor().insertText(textCursor().selectedText().toUpper());
  336. return;
  337. case Qt::Key_L: // lowercase word
  338. moveCursor(QTextCursor::WordRight, QTextCursor::KeepAnchor);
  339. textCursor().insertText(textCursor().selectedText().toLower());
  340. return;
  341. case Qt::Key_C: { // capitalize word
  342. moveCursor(QTextCursor::WordRight, QTextCursor::KeepAnchor);
  343. QString const text = textCursor().selectedText();
  344. textCursor().insertText(text.left(1).toUpper() + text.mid(1).toLower());
  345. return;
  346. }
  347. case Qt::Key_T: { // transpose words
  348. moveCursor(QTextCursor::StartOfWord);
  349. moveCursor(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
  350. QString const word1 = textCursor().selectedText();
  351. textCursor().clearSelection();
  352. moveCursor(QTextCursor::WordRight);
  353. moveCursor(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
  354. QString const word2 = textCursor().selectedText();
  355. if(!word2.isEmpty() && !word1.isEmpty()) {
  356. textCursor().insertText(word1);
  357. moveCursor(QTextCursor::WordLeft);
  358. moveCursor(QTextCursor::WordLeft);
  359. moveCursor(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
  360. textCursor().insertText(word2);
  361. moveCursor(QTextCursor::WordRight);
  362. moveCursor(QTextCursor::EndOfWord);
  363. }
  364. return;
  365. }
  366. default:
  367. break;
  368. }
  369. }
  370. }
  371. #ifdef HAVE_KDE
  372. KTextEdit::keyPressEvent(event);
  373. #else
  374. QTextEdit::keyPressEvent(event);
  375. #endif
  376. }
  377. QString MultiLineEdit::convertRichtextToMircCodes() {
  378. bool underline, bold, italic, color;
  379. QString mircText, mircFgColor, mircBgColor;
  380. QTextCursor cursor = textCursor();
  381. QTextCursor peekcursor = textCursor();
  382. cursor.movePosition(QTextCursor::Start);
  383. underline = bold = italic = color = false;
  384. while (cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor)) {
  385. if (cursor.selectedText() == QString(QChar(QChar::LineSeparator))
  386. || cursor.selectedText() == QString(QChar(QChar::ParagraphSeparator))) {
  387. if (color) {
  388. color = false;
  389. mircText.append('\x03');
  390. }
  391. if (underline) {
  392. underline = false;
  393. mircText.append('\x1f');
  394. }
  395. if (italic) {
  396. italic = false;
  397. mircText.append('\x1d');
  398. }
  399. if (bold) {
  400. bold = false;
  401. mircText.append('\x02');
  402. }
  403. mircText.append('\n');
  404. }
  405. else {
  406. if (!bold && cursor.charFormat().font().bold()) {
  407. bold = true;
  408. mircText.append('\x02');
  409. }
  410. if (!italic && cursor.charFormat().fontItalic()) {
  411. italic = true;
  412. mircText.append('\x1d');
  413. }
  414. if (!underline && cursor.charFormat().fontUnderline()) {
  415. underline = true;
  416. mircText.append('\x1f');
  417. }
  418. if (!color && (cursor.charFormat().foreground().isOpaque() || cursor.charFormat().background().isOpaque())) {
  419. color = true;
  420. mircText.append('\x03');
  421. mircFgColor = _mircColorMap.key(cursor.charFormat().foreground().color().name());
  422. mircBgColor = _mircColorMap.key(cursor.charFormat().background().color().name());
  423. if (mircFgColor.isEmpty()) {
  424. mircFgColor = "01"; //use black if the current foreground color can't be converted
  425. }
  426. mircText.append(mircFgColor);
  427. if (cursor.charFormat().background().isOpaque())
  428. mircText.append("," + mircBgColor);
  429. }
  430. mircText.append(cursor.selectedText());
  431. peekcursor.setPosition(cursor.position());
  432. peekcursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
  433. if (mircCodesChanged(cursor, peekcursor)) {
  434. if (color) {
  435. color = false;
  436. mircText.append('\x03');
  437. }
  438. if (underline) {
  439. underline = false;
  440. mircText.append('\x1f');
  441. }
  442. if (italic) {
  443. italic = false;
  444. mircText.append('\x1d');
  445. }
  446. if (bold) {
  447. bold = false;
  448. mircText.append('\x02');
  449. }
  450. }
  451. }
  452. cursor.clearSelection();
  453. }
  454. if (color) {
  455. color = false;
  456. mircText.append('\x03');
  457. }
  458. if (underline) {
  459. underline = false;
  460. mircText.append('\x1f');
  461. }
  462. if (italic) {
  463. italic = false;
  464. mircText.append('\x1d');
  465. }
  466. if (bold) {
  467. bold = false;
  468. mircText.append('\x02');
  469. }
  470. return mircText;
  471. }
  472. bool MultiLineEdit::mircCodesChanged(QTextCursor &cursor, QTextCursor &peekcursor) {
  473. bool changed = false;
  474. if (cursor.charFormat().font().bold() != peekcursor.charFormat().font().bold())
  475. changed = true;
  476. if (cursor.charFormat().fontItalic() != peekcursor.charFormat().fontItalic())
  477. changed = true;
  478. if (cursor.charFormat().fontUnderline() != peekcursor.charFormat().fontUnderline())
  479. changed = true;
  480. if (cursor.charFormat().foreground().color() != peekcursor.charFormat().foreground().color())
  481. changed = true;
  482. if (cursor.charFormat().background().color() != peekcursor.charFormat().background().color())
  483. changed = true;
  484. return changed;
  485. }
  486. QString MultiLineEdit::convertMircCodesToHtml(const QString &text) {
  487. QStringList words;
  488. QRegExp mircCode = QRegExp("(|||)", Qt::CaseSensitive);
  489. int posLeft = 0;
  490. int posRight = 0;
  491. for(;;) {
  492. posRight = mircCode.indexIn(text, posLeft);
  493. if(posRight < 0) {
  494. words << text.mid(posLeft);
  495. break; // no more mirc color codes
  496. }
  497. if (posLeft < posRight) {
  498. words << text.mid(posLeft, posRight - posLeft);
  499. posLeft = posRight;
  500. }
  501. posRight = text.indexOf(mircCode.cap(), posRight + 1);
  502. words << text.mid(posLeft, posRight + 1 - posLeft);
  503. posLeft = posRight + 1;
  504. }
  505. for (int i = 0; i < words.count(); i++) {
  506. QString style;
  507. if (words[i].contains('\x02')) {
  508. style.append(" font-weight:600;");
  509. words[i].replace('\x02',"");
  510. }
  511. if (words[i].contains('\x1d')) {
  512. style.append(" font-style:italic;");
  513. words[i].replace('\x1d',"");
  514. }
  515. if (words[i].contains('\x1f')) {
  516. style.append(" text-decoration: underline;");
  517. words[i].replace('\x1f',"");
  518. }
  519. if (words[i].contains('\x03')) {
  520. int pos = words[i].indexOf('\x03');
  521. int len = 3;
  522. QString fg = words[i].mid(pos + 1,2);
  523. QString bg;
  524. if (words[i][pos+3] == ',')
  525. bg = words[i].mid(pos+4,2);
  526. style.append(" color:");
  527. style.append(_mircColorMap[fg]);
  528. style.append(";");
  529. if (!bg.isEmpty()) {
  530. style.append(" background-color:");
  531. style.append(_mircColorMap[bg]);
  532. style.append(";");
  533. len = 6;
  534. }
  535. words[i].replace(pos, len, "");
  536. words[i].replace('\x03',"");
  537. }
  538. words[i].replace("&","&amp;");
  539. words[i].replace("<", "&lt;");
  540. words[i].replace(">", "&gt;");
  541. words[i].replace("\"", "&quot;");
  542. if (style.isEmpty()) {
  543. words[i] = "<span>" + words[i] + "</span>";
  544. }
  545. else {
  546. words[i] = "<span style=\"" + style + "\">" + words[i] + "</span>";
  547. }
  548. }
  549. return words.join("").replace("\n","<br />");
  550. }
  551. void MultiLineEdit::on_returnPressed() {
  552. on_returnPressed(convertRichtextToMircCodes());
  553. }
  554. void MultiLineEdit::on_returnPressed(const QString & text) {
  555. if(!text.isEmpty()) {
  556. foreach(const QString &line, text.split('\n', QString::SkipEmptyParts)) {
  557. if(line.isEmpty())
  558. continue;
  559. addToHistory(line);
  560. emit textEntered(line);
  561. }
  562. reset();
  563. _tempHistory.clear();
  564. } else {
  565. emit noTextEntered();
  566. }
  567. }
  568. void MultiLineEdit::on_textChanged() {
  569. QString newText = text();
  570. newText.replace("\r\n", "\n");
  571. newText.replace('\r', '\n');
  572. if(_mode == SingleLine) {
  573. if(!pasteProtectionEnabled())
  574. newText.replace('\n', ' ');
  575. else if(newText.contains('\n')) {
  576. QStringList lines = newText.split('\n', QString::SkipEmptyParts);
  577. clear();
  578. if(lines.count() >= 4) {
  579. QString msg = tr("Do you really want to paste %n lines?", "", lines.count());
  580. msg += "<p>";
  581. for(int i = 0; i < 4; i++) {
  582. msg += Qt::escape(lines[i].left(40));
  583. if(lines[i].count() > 40)
  584. msg += "...";
  585. msg += "<br />";
  586. }
  587. msg += "...</p>";
  588. QMessageBox question(QMessageBox::NoIcon, tr("Paste Protection"), msg, QMessageBox::Yes|QMessageBox::No);
  589. question.setDefaultButton(QMessageBox::No);
  590. #ifdef Q_WS_MAC
  591. question.setWindowFlags(question.windowFlags() | Qt::Sheet);
  592. #endif
  593. if(question.exec() != QMessageBox::Yes)
  594. return;
  595. }
  596. foreach(QString line, lines) {
  597. clear();
  598. insert(line);
  599. on_returnPressed();
  600. }
  601. }
  602. }
  603. _singleLine = (newText.indexOf('\n') < 0);
  604. if(document()->size().height() != _lastDocumentHeight) {
  605. _lastDocumentHeight = document()->size().height();
  606. on_documentHeightChanged(_lastDocumentHeight);
  607. }
  608. updateSizeHint();
  609. ensureCursorVisible();
  610. }
  611. void MultiLineEdit::on_documentHeightChanged(qreal) {
  612. updateScrollBars();
  613. }
  614. void MultiLineEdit::reset() {
  615. // every time the MultiLineEdit is cleared we also reset history index
  616. _idx = _history.count();
  617. clear();
  618. QTextBlockFormat format = textCursor().blockFormat();
  619. format.setLeftMargin(leftMargin); // we want a little space between the frame and the contents
  620. textCursor().setBlockFormat(format);
  621. updateScrollBars();
  622. }
  623. void MultiLineEdit::showHistoryEntry() {
  624. // if the user changed the history, display the changed line
  625. setHtml(convertMircCodesToHtml(_tempHistory.contains(_idx) ? _tempHistory[_idx] : _history[_idx]));
  626. QTextCursor cursor = textCursor();
  627. QTextBlockFormat format = cursor.blockFormat();
  628. format.setLeftMargin(leftMargin); // we want a little space between the frame and the contents
  629. cursor.setBlockFormat(format);
  630. cursor.movePosition(QTextCursor::End);
  631. setTextCursor(cursor);
  632. updateScrollBars();
  633. }