multilineedit: handle unterminated mIRC codes
[quassel.git] / src / uisupport / multilineedit.cpp
index 876f0b9..a9a2ae2 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2018 by the Quassel Project                        *
+ *   Copyright (C) 2005-2019 by the Quassel Project                        *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -18,6 +18,8 @@
  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
  ***************************************************************************/
 
+#include "multilineedit.h"
+
 #include <QApplication>
 #include <QMessageBox>
 #include <QScrollBar>
 #include "actioncollection.h"
 #include "bufferview.h"
 #include "graphicalui.h"
-#include "multilineedit.h"
 #include "tabcompleter.h"
 
 const int leftMargin = 3;
 
-MultiLineEdit::MultiLineEdit(QWidget *parent)
+MultiLineEdit::MultiLineEdit(QWidgetparent)
     : MultiLineEditParent(parent)
 {
     document()->setDocumentMargin(0);
@@ -52,7 +53,7 @@ MultiLineEdit::MultiLineEdit(QWidget *parent)
     // Prevent QTextHtmlImporter::appendNodeText from eating whitespace
     document()->setDefaultStyleSheet("span { white-space: pre-wrap; }");
 
-    connect(this, SIGNAL(textChanged()), this, SLOT(on_textChanged()));
+    connect(this, &QTextEdit::textChanged, this, &MultiLineEdit::on_textChanged);
 
     _mircColorMap["00"] = "#ffffff";
     _mircColorMap["01"] = "#000000";
@@ -72,18 +73,12 @@ MultiLineEdit::MultiLineEdit(QWidget *parent)
     _mircColorMap["15"] = "#c0c0c0";
 }
 
-
-MultiLineEdit::~MultiLineEdit()
-{
-}
-
 #if defined HAVE_SONNET && !defined HAVE_KDE
-Sonnet::Highlighter *MultiLineEdit::highlighter() const
+Sonnet::HighlighterMultiLineEdit::highlighter() const
 {
     return _spellCheckDecorator->highlighter();
 }
 
-
 void MultiLineEdit::setSpellCheckEnabled(bool enabled)
 {
     highlighter()->setActive(enabled);
@@ -92,15 +87,15 @@ void MultiLineEdit::setSpellCheckEnabled(bool enabled)
     }
 }
 
-void MultiLineEdit::contextMenuEvent(QContextMenuEvent *event)
+void MultiLineEdit::contextMenuEvent(QContextMenuEventevent)
 {
-    QMenu *menu = createStandardContextMenu();
+    QMenumenu = createStandardContextMenu();
     menu->addSeparator();
 
     auto action = menu->addAction(tr("Auto Spell Check"));
     action->setCheckable(true);
     action->setChecked(highlighter()->isActive());
-    connect(action, SIGNAL(toggled(bool)), this, SLOT(setSpellCheckEnabled(bool)));
+    connect(action, &QAction::toggled, this, &MultiLineEdit::setSpellCheckEnabled);
 
     menu->exec(event->globalPos());
     delete menu;
@@ -108,14 +103,12 @@ void MultiLineEdit::contextMenuEvent(QContextMenuEvent *event)
 
 #endif
 
-
-void MultiLineEdit::setCustomFont(const QFont &font)
+void MultiLineEdit::setCustomFont(const QFont& font)
 {
     setFont(font);
     updateSizeHint();
 }
 
-
 void MultiLineEdit::setMode(Mode mode)
 {
     if (mode == _mode)
@@ -124,14 +117,12 @@ void MultiLineEdit::setMode(Mode mode)
     _mode = mode;
 }
 
-
 void MultiLineEdit::setLineWrapEnabled(bool enable)
 {
     setLineWrapMode(enable ? WidgetWidth : NoWrap);
     updateSizeHint();
 }
 
-
 void MultiLineEdit::setMinHeight(int lines)
 {
     if (lines == _minHeight)
@@ -141,7 +132,6 @@ void MultiLineEdit::setMinHeight(int lines)
     updateSizeHint();
 }
 
-
 void MultiLineEdit::setMaxHeight(int lines)
 {
     if (lines == _maxHeight)
@@ -151,7 +141,6 @@ void MultiLineEdit::setMaxHeight(int lines)
     updateSizeHint();
 }
 
-
 void MultiLineEdit::setScrollBarsEnabled(bool enable)
 {
     if (_scrollBarsEnabled == enable)
@@ -161,7 +150,6 @@ void MultiLineEdit::setScrollBarsEnabled(bool enable)
     updateScrollBars();
 }
 
-
 void MultiLineEdit::updateScrollBars()
 {
     QFontMetrics fm(font());
@@ -177,15 +165,13 @@ void MultiLineEdit::updateScrollBars()
         setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
 }
 
-
-void MultiLineEdit::resizeEvent(QResizeEvent *event)
+void MultiLineEdit::resizeEvent(QResizeEvent* event)
 {
     QTextEdit::resizeEvent(event);
     updateSizeHint();
     updateScrollBars();
 }
 
-
 void MultiLineEdit::updateSizeHint()
 {
     QFontMetrics fm(font());
@@ -202,7 +188,7 @@ void MultiLineEdit::updateSizeHint()
     opt.lineWidth = lineWidth();
     opt.midLineWidth = midLineWidth();
     opt.state |= QStyle::State_Sunken;
-    QWidget *widget = this;
+    QWidgetwidget = this;
 #ifdef Q_OS_MAC
     widget = 0;
 #endif
@@ -213,35 +199,30 @@ void MultiLineEdit::updateSizeHint()
     }
 }
 
