qTox  Version: nightly | Commit: bc751c8e1cac455f9690654fcfe0f560d2d7dfdd
chatmessage.cpp
Go to the documentation of this file.
1 /*
2  Copyright © 2014-2019 by The qTox Project Contributors
3 
4  This file is part of qTox, a Qt-based graphical interface for Tox.
5 
6  qTox is libre software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  qTox is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with qTox. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "chatmessage.h"
21 #include "chatlinecontentproxy.h"
22 #include "textformatter.h"
24 #include "content/image.h"
26 #include "content/spinner.h"
27 #include "content/text.h"
28 #include "content/timestamp.h"
29 #include "content/broken.h"
30 #include "src/widget/style.h"
32 
33 #include <QDebug>
34 #include <QCryptographicHash>
35 
39 
40 #define NAME_COL_WIDTH 90.0
41 #define TIME_COL_WIDTH 90.0
42 
43 
45 {
46 }
47 
48 ChatMessage::~ChatMessage() = default;
49 
50 ChatMessage::Ptr ChatMessage::createChatMessage(const QString& sender, const QString& rawMessage,
51  MessageType type, bool isMe, MessageState state,
52  const QDateTime& date, bool colorizeName)
53 {
55 
56  QString text = rawMessage.toHtmlEscaped();
57  QString senderText = sender;
58 
59  auto textType = Text::NORMAL;
60  // smileys
61  if (Settings::getInstance().getUseEmoticons())
62  text = SmileyPack::getInstance().smileyfied(text);
63 
64  // quotes (green text)
65  text = detectQuotes(text, type);
66  text = highlightURI(text);
67 
68  // text styling
70  if (styleType != Settings::StyleType::NONE) {
71  text = applyMarkdown(text, styleType == Settings::StyleType::WITH_CHARS);
72  }
73 
74 
75  switch (type) {
76  case NORMAL:
77  text = wrapDiv(text, "msg");
78  break;
79  case ACTION:
80  textType = Text::ACTION;
81  senderText = "*";
82  text = wrapDiv(QString("%1 %2").arg(sender.toHtmlEscaped(), text), "action");
83  msg->setAsAction();
84  break;
85  case ALERT:
86  text = wrapDiv(text, "alert");
87  break;
88  }
89 
90  // Note: Eliding cannot be enabled for RichText items. (QTBUG-17207)
91  QFont baseFont = Settings::getInstance().getChatMessageFont();
92  QFont authorFont = baseFont;
93  if (isMe)
94  authorFont.setBold(true);
95 
96  QColor color = Style::getColor(Style::MainText);
97  if (colorizeName) {
98  QByteArray hash = QCryptographicHash::hash((sender.toUtf8()), QCryptographicHash::Sha256);
99  float lightness = color.lightnessF();
100  // Adapt as good as possible to Light/Dark themes
101  lightness = lightness*0.5 + 0.3;
102 
103  // Magic values
104  color.setHslF(Identicon::bytesToColor(hash.left(Identicon::IDENTICON_COLOR_BYTES)), 1.0, lightness);
105 
106  if (!isMe && textType == Text::NORMAL) {
107  textType = Text::CUSTOM;
108  }
109  }
110 
111  msg->addColumn(new Text(senderText, authorFont, true, sender, textType, color),
113  msg->addColumn(new Text(text, baseFont, false, ((type == ACTION) && isMe)
114  ? QString("%1 %2").arg(sender, rawMessage)
115  : rawMessage),
117 
118  switch (state) {
120  msg->addColumn(new Timestamp(date, Settings::getInstance().getTimestampFormat(), baseFont),
122  break;
124  msg->addColumn(new Spinner(Style::getImagePath("chatArea/spinner.svg"), QSize(16, 16), 360.0 / 1.6),
126  break;
128  msg->addColumn(new Broken(Style::getImagePath("chatArea/error.svg"), QSize(16, 16)),
130  break;
131  }
132  return msg;
133 }
134 
136  SystemMessageType type, const QDateTime& date)
137 {
139  QString text = rawMessage.toHtmlEscaped();
140 
141  QString img;
142  switch (type) {
143  case INFO:
144  img = Style::getImagePath("chatArea/info.svg");
145  break;
146  case ERROR:
147  img = Style::getImagePath("chatArea/error.svg");
148  break;
149  case TYPING:
150  img = Style::getImagePath("chatArea/typing.svg");
151  break;
152  }
153 
154  QFont baseFont = Settings::getInstance().getChatMessageFont();
155 
156  msg->addColumn(new Image(QSize(18, 18), img),
158  msg->addColumn(new Text("<b>" + text + "</b>", baseFont, false, text),
160  msg->addColumn(new Timestamp(date, Settings::getInstance().getTimestampFormat(), baseFont),
162 
163  return msg;
164 }
165 
167  ToxFile file, bool isMe, const QDateTime& date)
168 {
170 
171  QFont baseFont = Settings::getInstance().getChatMessageFont();
172  QFont authorFont = baseFont;
173  if (isMe) {
174  authorFont.setBold(true);
175  }
176 
177  msg->addColumn(new Text(sender, authorFont, true),
179  msg->addColumn(new ChatLineContentProxy(new FileTransferWidget(nullptr, coreFile, file), 320, 0.6f),
181  msg->addColumn(new Timestamp(date, Settings::getInstance().getTimestampFormat(), baseFont),
183 
184  return msg;
185 }
186 
188 {
190 
191  QFont baseFont = Settings::getInstance().getChatMessageFont();
192 
193  // Note: "[user]..." is just a placeholder. The actual text is set in
194  // ChatForm::setFriendTyping()
195  //
196  // FIXME: Due to circumstances, placeholder is being used in a case where
197  // user received typing notifications constantly since contact came online.
198  // This causes "[user]..." to be displayed in place of user nick, as long
199  // as user will keep typing. Issue #1280
200  msg->addColumn(new NotificationIcon(QSize(18, 18)),
202  msg->addColumn(new Text("[user]...", baseFont, false, ""),
204 
205  return msg;
206 }
207 
217 {
219  QFont baseFont = Settings::getInstance().getChatMessageFont();
220  baseFont.setPixelSize(baseFont.pixelSize() + 2);
221  baseFont.setBold(true);
222 
223  msg->addColumn(new Text(QObject::tr("Reformatting text...", "Waiting for text to be reformatted"), baseFont, false, ""),
225 
226  return msg;
227 }
228 
229 void ChatMessage::markAsDelivered(const QDateTime& time)
230 {
231  QFont baseFont = Settings::getInstance().getChatMessageFont();
232 
233  // remove the spinner and replace it by $time
234  replaceContent(2, new Timestamp(time, Settings::getInstance().getTimestampFormat(), baseFont));
235 }
236 
238 {
239  replaceContent(2, new Broken(Style::getImagePath("chatArea/error.svg"), QSize(16, 16)));
240 }
241 
242 QString ChatMessage::toString() const
243 {
244  ChatLineContent* c = getContent(1);
245  if (c)
246  return c->getText();
247 
248  return QString();
249 }
250 
252 {
253  return action;
254 }
255 
257 {
258  action = true;
259 }
260 
262 {
263  ChatLineContent* c = getContent(0);
264  if (c)
265  c->hide();
266 }
267 
269 {
270  ChatLineContent* c = getContent(2);
271  if (c)
272  c->hide();
273 }
274 
275 QString ChatMessage::detectQuotes(const QString& str, MessageType type)
276 {
277  // detect text quotes
278  QStringList messageLines = str.split("\n");
279  QString quotedText;
280  for (int i = 0; i < messageLines.size(); ++i) {
281  // don't quote first line in action message. This makes co-existence of
282  // quotes and action messages possible, since only first line can cause
283  // problems in case where there is quote in it used.
284  if (QRegExp("^(&gt;|>).*").exactMatch(messageLines[i])) {
285  if (i > 0 || type != ACTION)
286  quotedText += "<span class=quote>" + messageLines[i] + " </span>";
287  else
288  quotedText += messageLines[i];
289  } else {
290  quotedText += messageLines[i];
291  }
292 
293  if (i < messageLines.size() - 1) {
294  quotedText += '\n';
295  }
296  }
297 
298  return quotedText;
299 }
300 
301 QString ChatMessage::wrapDiv(const QString& str, const QString& div)
302 {
303  return QString("<p class=%1>%2</p>").arg(div, /*QChar(0x200E) + */ QString(str));
304 }
ColumnFormat::Right
@ Right
Definition: chatline.h:45
Identicon::bytesToColor
static float bytesToColor(QByteArray bytes)
Converts a series of IDENTICON_COLOR_BYTES bytes to a value in the range 0.0..1.0.
Definition: identicon.cpp:95
style.h
FileTransferWidget
Definition: filetransferwidget.h:37
history.h
textformatter.h
ColumnFormat::Center
@ Center
Definition: chatline.h:44
ChatMessage::~ChatMessage
~ChatMessage()
ColumnFormat
Definition: chatline.h:33
Timestamp
Definition: timestamp.h:28
settings.h
ChatMessage::hideSender
void hideSender()
Definition: chatmessage.cpp:261
Text::ACTION
@ ACTION
Definition: text.h:37
ChatMessage::ERROR
@ ERROR
Definition: chatmessage.h:39
NAME_COL_WIDTH
#define NAME_COL_WIDTH
Definition: chatmessage.cpp:40
Settings::getChatMessageFont
const QFont & getChatMessageFont() const
Definition: settings.cpp:1420
ChatLine::replaceContent
void replaceContent(int col, ChatLineContent *lineContent)
Definition: chatline.cpp:146
spinner.h
HistMessageContentType::file
@ file
ChatMessage::TYPING
@ TYPING
Definition: chatmessage.h:40
ChatMessage::toString
QString toString() const
Definition: chatmessage.cpp:242
ChatMessage::Ptr
std::shared_ptr< ChatMessage > Ptr
Definition: chatmessage.h:34
ChatMessage::action
bool action
Definition: chatmessage.h:79
applyMarkdown
QString applyMarkdown(const QString &message, bool showFormattingSymbols)
ChatMessage::ChatMessage
ChatMessage()
Definition: chatmessage.cpp:44
ChatLine::getContent
ChatLineContent * getContent(int col) const
Definition: chatline.cpp:50
ColumnFormat::Left
@ Left
Definition: chatline.h:43
broken.h
Image
Definition: image.h:26
Spinner
Definition: spinner.h:30
Style::getImagePath
static const QString getImagePath(const QString &filename)
Definition: style.cpp:182
Settings::StyleType
StyleType
Definition: settings.h:134
ChatMessage::MessageType
MessageType
Definition: chatmessage.h:43
ChatMessage::ALERT
@ ALERT
Definition: chatmessage.h:47
ChatMessage::markAsBroken
void markAsBroken()
Definition: chatmessage.cpp:237
ChatMessage::NORMAL
@ NORMAL
Definition: chatmessage.h:45
smileypack.h
SmileyPack::getInstance
static SmileyPack & getInstance()
Returns the singleton instance.
Definition: smileypack.cpp:147
filetransferwidget.h
Broken
Definition: broken.h:27
ColumnFormat::FixedSize
@ FixedSize
Definition: chatline.h:37
ChatMessage::createFileTransferMessage
static ChatMessage::Ptr createFileTransferMessage(const QString &sender, CoreFile &coreFile, ToxFile file, bool isMe, const QDateTime &date)
Definition: chatmessage.cpp:166
ChatMessage::ACTION
@ ACTION
Definition: chatmessage.h:46
Settings::getStylePreference
StyleType getStylePreference() const
Definition: settings.cpp:1507
SmileyPack::smileyfied
QString smileyfied(const QString &msg)
Replaces all found text emoticons to HTML reference with its according icon filename.
Definition: smileypack.cpp:303
text.h
Text
Definition: text.h:29
image.h
ChatMessage::setAsAction
void setAsAction()
Definition: chatmessage.cpp:256
ChatLineContent
Definition: chatlinecontent.h:26
chatlinecontentproxy.h
Style::getColor
static QColor getColor(ColorPalette entry)
Definition: style.cpp:209
Settings::getInstance
static Settings & getInstance()
Returns the singleton instance.
Definition: settings.cpp:88
ChatLineContentProxy
Definition: chatlinecontentproxy.h:27
Text::CUSTOM
@ CUSTOM
Definition: text.h:38
ChatMessage::isAction
bool isAction() const
Definition: chatmessage.cpp:251
ChatMessage::markAsDelivered
void markAsDelivered(const QDateTime &time)
Definition: chatmessage.cpp:229
Text::NORMAL
@ NORMAL
Definition: text.h:36
ColumnFormat::VariableSize
@ VariableSize
Definition: chatline.h:38
ChatLineContent::getText
virtual QString getText() const
Definition: chatlinecontent.cpp:90
MessageState::complete
@ complete
ChatMessage::INFO
@ INFO
Definition: chatmessage.h:38
Style::MainText
@ MainText
Definition: style.h:37
Settings::StyleType::NONE
@ NONE
TIME_COL_WIDTH
#define TIME_COL_WIDTH
Definition: chatmessage.cpp:41
ChatMessage::detectQuotes
static QString detectQuotes(const QString &str, MessageType type)
Definition: chatmessage.cpp:275
MessageState
MessageState
Definition: history.h:128
ChatMessage::createBusyNotification
static ChatMessage::Ptr createBusyNotification()
Create message placeholder while chatform restructures text.
Definition: chatmessage.cpp:216
ChatMessage::SystemMessageType
SystemMessageType
Definition: chatmessage.h:36
NotificationIcon
Definition: notificationicon.h:28
chatmessage.h
ChatMessage::createTypingNotification
static ChatMessage::Ptr createTypingNotification()
Definition: chatmessage.cpp:187
ChatMessage
Definition: chatmessage.h:31
ChatMessage::wrapDiv
static QString wrapDiv(const QString &str, const QString &div)
Definition: chatmessage.cpp:301
highlightURI
QString highlightURI(const QString &message)
ChatMessage::hideDate
void hideDate()
Definition: chatmessage.cpp:268
ToxFile
Definition: toxfile.h:32
MessageState::broken
@ broken
timestamp.h
CoreFile
Manages the file transfer service of toxcore.
Definition: corefile.h:46
Identicon::IDENTICON_COLOR_BYTES
static constexpr int IDENTICON_COLOR_BYTES
Definition: identicon.h:34
notificationicon.h
identicon.h
Settings::StyleType::WITH_CHARS
@ WITH_CHARS
ChatMessage::createChatMessage
static ChatMessage::Ptr createChatMessage(const QString &sender, const QString &rawMessage, MessageType type, bool isMe, MessageState state, const QDateTime &date, bool colorizeName=false)
Definition: chatmessage.cpp:50
MessageState::pending
@ pending
ChatMessage::createChatInfoMessage
static ChatMessage::Ptr createChatInfoMessage(const QString &rawMessage, SystemMessageType type, const QDateTime &date)
Definition: chatmessage.cpp:135