X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fuisupport%2Fmultilineedit.cpp;h=ac92235104057f846aed6c26b415ab4fbf76cf6c;hp=a0e0e8cc06644c089f97e2ccd7243b61ac1ebbf5;hb=e2188dc438be6f3eb0d9cdf47d28821aefe9835e;hpb=9dc0131dab77e3d3173906f1f8b14d3447523aea diff --git a/src/uisupport/multilineedit.cpp b/src/uisupport/multilineedit.cpp index a0e0e8cc..ac922351 100644 --- a/src/uisupport/multilineedit.cpp +++ b/src/uisupport/multilineedit.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-2010 by the Quassel Project * + * Copyright (C) 2005-2018 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -15,11 +15,10 @@ * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include -#include #include #include @@ -32,584 +31,772 @@ const int leftMargin = 3; MultiLineEdit::MultiLineEdit(QWidget *parent) - : MultiLineEditParent(parent), - _idx(0), - _mode(SingleLine), - _singleLine(true), - _minHeight(1), - _maxHeight(5), - _scrollBarsEnabled(true), - _pasteProtectionEnabled(true), - _lastDocumentHeight(-1) -{ -#if QT_VERSION >= 0x040500 - document()->setDocumentMargin(0); // new in Qt 4.5 and we really don't want it here -#endif + : MultiLineEditParent(parent) +{ + document()->setDocumentMargin(0); - setAcceptRichText(false); + setAcceptRichText(false); #ifdef HAVE_KDE - enableFindReplace(false); + enableFindReplace(false); #endif - setMode(SingleLine); - setWordWrapEnabled(false); - reset(); - - connect(this, SIGNAL(textChanged()), this, SLOT(on_textChanged())); +#if defined HAVE_SONNET && !defined HAVE_KDE + _spellCheckDecorator = new Sonnet::SpellCheckDecorator(this); + highlighter()->setActive(highlighter()->checkerEnabledByDefault()); +#endif - _mircColorMap["00"] = "#ffffff"; - _mircColorMap["01"] = "#000000"; - _mircColorMap["02"] = "#000080"; - _mircColorMap["03"] = "#008000"; - _mircColorMap["04"] = "#ff0000"; - _mircColorMap["05"] = "#800000"; - _mircColorMap["06"] = "#800080"; - _mircColorMap["07"] = "#ffa500"; - _mircColorMap["08"] = "#ffff00"; - _mircColorMap["09"] = "#00ff00"; - _mircColorMap["10"] = "#008080"; - _mircColorMap["11"] = "#00ffff"; - _mircColorMap["12"] = "#4169e1"; - _mircColorMap["13"] = "#ff00ff"; - _mircColorMap["14"] = "#808080"; - _mircColorMap["15"] = "#c0c0c0"; + setMode(SingleLine); + setLineWrapEnabled(false); + reset(); + // Prevent QTextHtmlImporter::appendNodeText from eating whitespace + document()->setDefaultStyleSheet("span { white-space: pre-wrap; }"); + + connect(this, SIGNAL(textChanged()), this, SLOT(on_textChanged())); + + _mircColorMap["00"] = "#ffffff"; + _mircColorMap["01"] = "#000000"; + _mircColorMap["02"] = "#000080"; + _mircColorMap["03"] = "#008000"; + _mircColorMap["04"] = "#ff0000"; + _mircColorMap["05"] = "#800000"; + _mircColorMap["06"] = "#800080"; + _mircColorMap["07"] = "#ffa500"; + _mircColorMap["08"] = "#ffff00"; + _mircColorMap["09"] = "#00ff00"; + _mircColorMap["10"] = "#008080"; + _mircColorMap["11"] = "#00ffff"; + _mircColorMap["12"] = "#4169e1"; + _mircColorMap["13"] = "#ff00ff"; + _mircColorMap["14"] = "#808080"; + _mircColorMap["15"] = "#c0c0c0"; } -MultiLineEdit::~MultiLineEdit() { + +#if defined HAVE_SONNET && !defined HAVE_KDE +Sonnet::Highlighter *MultiLineEdit::highlighter() const +{ + return _spellCheckDecorator->highlighter(); } -void MultiLineEdit::setCustomFont(const QFont &font) { - setFont(font); - updateSizeHint(); + +void MultiLineEdit::setSpellCheckEnabled(bool enabled) +{ + highlighter()->setActive(enabled); + if (enabled) { + highlighter()->slotRehighlight(); + } } -void MultiLineEdit::setMode(Mode mode) { - if(mode == _mode) - return; +void MultiLineEdit::contextMenuEvent(QContextMenuEvent *event) +{ + QMenu *menu = 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))); - _mode = mode; + menu->exec(event->globalPos()); + delete menu; } -void MultiLineEdit::setMinHeight(int lines) { - if(lines == _minHeight) - return; +#endif + - _minHeight = lines; - updateSizeHint(); +void MultiLineEdit::setCustomFont(const QFont &font) +{ + setFont(font); + updateSizeHint(); } -void MultiLineEdit::setMaxHeight(int lines) { - if(lines == _maxHeight) - return; - _maxHeight = lines; - updateSizeHint(); +void MultiLineEdit::setMode(Mode mode) +{ + if (mode == _mode) + return; + + _mode = mode; } -void MultiLineEdit::setScrollBarsEnabled(bool enable) { - if(_scrollBarsEnabled == enable) - return; - _scrollBarsEnabled = enable; - updateScrollBars(); +void MultiLineEdit::setLineWrapEnabled(bool enable) +{ + setLineWrapMode(enable ? WidgetWidth : NoWrap); + updateSizeHint(); } -void MultiLineEdit::updateScrollBars() { - QFontMetrics fm(font()); - int _maxPixelHeight = fm.lineSpacing() * _maxHeight; - if(_scrollBarsEnabled && document()->size().height() > _maxPixelHeight) - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); - else - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - if(!_scrollBarsEnabled || isSingleLine()) - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - else - setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); +void MultiLineEdit::setMinHeight(int lines) +{ + if (lines == _minHeight) + return; + + _minHeight = lines; + updateSizeHint(); } -void MultiLineEdit::resizeEvent(QResizeEvent *event) { - QTextEdit::resizeEvent(event); - updateSizeHint(); - updateScrollBars(); + +void MultiLineEdit::setMaxHeight(int lines) +{ + if (lines == _maxHeight) + return; + + _maxHeight = lines; + updateSizeHint(); } -void MultiLineEdit::updateSizeHint() { - QFontMetrics fm(font()); - int minPixelHeight = fm.lineSpacing() * _minHeight; - int maxPixelHeight = fm.lineSpacing() * _maxHeight; - int scrollBarHeight = horizontalScrollBar()->isVisible() ? horizontalScrollBar()->height() : 0; - // use the style to determine a decent size - int h = qMin(qMax((int)document()->size().height() + scrollBarHeight, minPixelHeight), maxPixelHeight) + 2 * frameWidth(); - QStyleOptionFrameV2 opt; - opt.initFrom(this); - opt.rect = QRect(0, 0, 100, h); - opt.lineWidth = lineWidth(); - opt.midLineWidth = midLineWidth(); - opt.state |= QStyle::State_Sunken; - QSize s = style()->sizeFromContents(QStyle::CT_LineEdit, &opt, QSize(100, h).expandedTo(QApplication::globalStrut()), this); - if(s != _sizeHint) { - _sizeHint = s; - updateGeometry(); - } +void MultiLineEdit::setScrollBarsEnabled(bool enable) +{ + if (_scrollBarsEnabled == enable) + return; + + _scrollBarsEnabled = enable; + updateScrollBars(); } -QSize MultiLineEdit::sizeHint() const { - if(!_sizeHint.isValid()) { - MultiLineEdit *that = const_cast(this); - that->updateSizeHint(); - } - return _sizeHint; + +void MultiLineEdit::updateScrollBars() +{ + QFontMetrics fm(font()); + int _maxPixelHeight = fm.lineSpacing() * _maxHeight; + if (_scrollBarsEnabled && document()->size().height() > _maxPixelHeight) + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + else + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + if (!_scrollBarsEnabled || isSingleLine()) + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + else + setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); } -QSize MultiLineEdit::minimumSizeHint() const { - return sizeHint(); + +void MultiLineEdit::resizeEvent(QResizeEvent *event) +{ + QTextEdit::resizeEvent(event); + updateSizeHint(); + updateScrollBars(); } -void MultiLineEdit::setSpellCheckEnabled(bool enable) { -#ifdef HAVE_KDE - setCheckSpellingEnabled(enable); -#else - Q_UNUSED(enable) + +void MultiLineEdit::updateSizeHint() +{ + QFontMetrics fm(font()); + int minPixelHeight = fm.lineSpacing() * _minHeight; + int maxPixelHeight = fm.lineSpacing() * _maxHeight; + int scrollBarHeight = horizontalScrollBar()->isVisible() ? horizontalScrollBar()->height() : 0; + + // use the style to determine a decent size + int h = qMin(qMax((int)document()->size().height() + scrollBarHeight, minPixelHeight), maxPixelHeight) + 2 * frameWidth(); + + QStyleOptionFrame opt; + opt.initFrom(this); + opt.rect = QRect(0, 0, 100, h); + opt.lineWidth = lineWidth(); + opt.midLineWidth = midLineWidth(); + opt.state |= QStyle::State_Sunken; + QWidget *widget = this; +#ifdef Q_OS_MAC + widget = 0; #endif + QSize s = style()->sizeFromContents(QStyle::CT_LineEdit, &opt, QSize(100, h).expandedTo(QApplication::globalStrut()), widget); + if (s != _sizeHint) { + _sizeHint = s; + updateGeometry(); + } } -void MultiLineEdit::setWordWrapEnabled(bool enable) { - setLineWrapMode(enable? WidgetWidth : NoWrap); - updateSizeHint(); + +QSize MultiLineEdit::sizeHint() const +{ + if (!_sizeHint.isValid()) { + auto *that = const_cast(this); + that->updateSizeHint(); + } + return _sizeHint; } -void MultiLineEdit::setPasteProtectionEnabled(bool enable, QWidget *) { - _pasteProtectionEnabled = enable; + +QSize MultiLineEdit::minimumSizeHint() const +{ + return sizeHint(); } -void MultiLineEdit::historyMoveBack() { - addToHistory(convertRichtextToMircCodes(), true); - if(_idx > 0) { - _idx--; - showHistoryEntry(); - } +void MultiLineEdit::setEmacsMode(bool enable) +{ + _emacsMode = enable; } -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 - showHistoryEntry(); - else - reset(); // equals clear() in this case - } else { - addToHistory(convertRichtextToMircCodes()); - reset(); - } +void MultiLineEdit::setPasteProtectionEnabled(bool enable, QWidget *) +{ + _pasteProtectionEnabled = enable; } -bool MultiLineEdit::addToHistory(const QString &text, bool temporary) { - if(text.isEmpty()) - return false; - Q_ASSERT(0 <= _idx && _idx <= _history.count()); +void MultiLineEdit::historyMoveBack() +{ + addToHistory(convertRichtextToMircCodes(), true); - if(temporary) { - // if an entry of the history is changed, we remember it and show it again at this - // position until a line was actually sent - // sent lines get appended to the history - if(_history.isEmpty() || text != _history[_idx - (int)(_idx == _history.count())]) { - _tempHistory[_idx] = text; - return true; + if (_idx > 0) { + _idx--; + showHistoryEntry(); } - } else { - if(_history.isEmpty() || text != _history.last()) { - _history << text; - _tempHistory.clear(); - return true; +} + + +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 + showHistoryEntry(); + else + reset(); // equals clear() in this case } - } - return false; -} - -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(e); - QKeySequence key = QKeySequence(event->key() | event->modifiers()); - foreach(QAction *action, GraphicalUi::actionCollection()->actions()) { - if(action->shortcuts().contains(key)) { - e->ignore(); - return false; - } + else { + addToHistory(convertRichtextToMircCodes()); + reset(); } - } - - return MultiLineEditParent::event(e); } -void MultiLineEdit::keyPressEvent(QKeyEvent *event) { - // Workaround the fact that Qt < 4.5 doesn't know InsertLineSeparator yet -#if QT_VERSION >= 0x040500 - if(event == QKeySequence::InsertLineSeparator) { -#else -# ifdef Q_WS_MAC - if((event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) && event->modifiers() & Qt::META) { -# else - if((event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) && event->modifiers() & Qt::SHIFT) { -# endif -#endif +bool MultiLineEdit::addToHistory(const QString &text, bool temporary) +{ + if (text.isEmpty()) + return false; - if(_mode == SingleLine) { - event->accept(); - on_returnPressed(); - return; + Q_ASSERT(0 <= _idx && _idx <= _history.count()); + + if (temporary) { + // if an entry of the history is changed, we remember it and show it again at this + // position until a line was actually sent + // sent lines get appended to the history + if (_history.isEmpty() || text != _history[_idx - (int)(_idx == _history.count())]) { + _tempHistory[_idx] = text; + return true; + } } - MultiLineEditParent::keyPressEvent(event); - return; - } - - switch(event->key()) { - case Qt::Key_Up: - if(event->modifiers() & Qt::ShiftModifier) - break; - { - event->accept(); - if(!(event->modifiers() & Qt::ControlModifier)) { - int pos = textCursor().position(); - moveCursor(QTextCursor::Up); - if(pos == textCursor().position()) // already on top line -> history - historyMoveBack(); - } else - historyMoveBack(); - return; + else { + if (_history.isEmpty() || text != _history.last()) { + _history << text; + _tempHistory.clear(); + return true; + } } + return false; +} - case Qt::Key_Down: - if(event->modifiers() & Qt::ShiftModifier) - break; - { - event->accept(); - if(!(event->modifiers() & Qt::ControlModifier)) { - int pos = textCursor().position(); - moveCursor(QTextCursor::Down); - if(pos == textCursor().position()) // already on bottom line -> history - historyMoveForward(); - } else - historyMoveForward(); - return; + +bool MultiLineEdit::event(QEvent *e) +{ + // We need to make sure that global shortcuts aren't eaten + if (e->type() == QEvent::ShortcutOverride) { + auto *event = static_cast(e); + QKeySequence key = QKeySequence(event->key() | event->modifiers()); + foreach(QAction *action, GraphicalUi::actionCollection()->actions()) { + if (action->shortcuts().contains(key)) { + e->ignore(); + return false; + } + } } - case Qt::Key_Return: - case Qt::Key_Enter: - case Qt::Key_Select: - event->accept(); - on_returnPressed(); - return; + return MultiLineEditParent::event(e); +} - // We don't want to have the tab key react even if no completer is installed - case Qt::Key_Tab: - event->accept(); - return; - default: - ; - } +void MultiLineEdit::keyPressEvent(QKeyEvent *event) +{ + if (event == QKeySequence::InsertLineSeparator) { + if (_mode == SingleLine) { + event->accept(); + on_returnPressed(); + return; + } + MultiLineEditParent::keyPressEvent(event); + return; + } + + switch (event->key()) { + case Qt::Key_Up: + if (event->modifiers() & Qt::ShiftModifier) + break; + { + event->accept(); + if (!(event->modifiers() & Qt::ControlModifier)) { + int pos = textCursor().position(); + moveCursor(QTextCursor::Up); + if (pos == textCursor().position()) // already on top line -> history + historyMoveBack(); + } + else + historyMoveBack(); + return; + } + + case Qt::Key_Down: + if (event->modifiers() & Qt::ShiftModifier) + break; + { + event->accept(); + if (!(event->modifiers() & Qt::ControlModifier)) { + int pos = textCursor().position(); + moveCursor(QTextCursor::Down); + if (pos == textCursor().position()) // already on bottom line -> history + historyMoveForward(); + } + else + historyMoveForward(); + return; + } + + case Qt::Key_Return: + case Qt::Key_Enter: + case Qt::Key_Select: + event->accept(); + on_returnPressed(); + return; + + // We don't want to have the tab key react even if no completer is installed + case Qt::Key_Tab: + event->accept(); + return; + + default: + ; + } + if (_emacsMode) { + if (event->modifiers() & Qt::ControlModifier) { + switch (event->key()) { + // move + case Qt::Key_A: + moveCursor(QTextCursor::StartOfLine); + return; + case Qt::Key_E: + moveCursor(QTextCursor::EndOfLine); + return; + case Qt::Key_F: + moveCursor(QTextCursor::Right); + return; + case Qt::Key_B: + moveCursor(QTextCursor::Left); + return; + + // modify + case Qt::Key_Y: + paste(); + return; + case Qt::Key_K: + moveCursor(QTextCursor::EndOfLine, QTextCursor::KeepAnchor); + cut(); + return; + + default: + break; + } + } + else if (event->modifiers() & Qt::MetaModifier || + event->modifiers() & Qt::AltModifier) + { + switch (event->key()) { + case Qt::Key_Right: + moveCursor(QTextCursor::WordRight); + return; + case Qt::Key_Left: + moveCursor(QTextCursor::WordLeft); + return; + case Qt::Key_F: + moveCursor(QTextCursor::WordRight); + return; + case Qt::Key_B: + moveCursor(QTextCursor::WordLeft); + return; + case Qt::Key_Less: + moveCursor(QTextCursor::Start); + return; + case Qt::Key_Greater: + moveCursor(QTextCursor::End); + return; + + // modify + case Qt::Key_D: + moveCursor(QTextCursor::WordRight, QTextCursor::KeepAnchor); + cut(); + return; + + case Qt::Key_U: // uppercase word + moveCursor(QTextCursor::WordRight, QTextCursor::KeepAnchor); + textCursor().insertText(textCursor().selectedText().toUpper()); + return; + + case Qt::Key_L: // lowercase word + moveCursor(QTextCursor::WordRight, QTextCursor::KeepAnchor); + textCursor().insertText(textCursor().selectedText().toLower()); + return; + + 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 + moveCursor(QTextCursor::StartOfWord); + moveCursor(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); + QString const word1 = textCursor().selectedText(); + textCursor().clearSelection(); + moveCursor(QTextCursor::WordRight); + moveCursor(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); + QString const word2 = textCursor().selectedText(); + if (!word2.isEmpty() && !word1.isEmpty()) { + textCursor().insertText(word1); + moveCursor(QTextCursor::WordLeft); + moveCursor(QTextCursor::WordLeft); + moveCursor(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); + textCursor().insertText(word2); + moveCursor(QTextCursor::WordRight); + moveCursor(QTextCursor::EndOfWord); + } + return; + } + + default: + break; + } + } + } #ifdef HAVE_KDE - KTextEdit::keyPressEvent(event); + KTextEdit::keyPressEvent(event); #else - QTextEdit::keyPressEvent(event); + QTextEdit::keyPressEvent(event); #endif } -QString MultiLineEdit::convertRichtextToMircCodes() { - bool underline, bold, italic, color; - QString mircText, mircFgColor, mircBgColor; - QTextCursor cursor = textCursor(); - QTextCursor peekcursor = textCursor(); - cursor.movePosition(QTextCursor::Start); - underline = bold = italic = color = false; +QString MultiLineEdit::convertRichtextToMircCodes() +{ + bool underline, bold, italic, color; + QString mircText, mircFgColor, mircBgColor; + QTextCursor cursor = textCursor(); + QTextCursor peekcursor = textCursor(); + cursor.movePosition(QTextCursor::Start); + + underline = bold = italic = color = false; + + while (cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor)) { + if (cursor.selectedText() == QString(QChar(QChar::LineSeparator)) + || cursor.selectedText() == QString(QChar(QChar::ParagraphSeparator))) { + if (color) { + color = false; + mircText.append('\x03'); + } + if (underline) { + underline = false; + mircText.append('\x1f'); + } + if (italic) { + italic = false; + mircText.append('\x1d'); + } + if (bold) { + bold = false; + mircText.append('\x02'); + } + mircText.append('\n'); + } + else { + if (!bold && cursor.charFormat().font().bold()) { + bold = true; + mircText.append('\x02'); + } + if (!italic && cursor.charFormat().fontItalic()) { + italic = true; + mircText.append('\x1d'); + } + if (!underline && cursor.charFormat().fontUnderline()) { + underline = true; + mircText.append('\x1f'); + } + if (!color && (cursor.charFormat().foreground().isOpaque() || cursor.charFormat().background().isOpaque())) { + color = true; + mircText.append('\x03'); + mircFgColor = _mircColorMap.key(cursor.charFormat().foreground().color().name()); + mircBgColor = _mircColorMap.key(cursor.charFormat().background().color().name()); + + if (mircFgColor.isEmpty()) { + mircFgColor = "01"; //use black if the current foreground color can't be converted + } + + mircText.append(mircFgColor); + if (cursor.charFormat().background().isOpaque()) + mircText.append("," + mircBgColor); + } + + mircText.append(cursor.selectedText()); + + peekcursor.setPosition(cursor.position()); + peekcursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); + + if (mircCodesChanged(cursor, peekcursor)) { + if (color) { + color = false; + mircText.append('\x03'); + } + if (underline) { + underline = false; + mircText.append('\x1f'); + } + if (italic) { + italic = false; + mircText.append('\x1d'); + } + if (bold) { + bold = false; + mircText.append('\x02'); + } + } + } - while (cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor)) { + cursor.clearSelection(); + } - if (cursor.selectedText() == QString(QChar(QChar::LineSeparator)) - || cursor.selectedText() == QString(QChar(QChar::ParagraphSeparator))) { - if (color) { - color = false; + if (color) mircText.append('\x03'); - } - if (underline) { - underline = false; + + if (underline) mircText.append('\x1f'); - } - if (italic) { - italic = false; + + if (italic) mircText.append('\x1d'); - } - if (bold) { - bold = false; - mircText.append('\x02'); - } - mircText.append('\n'); - } - else { - if (!bold && cursor.charFormat().font().bold()) { - bold = true; + + if (bold) mircText.append('\x02'); - } - if (!italic && cursor.charFormat().fontItalic()) { - italic = true; - mircText.append('\x1d'); - } - if (!underline && cursor.charFormat().fontUnderline()) { - underline = true; - mircText.append('\x1f'); - } - if (!color && (cursor.charFormat().foreground().isOpaque() || cursor.charFormat().background().isOpaque())) { - color = true; - mircText.append('\x03'); - mircFgColor = _mircColorMap.key(cursor.charFormat().foreground().color().name()); - mircBgColor = _mircColorMap.key(cursor.charFormat().background().color().name()); - if (mircFgColor.isEmpty()) { - mircFgColor = "01"; //use black if the current foreground color can't be converted - } + return mircText; +} + + +bool MultiLineEdit::mircCodesChanged(QTextCursor &cursor, QTextCursor &peekcursor) +{ + bool changed = false; + if (cursor.charFormat().font().bold() != peekcursor.charFormat().font().bold()) + changed = true; + if (cursor.charFormat().fontItalic() != peekcursor.charFormat().fontItalic()) + changed = true; + if (cursor.charFormat().fontUnderline() != peekcursor.charFormat().fontUnderline()) + changed = true; + if (cursor.charFormat().foreground().color() != peekcursor.charFormat().foreground().color()) + changed = true; + if (cursor.charFormat().background().color() != peekcursor.charFormat().background().color()) + changed = true; + return changed; +} + + +QString MultiLineEdit::convertMircCodesToHtml(const QString &text) +{ + QStringList words; + QRegExp mircCode = QRegExp("(|||)", Qt::CaseSensitive); - mircText.append(mircFgColor); - if (cursor.charFormat().background().isOpaque()) - mircText.append("," + mircBgColor); - } + int posLeft = 0; + int posRight = 0; - mircText.append(cursor.selectedText()); + for (;;) { + posRight = mircCode.indexIn(text, posLeft); - peekcursor.setPosition(cursor.position()); - peekcursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); + if (posRight < 0) { + words << text.mid(posLeft); + break; // no more mirc color codes + } + + if (posLeft < posRight) { + words << text.mid(posLeft, posRight - posLeft); + posLeft = posRight; + } + + posRight = text.indexOf(mircCode.cap(), posRight + 1); + words << text.mid(posLeft, posRight + 1 - posLeft); + posLeft = posRight + 1; + } - if (mircCodesChanged(cursor, peekcursor)) { - if (color) { - color = false; - mircText.append('\x03'); + for (int i = 0; i < words.count(); i++) { + QString style; + if (words[i].contains('\x02')) { + style.append(" font-weight:600;"); + words[i].replace('\x02', ""); } - if (underline) { - underline = false; - mircText.append('\x1f'); + if (words[i].contains('\x1d')) { + style.append(" font-style:italic;"); + words[i].replace('\x1d', ""); } - if (italic) { - italic = false; - mircText.append('\x1d'); + if (words[i].contains('\x1f')) { + style.append(" text-decoration: underline;"); + words[i].replace('\x1f', ""); } - if (bold) { - bold = false; - mircText.append('\x02'); + if (words[i].contains('\x03')) { + int pos = words[i].indexOf('\x03'); + 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); + + style.append(" color:"); + style.append(_mircColorMap[fg]); + style.append(";"); + + if (!bg.isEmpty()) { + style.append(" background-color:"); + style.append(_mircColorMap[bg]); + style.append(";"); + len = 6; + } + words[i].replace(pos, len, ""); + words[i].replace('\x03', ""); + } + words[i].replace("&", "&"); + words[i].replace("<", "<"); + words[i].replace(">", ">"); + words[i].replace("\"", """); + if (style.isEmpty()) { + words[i] = "" + words[i] + ""; + } + else { + words[i] = "" + words[i] + ""; } - } } + return words.join("").replace("\n", "
"); +} - cursor.clearSelection(); - } - if (color) { - color = false; - mircText.append('\x03'); - } - if (underline) { - underline = false; - mircText.append('\x1f'); - } - if (italic) { - italic = false; - mircText.append('\x1d'); - } - if (bold) { - bold = false; - mircText.append('\x02'); - } - - return mircText; -} - -bool MultiLineEdit::mircCodesChanged(QTextCursor &cursor, QTextCursor &peekcursor) { - bool changed = false; - if (cursor.charFormat().font().bold() != peekcursor.charFormat().font().bold()) - changed = true; - if (cursor.charFormat().fontItalic() != peekcursor.charFormat().fontItalic()) - changed = true; - if (cursor.charFormat().fontUnderline() != peekcursor.charFormat().fontUnderline()) - changed = true; - if (cursor.charFormat().foreground().color() != peekcursor.charFormat().foreground().color()) - changed = true; - if (cursor.charFormat().background().color() != peekcursor.charFormat().background().color()) - changed = true; - return changed; -} - -QString MultiLineEdit::convertMircCodesToHtml(const QString &text) { - QStringList words; - QRegExp mircCode = QRegExp("(|||)", Qt::CaseSensitive); - - int posLeft = 0; - int posRight = 0; - - for(;;) { - posRight = mircCode.indexIn(text, posLeft); - - if(posRight < 0) { - words << text.mid(posLeft); - break; // no more mirc color codes - } - if (posLeft < posRight) { - words << text.mid(posLeft, posRight - posLeft); - posLeft = posRight; +void MultiLineEdit::on_returnPressed() +{ + on_returnPressed(convertRichtextToMircCodes()); +} + + +void MultiLineEdit::on_returnPressed(QString text) +{ + if (_completionSpace && text.endsWith(" ")) { + text.chop(1); } - posRight = text.indexOf(mircCode.cap(), posRight + 1); - words << text.mid(posLeft, posRight + 1 - posLeft); - posLeft = posRight + 1; - } - - for (int i = 0; i < words.count(); i++) { - QString style; - if (words[i].contains('\x02')) { - style.append(" font-weight:600;"); - words[i].replace('\x02',""); - } - if (words[i].contains('\x1d')) { - style.append(" font-style:italic;"); - words[i].replace('\x1d',""); - } - if (words[i].contains('\x1f')) { - style.append(" text-decoration: underline;"); - words[i].replace('\x1f',""); - } - if (words[i].contains('\x03')) { - int pos = words[i].indexOf('\x03'); - 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); - - style.append(" color:"); - style.append(_mircColorMap[fg]); - style.append(";"); - - if (!bg.isEmpty()) { - style.append(" background-color:"); - style.append(_mircColorMap[bg]); - style.append(";"); - len = 6; + if (!text.isEmpty()) { + foreach(const QString &line, text.split('\n', QString::SkipEmptyParts)) { + if (line.isEmpty()) + continue; + addToHistory(line); + emit textEntered(line); } - words[i].replace(pos, len, ""); - words[i].replace('\x03',""); - } - words[i].replace("&","&"); - words[i].replace("<", "<"); - words[i].replace(">", ">"); - words[i].replace("\"", """); - if (style.isEmpty()) { - words[i] = "" + words[i] + ""; - } - else { - words[i] = "" + words[i] + ""; - } - } - return words.join("").replace("\n","
"); -} - -void MultiLineEdit::on_returnPressed() { - on_returnPressed(convertRichtextToMircCodes()); -} - -void MultiLineEdit::on_returnPressed(const QString & text) { - if(!text.isEmpty()) { - foreach(const QString &line, text.split('\n', QString::SkipEmptyParts)) { - if(line.isEmpty()) - continue; - addToHistory(line); - emit textEntered(line); + reset(); + _tempHistory.clear(); } - reset(); - _tempHistory.clear(); - } else { - emit noTextEntered(); - } -} - -void MultiLineEdit::on_textChanged() { - QString newText = text(); - newText.replace("\r\n", "\n"); - newText.replace('\r', '\n'); - if(_mode == SingleLine) { - if(!pasteProtectionEnabled()) - newText.replace('\n', ' '); - else if(newText.contains('\n')) { - QStringList lines = newText.split('\n', QString::SkipEmptyParts); - clear(); - - if(lines.count() >= 4) { - QString msg = tr("Do you really want to paste %n lines?", "", lines.count()); - msg += "

"; - for(int i = 0; i < 4; i++) { - msg += Qt::escape(lines[i].left(40)); - if(lines[i].count() > 40) - msg += "..."; - msg += "
"; - } - msg += "...

"; - QMessageBox question(QMessageBox::NoIcon, tr("Paste Protection"), msg, QMessageBox::Yes|QMessageBox::No); - question.setDefaultButton(QMessageBox::No); -#ifdef Q_WS_MAC - question.setWindowFlags(question.windowFlags() | Qt::Sheet); + else { + emit noTextEntered(); + } +} + + +void MultiLineEdit::on_textChanged() +{ + _completionSpace = qMax(_completionSpace - 1, 0); + + QString newText = text(); + newText.replace("\r\n", "\n"); + newText.replace('\r', '\n'); + if (_mode == SingleLine) { + if (!pasteProtectionEnabled()) + newText.replace('\n', ' '); + else if (newText.contains('\n')) { + QStringList lines = newText.split('\n', QString::SkipEmptyParts); + clear(); + + if (lines.count() >= 4) { + QString msg = tr("Do you really want to paste %n line(s)?", "", lines.count()); + msg += "

"; + for (int i = 0; i < 4; i++) { + msg += lines[i].left(40).toHtmlEscaped(); + if (lines[i].count() > 40) + msg += "..."; + msg += "
"; + } + msg += "...

"; + 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); #endif - if(question.exec() != QMessageBox::Yes) - return; - } + if (question.exec() != QMessageBox::Yes) + return; + } + + foreach(QString line, lines) { + clear(); + insert(line); + on_returnPressed(); + } + } + } - foreach(QString line, lines) { - clear(); - insert(line); - on_returnPressed(); - } + _singleLine = (newText.indexOf('\n') < 0); + + if (document()->size().height() != _lastDocumentHeight) { + _lastDocumentHeight = document()->size().height(); + on_documentHeightChanged(_lastDocumentHeight); } - } + updateSizeHint(); + ensureCursorVisible(); +} - _singleLine = (newText.indexOf('\n') < 0); - if(document()->size().height() != _lastDocumentHeight) { - _lastDocumentHeight = document()->size().height(); - on_documentHeightChanged(_lastDocumentHeight); - } - updateSizeHint(); - ensureCursorVisible(); +void MultiLineEdit::on_documentHeightChanged(qreal) +{ + updateScrollBars(); } -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 + textCursor().setBlockFormat(format); + 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 - 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 + cursor.setBlockFormat(format); + cursor.movePosition(QTextCursor::End); + setTextCursor(cursor); + 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 - 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(" "); } +