-
 QSize MultiLineEdit::sizeHint() const
 {
     if (!_sizeHint.isValid()) {
-        MultiLineEdit *that = const_cast<MultiLineEdit *>(this);
+        auto* that = const_cast<MultiLineEdit*>(this);
         that->updateSizeHint();
     }
     return _sizeHint;
 }
 
-
 QSize MultiLineEdit::minimumSizeHint() const
 {
     return sizeHint();
 }
 
-
 void MultiLineEdit::setEmacsMode(bool enable)
 {
     _emacsMode = enable;
 }
 
-
-void MultiLineEdit::setPasteProtectionEnabled(bool enable, QWidget *)
+void MultiLineEdit::setPasteProtectionEnabled(bool enable, QWidget*)
 {
     _pasteProtectionEnabled = enable;
 }
 
-
 void MultiLineEdit::historyMoveBack()
 {
     addToHistory(convertRichtextToMircCodes(), true);
@@ -252,17 +233,16 @@ void MultiLineEdit::historyMoveBack()
     }
 }
 
-
 void MultiLineEdit::historyMoveForward()
 {
     addToHistory(convertRichtextToMircCodes(), true);
 
     if (_idx < _history.count()) {
         _idx++;
-        if (_idx < _history.count() || _tempHistory.contains(_idx)) // tempHistory might have an entry for idx == history.count() + 1
+        if (_idx < _history.count() || _tempHistory.contains(_idx))  // tempHistory might have an entry for idx == history.count() + 1
             showHistoryEntry();
         else
-            reset();        // equals clear() in this case
+            reset();  // equals clear() in this case
     }
     else {
         addToHistory(convertRichtextToMircCodes());
@@ -270,8 +250,7 @@ void MultiLineEdit::historyMoveForward()
     }
 }
 
-
-bool MultiLineEdit::addToHistory(const QString &text, bool temporary)
+bool MultiLineEdit::addToHistory(const QString& text, bool temporary)
 {
     if (text.isEmpty())
         return false;
@@ -297,14 +276,13 @@ bool MultiLineEdit::addToHistory(const QString &text, bool temporary)
     return false;
 }
 
