From a9c2f4157175cfb775980eb72c8312cdffcbfe00 Mon Sep 17 00:00:00 2001 From: Dirk Rettschlag Date: Thu, 4 Feb 2010 16:23:52 +0100 Subject: [PATCH] added support for RichText input and conversion to mirc format --- src/qtui/inputwidget.cpp | 85 +++++++++++++++ src/qtui/inputwidget.h | 17 +++ src/uisupport/multilineedit.cpp | 187 +++++++++++++++++++++++++++++++- src/uisupport/multilineedit.h | 6 + 4 files changed, 290 insertions(+), 5 deletions(-) diff --git a/src/qtui/inputwidget.cpp b/src/qtui/inputwidget.cpp index e30ba786..be18830b 100644 --- a/src/qtui/inputwidget.cpp +++ b/src/qtui/inputwidget.cpp @@ -88,6 +88,35 @@ InputWidget::InputWidget(QWidget *parent) connect(activateInputline, SIGNAL(triggered()), SLOT(setFocus())); activateInputline->setText(tr("Focus Input Line")); activateInputline->setShortcut(tr("Ctrl+L")); + + actionTextBold = coll->add("TextBold"); + QFont bold; + bold.setBold(true); + actionTextBold->setFont(bold); + actionTextBold->setText(tr("Bold")); + actionTextBold->setShortcut(tr("Ctrl+B")); + actionTextBold->setCheckable(true); + connect(actionTextBold, SIGNAL(triggered()), SLOT(textBold())); + + actionTextUnderline = coll->add("TextUnderline"); + QFont underline; + underline.setUnderline(true); + actionTextUnderline->setFont(underline); + actionTextUnderline->setText(tr("Underline")); + actionTextUnderline->setShortcut(tr("Ctrl+U")); + actionTextUnderline->setCheckable(true); + connect(actionTextUnderline, SIGNAL(triggered()), SLOT(textUnderline())); + + actionTextItalic = coll->add("TextItalic"); + QFont italic; + italic.setItalic(true); + actionTextItalic->setFont(italic); + actionTextItalic->setText(tr("Italic")); + actionTextItalic->setShortcut(tr("Ctrl+I")); + actionTextItalic->setCheckable(true); + connect(actionTextItalic, SIGNAL(triggered()), SLOT(textItalic())); + + connect(inputLine(), SIGNAL(currentCharFormatChanged(QTextCharFormat)), this, SLOT(currentCharFormatChanged(QTextCharFormat))); } InputWidget::~InputWidget() { @@ -324,8 +353,64 @@ void InputWidget::changeNick(const QString &newNick) const { void InputWidget::on_inputEdit_textEntered(const QString &text) const { Client::userInput(currentBufferInfo(), text); + actionTextBold->setChecked(false); + actionTextUnderline->setChecked(false); + actionTextItalic->setChecked(false); + inputLine()->setFontWeight(QFont::Normal); + inputLine()->setFontUnderline(false); + inputLine()->setFontItalic(false); +} + +void InputWidget::mergeFormatOnWordOrSelection(const QTextCharFormat &format) { + QTextCursor cursor = inputLine()->textCursor(); + if (!cursor.hasSelection()) + cursor.select(QTextCursor::WordUnderCursor); + cursor.mergeCharFormat(format); + inputLine()->mergeCurrentCharFormat(format); } +void InputWidget::currentCharFormatChanged(const QTextCharFormat &format) { + fontChanged(format.font()); + colorChanged(format.foreground().color(), format.background().color()); +} + +void InputWidget::textBold() +{ + if (inputLine()->hasFocus()) { + QTextCharFormat fmt; + fmt.setFontWeight(actionTextBold->isChecked() ? QFont::Bold : QFont::Normal); + mergeFormatOnWordOrSelection(fmt); + } +} + +void InputWidget::textUnderline() +{ + if (inputLine()->hasFocus()) { + QTextCharFormat fmt; + fmt.setFontUnderline(actionTextUnderline->isChecked()); + mergeFormatOnWordOrSelection(fmt); + } +} + +void InputWidget::textItalic() +{ + if (inputLine()->hasFocus()) { + QTextCharFormat fmt; + fmt.setFontItalic(actionTextItalic->isChecked()); + mergeFormatOnWordOrSelection(fmt); + } +} + +void InputWidget::fontChanged(const QFont &f) +{ + actionTextBold->setChecked(f.bold()); + actionTextItalic->setChecked(f.italic()); + actionTextUnderline->setChecked(f.underline()); +} + +void InputWidget::colorChanged(const QColor &fg, const QColor &bg) { + //TODO update colorpicker +} // MOUSE WHEEL FILTER MouseWheelFilter::MouseWheelFilter(QObject *parent) diff --git a/src/qtui/inputwidget.h b/src/qtui/inputwidget.h index 7d7c0534..b1855677 100644 --- a/src/qtui/inputwidget.h +++ b/src/qtui/inputwidget.h @@ -28,6 +28,7 @@ #include "bufferinfo.h" #include "identity.h" #include "network.h" +#include class MultiLineEdit; @@ -70,11 +71,27 @@ private slots: BufferInfo currentBufferInfo() const; + void currentCharFormatChanged(const QTextCharFormat &format); + void textBold(); + void textUnderline(); + void textItalic(); + private: Ui::InputWidget ui; NetworkId _networkId; IdentityId _identityId; + + Action *actionTextBold, + *actionTextUnderline, + *actionTextItalic, + *actionTextFgColor, + *actionTextBgColor; + + void mergeFormatOnWordOrSelection(const QTextCharFormat &format); + void fontChanged(const QFont &f); + void colorChanged(const QColor &fg, const QColor &bg); + }; diff --git a/src/uisupport/multilineedit.cpp b/src/uisupport/multilineedit.cpp index 9029f02f..c80c2fce 100644 --- a/src/uisupport/multilineedit.cpp +++ b/src/uisupport/multilineedit.cpp @@ -59,6 +59,24 @@ MultiLineEdit::MultiLineEdit(QWidget *parent) reset(); 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() { @@ -171,7 +189,7 @@ void MultiLineEdit::setPasteProtectionEnabled(bool enable, QWidget *) { } void MultiLineEdit::historyMoveBack() { - addToHistory(text(), true); + addToHistory(convertHtmlToMircCodes(html()), true); if(idx > 0) { idx--; @@ -180,7 +198,7 @@ void MultiLineEdit::historyMoveBack() { } void MultiLineEdit::historyMoveForward() { - addToHistory(text(), true); + addToHistory(convertHtmlToMircCodes(html()), true); if(idx < history.count()) { idx++; @@ -189,7 +207,7 @@ void MultiLineEdit::historyMoveForward() { else reset(); // equals clear() in this case } else { - addToHistory(text()); + addToHistory(convertHtmlToMircCodes(html())); reset(); } } @@ -299,8 +317,166 @@ void MultiLineEdit::keyPressEvent(QKeyEvent *event) { #endif } +QString MultiLineEdit::convertHtmlToMircCodes(const QString &text) { + QRegExp regexLines = QRegExp("(?:(.*)

\\n?)+", Qt::CaseInsensitive); + regexLines.setMinimal(true); + + QRegExp regexStyles = QRegExp("(?:(()(.*)))", Qt::CaseInsensitive); + regexStyles.setMinimal(true); + + QRegExp regexColors = QRegExp("((?:background-)?color):(#[0-9a-f]{6})", Qt::CaseInsensitive); + regexStyles.setMinimal(true); + + QStringList result; + int posLines = 0; + QString line, line2, styleText, style, content; + + while ((posLines = regexLines.indexIn(text, posLines)) != -1) { + line = line2 = regexLines.cap(1); + int posStyles = 0; + while ((posStyles = regexStyles.indexIn(line2, posStyles)) != -1) { + styleText = regexStyles.cap(1); + style = regexStyles.cap(2); + content = regexStyles.cap(3); + + if (style.contains("font-weight:600;")) { + content.prepend('\x02'); + content.append('\x02'); + } + if (style.contains("font-style:italic;")) { + content.prepend('\x1d'); + content.append('\x1d'); + } + if (style.contains("text-decoration: underline;")) { + content.prepend('\x1f'); + content.append('\x1f'); + } + if (style.contains("color:#")) { // we have either foreground or background color or both + int posColors = 0; + QString mircFgColor, mircBgColor; + while ((posColors = regexColors.indexIn(style, posColors)) != -1) { + QString colorType = regexColors.cap(1); + QString color = regexColors.cap(2); + + if (colorType == "color") + mircFgColor = mircColorMap.key(color); + + if (colorType == "background-color") + mircBgColor = mircColorMap.key(color); + + posColors += regexColors.matchedLength(); + } + if (!mircBgColor.isEmpty()) + content.prepend("," + mircBgColor); + + // we need a fg color to be able to use a bg color + if (mircFgColor.isEmpty()) { + //FIXME try to use the current forecolor + mircFgColor = mircColorMap.key(textColor().name()); + if (mircFgColor.isEmpty()) + mircFgColor = "01"; //use black if the current foreground color can't be converted + } + + content.prepend(mircFgColor); + content.prepend('\x03'); + content.append('\x03'); + } + + line.replace(styleText, content); + posStyles += regexStyles.matchedLength(); + } + + // get rid of all remaining html tags + QRegExp regexTags = QRegExp("<.*>",Qt::CaseInsensitive); + regexTags.setMinimal(true); + line.replace(regexTags, ""); + + line.replace("&","&"); + line.replace("<","<"); + line.replace(">",">"); + + result << line; + posLines += regexLines.matchedLength(); + } + + return result.join("\n").replace("
", "\n"); +} + +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; + } + + 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; + } + words[i].replace(pos, len, ""); + words[i].replace('\x03',""); + } + words[i].replace("&","&"); + words[i].replace("<", "<"); + words[i].replace(">", ">"); + if (style.isEmpty()) { + words[i] = "" + words[i] + ""; + } + else { + words[i] = "" + words[i] + ""; + } + } + return words.join(""); +} + void MultiLineEdit::on_returnPressed() { - on_returnPressed(text()); + on_returnPressed(convertHtmlToMircCodes(html())); } void MultiLineEdit::on_returnPressed(const QString & text) { @@ -382,7 +558,8 @@ void MultiLineEdit::reset() { void MultiLineEdit::showHistoryEntry() { // if the user changed the history, display the changed line - setPlainText(tempHistory.contains(idx) ? tempHistory[idx] : history[idx]); + setHtml(convertMircCodesToHtml(tempHistory.contains(idx) ? tempHistory[idx] : history[idx])); + //setPlainText(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 diff --git a/src/uisupport/multilineedit.h b/src/uisupport/multilineedit.h index d8a81ec6..da443041 100644 --- a/src/uisupport/multilineedit.h +++ b/src/uisupport/multilineedit.h @@ -54,6 +54,7 @@ public: // Compatibility methods with the rest of the classes which still expect this to be a QLineEdit inline QString text() { return toPlainText(); } + inline QString html() { return toHtml(); } inline int cursorPosition() { return textCursor().position(); } inline void insert(const QString &newText) { insertPlainText(newText); } inline void backspace() { keyPressEvent(new QKeyEvent(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier)); } @@ -94,6 +95,9 @@ private slots: void historyMoveForward(); void historyMoveBack(); + QString convertHtmlToMircCodes(const QString &text); + QString convertMircCodesToHtml(const QString &text); + private: QStringList history; QHash tempHistory; @@ -108,6 +112,8 @@ private: QSize _sizeHint; qreal _lastDocumentHeight; + QHash mircColorMap; + void reset(); void showHistoryEntry(); void updateScrollBars(); -- 2.20.1