1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00
qTox/src/chatlog/chatmessage.cpp

281 lines
9.2 KiB
C++
Raw Normal View History

2014-11-16 19:58:43 +08:00
/*
Copyright © 2014-2019 by The qTox Project Contributors
2014-11-16 19:58:43 +08:00
This file is part of qTox, a Qt-based graphical interface for Tox.
qTox is libre software: you can redistribute it and/or modify
2014-11-16 19:58:43 +08:00
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
qTox is distributed in the hope that it will be useful,
2014-11-16 19:58:43 +08:00
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
2014-11-16 19:58:43 +08:00
You should have received a copy of the GNU General Public License
along with qTox. If not, see <http://www.gnu.org/licenses/>.
2014-11-16 19:58:43 +08:00
*/
2014-11-12 21:11:25 +08:00
#include "chatmessage.h"
2015-01-03 22:03:33 +08:00
#include "chatlinecontentproxy.h"
#include "textformatter.h"
2015-01-03 22:03:33 +08:00
#include "content/filetransferwidget.h"
#include "content/image.h"
2015-01-20 02:04:19 +08:00
#include "content/notificationicon.h"
#include "content/spinner.h"
#include "content/text.h"
#include "content/timestamp.h"
#include "src/widget/style.h"
2014-11-12 21:11:25 +08:00
#include <QDebug>
#include <QCryptographicHash>
#include "src/persistence/settings.h"
#include "src/persistence/smileypack.h"
2015-01-03 03:07:45 +08:00
2015-01-11 05:21:33 +08:00
#define NAME_COL_WIDTH 90.0
2015-01-05 21:06:14 +08:00
#define TIME_COL_WIDTH 90.0
2014-11-12 23:45:24 +08:00
2015-01-05 01:21:35 +08:00
ChatMessage::ChatMessage()
2014-11-12 23:45:24 +08:00
{
}
ChatMessage::Ptr ChatMessage::createChatMessage(const QString& sender, const QString& rawMessage,
2018-10-23 00:23:41 +08:00
MessageType type, bool isMe, const QDateTime& date, bool colorizeName)
2015-01-03 22:03:33 +08:00
{
2015-01-05 01:21:35 +08:00
ChatMessage::Ptr msg = ChatMessage::Ptr(new ChatMessage);
2015-01-03 22:03:33 +08:00
2015-02-16 22:22:29 +08:00
QString text = rawMessage.toHtmlEscaped();
2015-02-15 17:51:54 +08:00
QString senderText = sender;
2019-02-21 21:53:31 +08:00
auto textType = Text::NORMAL;
// smileys
if (Settings::getInstance().getUseEmoticons())
2015-01-04 02:05:38 +08:00
text = SmileyPack::getInstance().smileyfied(text);
// quotes (green text)
text = detectQuotes(text, type);
text = highlightURI(text);
2015-01-03 22:03:33 +08:00
// text styling
Settings::StyleType styleType = Settings::getInstance().getStylePreference();
if (styleType != Settings::StyleType::NONE) {
text = applyMarkdown(text, styleType == Settings::StyleType::WITH_CHARS);
}
switch (type) {
case NORMAL:
text = wrapDiv(text, "msg");
break;
2015-02-15 17:51:54 +08:00
case ACTION:
2019-02-21 21:53:31 +08:00
textType = Text::ACTION;
2015-02-15 17:51:54 +08:00
senderText = "*";
text = wrapDiv(QString("%1 %2").arg(sender.toHtmlEscaped(), text), "action");
2015-01-03 22:03:33 +08:00
msg->setAsAction();
2015-02-15 17:51:54 +08:00
break;
case ALERT:
text = wrapDiv(text, "alert");
break;
2015-01-04 02:06:10 +08:00
}
2015-01-03 22:03:33 +08:00
2015-02-15 17:51:54 +08:00
// Note: Eliding cannot be enabled for RichText items. (QTBUG-17207)
QFont baseFont = Settings::getInstance().getChatMessageFont();
QFont authorFont = baseFont;
if (isMe)
authorFont.setBold(true);
2019-02-23 00:01:43 +08:00
QColor color = Style::getColor(Style::MainText);
refactor(chatform): Remove message handling logic from gui path Functional changes * Offline messages are still sent when the chat log is cleared * Spinner now does not wait for history to be complete, just a receipt from our friend * Export chat and load chat history are now available in group chats * Merged save chat log and export chat log * Note that we lost the info messages in the process NonFunctional Changes * FileTransferWidget slots only called for correct file * Settings::getEnableGroupChatsColor now embedded in GenericChatForm::colorizeNames * Settings::setEnableGroupChatscolor now emits signal connected to GenericChatForm::setColorizedNames to keep state in sync * Chatlog history not reloaded on setPassword() * I am pretty sure this had no purpose * Removed a lot of responsibility from ChatForm * History moved to ChatHistory implementation of IChatLog * OfflineMsgEngine moved to FriendMessageDispatcher * Export chat and load chat history moved to GenericChatLog * Backed by IChatLog so can be used generically * Message processing moved to FriendMessageDispatcher * The action of sending files to coreFile is still handled by ChatForm, but displaying of the sent messages is done through IChatLog -> GenericChatForm * Search moved to ChatHistory/SessionChatLog * All insertion of chat log elements should be handled by GenericChatForm now * Removed overlapping responsibilities from GroupChatForm * Search and message sending goes through ichatlog/messagedispatcher too * Lots of search functionality pushed down into IChatLog * Some of the file logic was moved into Widget. This is mostly to avoid scope increase of this PR even further. * History APIs removed that were no longer used
2019-05-26 08:11:44 +08:00
if (colorizeName) {
2018-10-24 06:17:06 +08:00
QByteArray hash = QCryptographicHash::hash((sender.toUtf8()), QCryptographicHash::Sha256);
quint8 *data = (quint8*)hash.data();
2019-02-21 21:53:31 +08:00
color.setHsv(data[0], 255, 196);
2019-02-21 21:53:31 +08:00
if (!isMe && textType == Text::NORMAL) {
textType = Text::CUSTOM;
2018-10-24 06:17:06 +08:00
}
}
2019-02-21 21:53:31 +08:00
msg->addColumn(new Text(senderText, authorFont, true, sender, textType, color),
ColumnFormat(NAME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));
msg->addColumn(new Text(text, baseFont, false, ((type == ACTION) && isMe)
? QString("%1 %2").arg(sender, rawMessage)
: rawMessage),
ColumnFormat(1.0, ColumnFormat::VariableSize));
msg->addColumn(new Spinner(Style::getImagePath("chatArea/spinner.svg"), QSize(16, 16), 360.0 / 1.6),
ColumnFormat(TIME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));
2015-01-03 22:03:33 +08:00
if (!date.isNull())
2015-01-03 22:03:33 +08:00
msg->markAsSent(date);
return msg;
}
ChatMessage::Ptr ChatMessage::createChatInfoMessage(const QString& rawMessage,
SystemMessageType type, const QDateTime& date)
2015-01-03 22:03:33 +08:00
{
2015-01-05 01:21:35 +08:00
ChatMessage::Ptr msg = ChatMessage::Ptr(new ChatMessage);
2015-02-16 22:22:29 +08:00
QString text = rawMessage.toHtmlEscaped();
2015-01-03 22:03:33 +08:00
2015-01-06 21:30:24 +08:00
QString img;
switch (type) {
case INFO:
img = Style::getImagePath("chatArea/info.svg");
break;
case ERROR:
img = Style::getImagePath("chatArea/error.svg");
break;
case TYPING:
img = Style::getImagePath("chatArea/typing.svg");
break;
2015-01-06 21:30:24 +08:00
}
QFont baseFont = Settings::getInstance().getChatMessageFont();
msg->addColumn(new Image(QSize(18, 18), img),
ColumnFormat(NAME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));
msg->addColumn(new Text("<b>" + text + "</b>", baseFont, false, text),
ColumnFormat(1.0, ColumnFormat::VariableSize, ColumnFormat::Left));
msg->addColumn(new Timestamp(date, Settings::getInstance().getTimestampFormat(), baseFont),
ColumnFormat(TIME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));
2015-01-03 22:03:33 +08:00
return msg;
}
ChatMessage::Ptr ChatMessage::createFileTransferMessage(const QString& sender, ToxFile file,
bool isMe, const QDateTime& date)
2015-01-03 22:03:33 +08:00
{
2015-01-05 01:21:35 +08:00
ChatMessage::Ptr msg = ChatMessage::Ptr(new ChatMessage);
2015-01-03 22:03:33 +08:00
QFont baseFont = Settings::getInstance().getChatMessageFont();
QFont authorFont = baseFont;
if (isMe)
authorFont.setBold(true);
msg->addColumn(new Text(sender, authorFont, true),
ColumnFormat(NAME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));
msg->addColumn(new ChatLineContentProxy(new FileTransferWidget(nullptr, file), 320, 0.6f),
ColumnFormat(1.0, ColumnFormat::VariableSize));
msg->addColumn(new Timestamp(date, Settings::getInstance().getTimestampFormat(), baseFont),
ColumnFormat(TIME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));
2015-01-03 22:03:33 +08:00
return msg;
}
ChatMessage::Ptr ChatMessage::createTypingNotification()
{
ChatMessage::Ptr msg = ChatMessage::Ptr(new ChatMessage);
QFont baseFont = Settings::getInstance().getChatMessageFont();
// Note: "[user]..." is just a placeholder. The actual text is set in
// ChatForm::setFriendTyping()
//
// FIXME: Due to circumstances, placeholder is being used in a case where
// user received typing notifications constantly since contact came online.
// This causes "[user]..." to be displayed in place of user nick, as long
// as user will keep typing. Issue #1280
msg->addColumn(new NotificationIcon(QSize(18, 18)),
ColumnFormat(NAME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));
msg->addColumn(new Text("[user]...", baseFont, false, ""),
ColumnFormat(1.0, ColumnFormat::VariableSize, ColumnFormat::Left));
return msg;
}
/**
* @brief Create message placeholder while chatform restructures text
*
* It can take a while for chatform to resize large amounts of text, thus
* a message placeholder is needed to inform users about it.
*
* @return created message
*/
ChatMessage::Ptr ChatMessage::createBusyNotification()
{
ChatMessage::Ptr msg = ChatMessage::Ptr(new ChatMessage);
QFont baseFont = Settings::getInstance().getChatMessageFont();
baseFont.setPixelSize(baseFont.pixelSize() + 2);
baseFont.setBold(true);
msg->addColumn(new Text(QObject::tr("Reformatting text in progress.."), baseFont, false, ""),
ColumnFormat(1.0, ColumnFormat::VariableSize, ColumnFormat::Center));
return msg;
}
void ChatMessage::markAsSent(const QDateTime& time)
2014-11-12 21:11:25 +08:00
{
QFont baseFont = Settings::getInstance().getChatMessageFont();
2014-11-12 23:45:24 +08:00
// remove the spinner and replace it by $time
replaceContent(2, new Timestamp(time, Settings::getInstance().getTimestampFormat(), baseFont));
2014-11-12 21:11:25 +08:00
}
2014-11-14 01:27:32 +08:00
QString ChatMessage::toString() const
{
2015-01-05 01:21:35 +08:00
ChatLineContent* c = getContent(1);
if (c)
2015-01-05 01:21:35 +08:00
return c->getText();
return QString();
2014-11-14 01:27:32 +08:00
}
2014-12-14 04:11:03 +08:00
bool ChatMessage::isAction() const
{
return action;
}
void ChatMessage::setAsAction()
{
action = true;
}
2015-01-03 22:03:33 +08:00
2015-01-05 01:21:35 +08:00
void ChatMessage::hideSender()
{
ChatLineContent* c = getContent(0);
if (c)
2015-01-05 01:21:35 +08:00
c->hide();
}
void ChatMessage::hideDate()
{
ChatLineContent* c = getContent(2);
if (c)
2015-01-05 01:21:35 +08:00
c->hide();
}
QString ChatMessage::detectQuotes(const QString& str, MessageType type)
2015-01-03 22:03:33 +08:00
{
// detect text quotes
QStringList messageLines = str.split("\n");
QString quotedText;
for (int i = 0; i < messageLines.size(); ++i) {
// don't quote first line in action message. This makes co-existence of
// quotes and action messages possible, since only first line can cause
// problems in case where there is quote in it used.
if (QRegExp("^(&gt;|).*").exactMatch(messageLines[i])) {
if (i > 0 || type != ACTION)
quotedText += "<span class=quote>" + messageLines[i] + " </span>";
else
quotedText += messageLines[i];
} else {
2015-01-03 22:03:33 +08:00
quotedText += messageLines[i];
}
2015-01-03 22:03:33 +08:00
if (i < messageLines.size() - 1) {
quotedText += '\n';
}
2015-01-03 22:03:33 +08:00
}
return quotedText;
}
QString ChatMessage::wrapDiv(const QString& str, const QString& div)
2015-02-15 17:51:54 +08:00
{
return QString("<p class=%1>%2</p>").arg(div, /*QChar(0x200E) + */ QString(str));
2015-02-15 17:51:54 +08:00
}