-
-bool MultiLineEdit::event(QEvent *e)
+bool MultiLineEdit::event(QEvent* e)
 {
     // We need to make sure that global shortcuts aren't eaten
     if (e->type() == QEvent::ShortcutOverride) {
-        QKeyEvent *event = static_cast<QKeyEvent *>(e);
+        auto* event = static_cast<QKeyEvent*>(e);
         QKeySequence key = QKeySequence(event->key() | event->modifiers());
-        foreach(QAction *action, GraphicalUi::actionCollection()->actions()) {
+        foreach (QAction* action, GraphicalUi::actionCollection()->actions()) {
             if (action->shortcuts().contains(key)) {
                 e->ignore();
                 return false;
@@ -315,8 +293,7 @@ bool MultiLineEdit::event(QEvent *e)
     return MultiLineEditParent::event(e);
 }
 
-
-void MultiLineEdit::keyPressEvent(QKeyEvent *event)
+void MultiLineEdit::keyPressEvent(QKeyEvent* event)
 {
     if (event == QKeySequence::InsertLineSeparator) {
         if (_mode == SingleLine) {
@@ -337,7 +314,7 @@ void MultiLineEdit::keyPressEvent(QKeyEvent *event)
             if (!(event->modifiers() & Qt::ControlModifier)) {
                 int pos = textCursor().position();
                 moveCursor(QTextCursor::Up);
-                if (pos == textCursor().position()) // already on top line -> history
+                if (pos == textCursor().position())  // already on top line -> history
                     historyMoveBack();
             }
             else
@@ -353,7 +330,7 @@ void MultiLineEdit::keyPressEvent(QKeyEvent *event)
             if (!(event->modifiers() & Qt::ControlModifier)) {
                 int pos = textCursor().position();
                 moveCursor(QTextCursor::Down);
-                if (pos == textCursor().position()) // already on bottom line -> history
+                if (pos == textCursor().position())  // already on bottom line -> history
                     historyMoveForward();
             }
             else
@@ -407,9 +384,7 @@ void MultiLineEdit::keyPressEvent(QKeyEvent *event)
                 break;
             }
         }
-        else if (event->modifiers() & Qt::MetaModifier ||
-                 event->modifiers() & Qt::AltModifier)
-        {
+        else if (event->modifiers() & Qt::MetaModifier || event->modifiers() & Qt::AltModifier) {
             switch (event->key()) {
             case Qt::Key_Right:
                 moveCursor(QTextCursor::WordRight);
@@ -436,26 +411,24 @@ void MultiLineEdit::keyPressEvent(QKeyEvent *event)
                 cut();
                 return;
 
-            case Qt::Key_U: // uppercase word
+            case Qt::Key_U:  // uppercase word
                 moveCursor(QTextCursor::WordRight, QTextCursor::KeepAnchor);
                 textCursor().insertText(textCursor().selectedText().toUpper());
                 return;
 
-            case Qt::Key_L: // lowercase word
+            case Qt::Key_L:  // lowercase word
                 moveCursor(QTextCursor::WordRight, QTextCursor::KeepAnchor);
                 textCursor().insertText(textCursor().selectedText().toLower());
                 return;
 
-            case Qt::Key_C:
-            {           // capitalize word
+            case Qt::Key_C: {  // capitalize word
                 moveCursor(QTextCursor::WordRight, QTextCursor::KeepAnchor);
                 QString const text = textCursor().selectedText();
                 textCursor().insertText(text.left(1).toUpper() + text.mid(1).toLower());
                 return;
             }
 
-            case Qt::Key_T:
-            {           // transpose words
+            case Qt::Key_T: {  // transpose words
                 moveCursor(QTextCursor::StartOfWord);
                 moveCursor(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
                 QString const word1 = textCursor().selectedText();
@@ -488,7 +461,6 @@ void MultiLineEdit::keyPressEvent(QKeyEvent *event)
 #endif
 }
 
-
 QString MultiLineEdit::convertRichtextToMircCodes()
 {
     bool underline, bold, italic, color;
@@ -540,7 +512,7 @@ QString MultiLineEdit::convertRichtextToMircCodes()
                 mircBgColor = _mircColorMap.key(cursor.charFormat().background().color().name());
 
                 if (mircFgColor.isEmpty()) {
-                    mircFgColor = "01"; //use black if the current foreground color can't be converted
+                    mircFgColor = "01";  // use black if the current foreground color can't be converted
                 }
 
                 mircText.append(mircFgColor);
@@ -591,8 +563,7 @@ QString MultiLineEdit::convertRichtextToMircCodes()
     return mircText;
 }
 
-
-bool MultiLineEdit::mircCodesChanged(QTextCursor &cursor, QTextCursor &peekcursor)
+bool MultiLineEdit::mircCodesChanged(QTextCursor& cursor, QTextCursor& peekcursor)
 {
     bool changed = false;
     if (cursor.charFormat().font().bold() != peekcursor.charFormat().font().bold())
@@ -608,8 +579,7 @@ bool MultiLineEdit::mircCodesChanged(QTextCursor &cursor, QTextCursor &peekcurso
     return changed;
 }
 
-
-QString MultiLineEdit::convertMircCodesToHtml(const QString &text)
+QString MultiLineEdit::convertMircCodesToHtml(const QString& text)
 {
     QStringList words;
     QRegExp mircCode = QRegExp("(\ 2|\1d|\1f|\ 3)", Qt::CaseSensitive);
@@ -622,7 +592,7 @@ QString MultiLineEdit::convertMircCodesToHtml(const QString &text)
 
         if (posRight < 0) {
             words << text.mid(posLeft);
-            break; // no more mirc color codes
+            break;  // no more mirc color codes
         }
 
         if (posLeft < posRight) {
@@ -631,6 +601,10 @@ QString MultiLineEdit::convertMircCodesToHtml(const QString &text)
         }
 
         posRight = text.indexOf(mircCode.cap(), posRight + 1);
+        if (posRight == -1) {
+            words << text.mid(posLeft);
+            break;  // unclosed color code; can't process
+        }
         words << text.mid(posLeft, posRight + 1 - posLeft);
         posLeft = posRight + 1;
     }
@@ -654,8 +628,8 @@ QString MultiLineEdit::convertMircCodesToHtml(const QString &text)
             int len = 3;
             QString fg = words[i].mid(pos + 1, 2);
             QString bg;
-            if (words[i][pos+3] == ',')
-                bg = words[i].mid(pos+4, 2);
+            if (words[i][pos + 3] == ',')
+                bg = words[i].mid(pos + 4, 2);
 
             style.append(" color:");
             style.append(_mircColorMap[fg]);
@@ -684,13 +658,11 @@ QString MultiLineEdit::convertMircCodesToHtml(const QString &text)
     return words.join("").replace("\n", "<br />");
 }
 
-
 void MultiLineEdit::on_returnPressed()
 {
     on_returnPressed(convertRichtextToMircCodes());
 }
 
-
 void MultiLineEdit::on_returnPressed(QString text)
 {
     if (_completionSpace && text.endsWith(" ")) {
@@ -698,7 +670,7 @@ void MultiLineEdit::on_returnPressed(QString text)
     }
 
     if (!text.isEmpty()) {
-        foreach(const QString &line, text.split('\n', QString::SkipEmptyParts)) {
+        foreach (const QString& line, text.split('\n', QString::SkipEmptyParts)) {
             if (line.isEmpty())
                 continue;
             addToHistory(line);
@@ -712,7 +684,6 @@ void MultiLineEdit::on_returnPressed(QString text)
     }
 }
 
-
 void MultiLineEdit::on_textChanged()
 {
     _completionSpace = qMax(_completionSpace - 1, 0);
@@ -737,7 +708,7 @@ void MultiLineEdit::on_textChanged()
                     msg += "<br />";
                 }
                 msg += "...</p>";
-                QMessageBox question(QMessageBox::NoIcon, tr("Paste Protection"), msg, QMessageBox::Yes|QMessageBox::No);
+                QMessageBox question(QMessageBox::NoIcon, tr("Paste Protection"), msg, QMessageBox::Yes | QMessageBox::No);
                 question.setDefaultButton(QMessageBox::No);
 #ifdef Q_OS_MAC
                 question.setWindowFlags(question.windowFlags() | Qt::Sheet);
@@ -746,7 +717,7 @@ void MultiLineEdit::on_textChanged()
                     return;
             }
 
-            foreach(QString line, lines) {
+            foreach (QString line, lines) {
                 clear();
                 insert(line);
                 on_returnPressed();
@@ -764,43 +735,38 @@ void MultiLineEdit::on_textChanged()
     ensureCursorVisible();
 }
 
-
 void MultiLineEdit::on_documentHeightChanged(qreal)
 {
     updateScrollBars();
 }
 
-
 void MultiLineEdit::reset()
 {
     // every time the MultiLineEdit is cleared we also reset history index
     _idx = _history.count();
     clear();
     QTextBlockFormat format = textCursor().blockFormat();
-    format.setLeftMargin(leftMargin); // we want a little space between the frame and the contents
+    format.setLeftMargin(leftMargin);  // we want a little space between the frame and the contents
     textCursor().setBlockFormat(format);
     updateScrollBars();
 }
 
-
 void MultiLineEdit::showHistoryEntry()
 {
     // if the user changed the history, display the changed line
     setHtml(convertMircCodesToHtml(_tempHistory.contains(_idx) ? _tempHistory[_idx] : _history[_idx]));
     QTextCursor cursor = textCursor();
     QTextBlockFormat format = cursor.blockFormat();
-    format.setLeftMargin(leftMargin); // we want a little space between the frame and the contents
+    format.setLeftMargin(leftMargin);  // we want a little space between the frame and the contents
     cursor.setBlockFormat(format);
     cursor.movePosition(QTextCursor::End);
     setTextCursor(cursor);
     updateScrollBars();
 }
 
-
 void MultiLineEdit::addCompletionSpace()
 {
     // Inserting the space emits textChanged, which should not disable removal
     _completionSpace = 2;
     insertPlainText(" ");
 }
-