mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-17 03:18:07 +00:00
Chat - clickable links and emoji display in bubbles
This commit is contained in:
parent
32e5d5bc1d
commit
096501ee20
11 changed files with 405 additions and 12 deletions
|
|
@ -93,6 +93,9 @@ SettingsCore::SettingsCore(QObject *parent) : QObject(parent) {
|
|||
mShowAccountDevices = (accountDomain == mDefaultDomain);
|
||||
}
|
||||
|
||||
// Chat
|
||||
mEmojiFont = settingsModel->getEmojiFont();
|
||||
|
||||
// Ui
|
||||
INIT_CORE_MEMBER(DisableChatFeature, settingsModel)
|
||||
INIT_CORE_MEMBER(DisableMeetingsFeature, settingsModel)
|
||||
|
|
|
|||
|
|
@ -233,6 +233,7 @@ public:
|
|||
DECLARE_CORE_GETSET_MEMBER(QString, commandLine, CommandLine)
|
||||
DECLARE_CORE_GETSET_MEMBER(bool, disableCallForward, DisableCallForward)
|
||||
DECLARE_CORE_GETSET_MEMBER(QString, callForwardToAddress, CallForwardToAddress)
|
||||
DECLARE_CORE_GET_CONSTANT(QFont, emojiFont, EmojiFont)
|
||||
|
||||
signals:
|
||||
|
||||
|
|
|
|||
|
|
@ -729,6 +729,17 @@ void SettingsModel::setCallForwardToAddress(QString data) {
|
|||
emit(callForwardToAddressChanged(data));
|
||||
}
|
||||
|
||||
QFont SettingsModel::getEmojiFont() const {
|
||||
QString family = Utils::coreStringToAppString(mConfig->getString(
|
||||
UiSection, "emoji_font", Utils::appStringToCoreString(QFont(Constants::DefaultEmojiFont).family())));
|
||||
int pointSize = getEmojiFontSize();
|
||||
return QFont(family, pointSize);
|
||||
}
|
||||
|
||||
int SettingsModel::getEmojiFontSize() const {
|
||||
return mConfig->getInt(UiSection, "emoji_font_size", Constants::DefaultEmojiFontPointSize);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
void SettingsModel::notifyConfigReady(){
|
||||
DEFINE_NOTIFY_CONFIG_READY(disableChatFeature, DisableChatFeature)
|
||||
|
|
|
|||
|
|
@ -157,6 +157,9 @@ public:
|
|||
|
||||
static bool clearLocalLdapFriendsUponStartup(const std::shared_ptr<linphone::Config> &config);
|
||||
|
||||
QFont getEmojiFont() const;
|
||||
int getEmojiFontSize() const;
|
||||
|
||||
// UI
|
||||
DECLARE_GETSET(bool, disableChatFeature, DisableChatFeature)
|
||||
DECLARE_GETSET(bool, disableMeetingsFeature, DisableMeetingsFeature)
|
||||
|
|
|
|||
|
|
@ -91,6 +91,11 @@ public:
|
|||
Q_SIGNAL void x##Changed(); \
|
||||
type m##X;
|
||||
|
||||
#define DECLARE_CORE_GET_CONSTANT(type, x, X) \
|
||||
Q_PROPERTY(type x MEMBER m##X CONSTANT) \
|
||||
type m##X; \
|
||||
type get##X() const { return m##X;}
|
||||
|
||||
#define DECLARE_GETSET(type, x, X) \
|
||||
type get##X() const; \
|
||||
void set##X(type data); \
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ list(APPEND _LINPHONEAPP_SOURCES
|
|||
tool/Constants.cpp
|
||||
tool/EnumsToString.cpp
|
||||
tool/Utils.cpp
|
||||
tool/UriTools.cpp
|
||||
|
||||
tool/LinphoneEnums.cpp
|
||||
tool/thread/SafeSharedPointer.hpp
|
||||
|
|
|
|||
203
Linphone/tool/UriTools.cpp
Normal file
203
Linphone/tool/UriTools.cpp
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// =============================================================================
|
||||
// Library to deal with IRI and URI.
|
||||
// See:
|
||||
// IRI : https://tools.ietf.org/html/rfc3987
|
||||
// URI : https://tools.ietf.org/html/rfc3986
|
||||
// =============================================================================
|
||||
|
||||
#include "UriTools.hpp"
|
||||
|
||||
static UriTools gUriTools;
|
||||
|
||||
UriTools::UriTools() {
|
||||
initRegularExpressions();
|
||||
}
|
||||
|
||||
QVector<QPair<bool, QString>> UriTools::parseIri(const QString &text) {
|
||||
return parse(text, gUriTools.mIriRegularExpression);
|
||||
}
|
||||
|
||||
QVector<QPair<bool, QString>> UriTools::parseUri(const QString &text) {
|
||||
return parse(text, gUriTools.mUriRegularExpression);
|
||||
}
|
||||
|
||||
// Parse a text and return all lines where regex is matched or not
|
||||
QVector<QPair<bool, QString>> UriTools::parse(const QString &text, const QRegularExpression regex) {
|
||||
QVector<QPair<bool, QString>> results;
|
||||
int currentIndex = 0;
|
||||
auto match = regex.match(text);
|
||||
|
||||
for (int i = 0; i <= match.lastCapturedIndex(); ++i) {
|
||||
int startIndex = match.capturedStart(i);
|
||||
if (currentIndex != startIndex) {
|
||||
results.push_back({false, text.mid(currentIndex, startIndex - currentIndex)});
|
||||
}
|
||||
results.push_back({true, match.captured(i)});
|
||||
currentIndex = startIndex;
|
||||
}
|
||||
|
||||
if (results.size() == 0) results.push_back({false, text});
|
||||
else {
|
||||
currentIndex += results.back().second.length();
|
||||
if (currentIndex < text.size()) results.append(parse(text.mid(currentIndex), regex));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
void UriTools::initRegularExpressions() {
|
||||
// Level 0. --------------------------------------------------------------------
|
||||
QString URI_DEC_OCTET =
|
||||
QString("(?:") + "25[0-5]" + "|" + "2[0-4]\\d" + "|" + "1\\d{2}" + "|" + "[1-9]\\d" + "|" + "\\d" + ")";
|
||||
|
||||
QString URI_H16 = "[0-9A-Fa-f]{1,4}";
|
||||
QString URI_PCT_ENCODED = "%[A-Fa-f\\d]{2}";
|
||||
QString URI_PORT = "\\d*";
|
||||
QString URI_SCHEME = "[a-zA-Z][\\w+\\.\\-]*";
|
||||
QString URI_SUB_DELIMS = "[!$&\"()*+,;=]";
|
||||
QString URI_UNRESERVED = "[\\w\\._~\\-]";
|
||||
QString IRI_UCS_CHAR =
|
||||
QString("(?:") + "[\\x{00A0}-\\x{D7FF}]" + "|" + "[\\x{F900}-\\x{FDCF}]" + "|" + "[\\x{FDF0}-\\x{FFEF}]" + "|" +
|
||||
"[\\x{10000}-\\x{1FFFD}]" + "|" + "[\\x{20000}-\\x{2FFFD}]" + "|" + "[\\x{30000}-\\x{3FFFD}]" +
|
||||
//"|" + "[\\x{D800\\x{DC00}-\\x{D83F\\x{DFFD}]" + "|" + "[\\x{D840\\x{DC00}-\\x{D87F\\x{DFFD}]" + "|" +
|
||||
//"[\\x{D880\\x{DC00}-\\x{D8BF\\x{DFFD}]" +
|
||||
|
||||
"|" + "[\\x{40000}-\\x{4FFFD}]" + "|" + "[\\x{50000}-\\x{5FFFD}]" + "|" + "[\\x{60000}-\\x{6FFFD}]" +
|
||||
//"|" + "[\\x{D8C0\\x{DC00}-\\x{D8FF\\x{DFFD}]" + "|" + "[\\x{D900\\x{DC00}-\\x{D93F\\x{DFFD}]" + "|" +
|
||||
//"[\\x{D940\\x{DC00}-\\x{D97F\\x{DFFD}]" +
|
||||
|
||||
"|" + "[\\x{70000}-\\x{7FFFD}]" + "|" + "[\\x{80000}-\\x{8FFFD}]" + "|" + "[\\x{90000}-\\x{9FFFD}]" +
|
||||
//"|" + "[\\x{D980\\x{DC00}-\\x{D9BF\\x{DFFD}]" + "|" + "[\\x{D9C0\\x{DC00}-\\x{D9FF\\x{DFFD}]" + "|" +
|
||||
//"[\\x{DA00\\x{DC00}-\\x{DA3F\\x{DFFD}]" +
|
||||
|
||||
"|" + "[\\x{A0000}-\\x{AFFFD}]" + "|" + "[\\x{B0000}-\\x{BFFFD}]" + "|" + "[\\x{C0000}-\\x{CFFFD}]" +
|
||||
//"|" + "[\\x{DA40\\x{DC00}-\\x{DA7F\\x{DFFD}]" + "|" + "[\\x{DA80\\x{DC00}-\\x{DABF\\x{DFFD}]" + "|" +
|
||||
//"[\\x{DAC0\\x{DC00}-\\x{DAFF\\x{DFFD}]" +
|
||||
|
||||
"|" + "[\\x{D0000}-\\x{DFFFD}]" + "|" + "[\\x{E1000}-\\x{EFFFD}]" +
|
||||
//"|" + "[\\x{DB00\\x{DC00}-\\x{DB3F\\x{DFFD}]" + "|" + "[\\x{DB44\\x{DC00}-\\x{DB7F\\x{DFFD}]" +
|
||||
")";
|
||||
|
||||
QString IRI_PRIVATE =
|
||||
QString("(?:") + "[\\x{E000}-\\x{F8FF}]" + "|" + "[\\x{F0000}-\\x{FFFFD}]" + "|" + "[\\x{100000}-\\x{10FFFD}]" +
|
||||
//"|" + "[\\x{DBC0\\x{DC00}-\\x{DBFF\\x{DFFD}]" + "|" + "[\\x{DBC0\\x{DC00}-\\x{DBFF\\x{DFFD}]" +
|
||||
")";
|
||||
|
||||
// Level 1. --------------------------------------------------------------------
|
||||
QString URI_IPV_FUTURE = QString("v[0-9A-Fa-f]+\\.") + "(?:" + URI_UNRESERVED + URI_SUB_DELIMS + ":" + ")";
|
||||
|
||||
QString IRI_UNRESERVED = QString("(?:") + "[\\w\\._~\\-]" + "|" + IRI_UCS_CHAR + ")";
|
||||
|
||||
QString URI_IPV4_ADDRESS = URI_DEC_OCTET + "\\." + URI_DEC_OCTET + "\\." + URI_DEC_OCTET + "\\." + URI_DEC_OCTET;
|
||||
|
||||
QString URI_PCHAR = "(?:" + URI_UNRESERVED + "|" + URI_PCT_ENCODED + "|" + URI_SUB_DELIMS + "|" + "[:@]" + ")";
|
||||
|
||||
QString URI_REG_NAME = "(?:" + URI_UNRESERVED + "|" + URI_PCT_ENCODED + "|" + URI_SUB_DELIMS + ")*";
|
||||
|
||||
QString URI_USERINFO = "(?:" + URI_UNRESERVED + "|" + URI_PCT_ENCODED + "|" + URI_SUB_DELIMS + "|" + ":" + ")*";
|
||||
|
||||
// Level 2. --------------------------------------------------------------------
|
||||
|
||||
QString URI_FRAGMENT = "(?:" + URI_PCHAR + "|" + "[/?]" + ")*";
|
||||
|
||||
QString URI_LS32 = "(?:" + URI_H16 + ":" + URI_H16 + "|" + URI_IPV4_ADDRESS + ")";
|
||||
|
||||
QString URI_QUERY = "(?:" + URI_PCHAR + "|" + "[/?]" + ")*";
|
||||
|
||||
QString URI_SEGMENT = URI_PCHAR + "*";
|
||||
|
||||
QString URI_SEGMENT_NZ = URI_PCHAR + "+";
|
||||
|
||||
QString IRI_PCHAR = "(?:" + IRI_UNRESERVED + "|" + URI_PCT_ENCODED + "|" + URI_SUB_DELIMS + "|" + "[:@]" + ")";
|
||||
|
||||
QString IRI_REG_NAME = "(?:" + IRI_UNRESERVED + "|" + URI_PCT_ENCODED + "|" + URI_SUB_DELIMS + ")*";
|
||||
|
||||
QString IRI_USERINFO = "(?:" + IRI_UNRESERVED + "|" + URI_PCT_ENCODED + "|" + URI_SUB_DELIMS + "|" + ":" + ")*";
|
||||
|
||||
// Level 3. --------------------------------------------------------------------
|
||||
|
||||
QString URI_IPV6_ADDRESS = QString("(?:") + "(?:" + URI_H16 + ":){6}" + URI_LS32 + "|" + "::(?:" + URI_H16 +
|
||||
":){5}" + URI_LS32 + "|" + "\\[" + URI_H16 + "\\]::(?:" + URI_H16 + ":){4}" + URI_LS32 +
|
||||
"|" + "\\[" + "(?:" + URI_H16 + ":)?" + URI_H16 + "\\]::(?:" + URI_H16 + ":){3}" +
|
||||
URI_LS32 + "|" + "\\[" + "(?:" + URI_H16 + ":){0,2}" + URI_H16 + "\\]::(?:" + URI_H16 +
|
||||
":){2}" + URI_LS32 + "|" + "\\[" + "(?:" + URI_H16 + ":){0,3}" + URI_H16 +
|
||||
"\\]::" + URI_H16 + ":" + URI_LS32 + "|" + "\\[" + "(?:" + URI_H16 + ":){0,4}" +
|
||||
URI_H16 + "\\]::" + URI_LS32 + "|" + "\\[" + "(?:" + URI_H16 + ":){0,5}" + URI_H16 +
|
||||
"\\]::" + URI_H16 + "|" + "\\[" + "(?:" + URI_H16 + ":){0,6}" + URI_H16 + "\\]::" + ")";
|
||||
|
||||
QString URI_PATH_ABEMPTY = QString("(?:") + "/" + URI_SEGMENT + ")*";
|
||||
|
||||
QString URI_PATH_ABSOLUTE = QString("/") + "(?:" + URI_SEGMENT_NZ + "(?:" + "/" + URI_SEGMENT + ")*" + ")?";
|
||||
|
||||
QString URI_PATH_ROOTLESS = URI_SEGMENT_NZ + "(?:" + "/" + URI_SEGMENT + ")*";
|
||||
|
||||
QString IRI_FRAGMENT = "(?:" + IRI_PCHAR + "|" + "[/?]" + ")*";
|
||||
|
||||
QString IRI_QUERY = "(?:" + IRI_PCHAR + "|" + IRI_PRIVATE + "|" + "[/?]" + ")*";
|
||||
|
||||
QString IRI_SEGMENT = IRI_PCHAR + "*";
|
||||
QString IRI_SEGMENT_NZ = IRI_PCHAR + "+";
|
||||
|
||||
// Level 4. --------------------------------------------------------------------
|
||||
|
||||
QString URI_IP_LITERAL = QString("\\[") + "(?:" + URI_IPV6_ADDRESS + "|" + URI_IPV_FUTURE + ")" + "\\]";
|
||||
|
||||
QString IRI_PATH_ABEMPTY = QString("(?:") + "/" + IRI_SEGMENT + ")*";
|
||||
|
||||
QString IRI_PATH_ABSOLUTE = QString("/") + "(?:" + IRI_SEGMENT_NZ + "(?:" + "/" + IRI_SEGMENT + ")*" + ")?";
|
||||
|
||||
QString IRI_PATH_ROOTLESS = IRI_SEGMENT_NZ + "(?:" + "/" + IRI_SEGMENT + ")*";
|
||||
|
||||
// Level 5. --------------------------------------------------------------------
|
||||
|
||||
QString URI_HOST = "(?:" + URI_REG_NAME + "|" + URI_IPV4_ADDRESS + "|" + URI_IP_LITERAL + ")";
|
||||
|
||||
QString IRI_HOST = "(?:" + IRI_REG_NAME + "|" + URI_IPV4_ADDRESS + "|" + URI_IP_LITERAL + ")";
|
||||
|
||||
// Level 6. --------------------------------------------------------------------
|
||||
|
||||
QString URI_AUTHORITY = "(?:" + URI_USERINFO + "@" + ")?" + URI_HOST + "(?:" + ":" + URI_PORT + ")?";
|
||||
|
||||
QString IRI_AUTHORITY = "(?:" + IRI_USERINFO + "@" + ")?" + IRI_HOST + "(?:" + ":" + URI_PORT + ")?";
|
||||
|
||||
// Level 7. --------------------------------------------------------------------
|
||||
|
||||
// `path-empty` not used.
|
||||
QString URI_HIER_PART = QString("(?:") + "//" + URI_AUTHORITY + URI_PATH_ABEMPTY + "|" + URI_PATH_ABSOLUTE + "|" +
|
||||
URI_PATH_ROOTLESS + ")";
|
||||
QString IRI_HIER_PART = QString("(?:") + "//" + IRI_AUTHORITY + IRI_PATH_ABEMPTY + "|" + IRI_PATH_ABSOLUTE + "|" +
|
||||
IRI_PATH_ROOTLESS + ")";
|
||||
|
||||
// Level 8. --------------------------------------------------------------------
|
||||
|
||||
// Regex to match URI. It respects the RFC 3986.
|
||||
QString URI = "(?:" + URI_SCHEME + ":" + "|" + "www\\." + ")" + URI_HIER_PART + "(?:" + "\\?" + URI_QUERY + ")?" +
|
||||
"(?:" + "#" + URI_FRAGMENT + ")?";
|
||||
|
||||
// Regex to match URI. It respects the RFC 3987.
|
||||
QString IRI = "(?:" + URI_SCHEME + ":" + "|" + "www\\." + ")" + IRI_HIER_PART + "(?:" + "\\?" + IRI_QUERY + ")?" +
|
||||
"(?:" + "#" + IRI_FRAGMENT + ")?";
|
||||
|
||||
mIriRegularExpression = QRegularExpression(IRI, QRegularExpression::CaseInsensitiveOption |
|
||||
QRegularExpression::UseUnicodePropertiesOption);
|
||||
mUriRegularExpression = QRegularExpression(URI, QRegularExpression::CaseInsensitiveOption |
|
||||
QRegularExpression::UseUnicodePropertiesOption);
|
||||
}
|
||||
54
Linphone/tool/UriTools.hpp
Normal file
54
Linphone/tool/UriTools.hpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// =============================================================================
|
||||
// Library to deal with IRI and URI.
|
||||
// See:
|
||||
// IRI : https://tools.ietf.org/html/rfc3987
|
||||
// NOTE : Unicodes after \uFFFF are not supported by the QML RegExp (or the right syntax has not been found) : "Invalid
|
||||
// regular expression" (even with surrogate pairs). Parts have been commented out for latter use.
|
||||
// URI : https://tools.ietf.org/html/rfc3986
|
||||
// =============================================================================
|
||||
|
||||
#ifndef URI_TOOLS_H
|
||||
#define URI_TOOLS_H
|
||||
|
||||
#include <QPair>
|
||||
#include <QRegularExpression>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
class UriTools {
|
||||
public:
|
||||
UriTools();
|
||||
bool mSupportUrl = true;
|
||||
|
||||
static QVector<QPair<bool, QString>> parseIri(const QString &text);
|
||||
static QVector<QPair<bool, QString>> parseUri(const QString &text);
|
||||
static QRegularExpression getRegularExpression();
|
||||
|
||||
private:
|
||||
void initRegularExpressions();
|
||||
static QVector<QPair<bool, QString>> parse(const QString &text, const QRegularExpression regex);
|
||||
|
||||
QRegularExpression mIriRegularExpression; // https://tools.ietf.org/html/rfc3987
|
||||
QRegularExpression mUriRegularExpression; // https://tools.ietf.org/html/rfc3986
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "Utils.hpp"
|
||||
|
||||
#include "UriTools.hpp"
|
||||
#include "core/App.hpp"
|
||||
#include "core/call/CallGui.hpp"
|
||||
#include "core/chat/ChatCore.hpp"
|
||||
|
|
@ -1749,3 +1750,94 @@ QString Utils::getPresenceStatus(LinphoneEnums::Presence presence) {
|
|||
}
|
||||
return presenceStatus;
|
||||
}
|
||||
|
||||
QString Utils::encodeTextToQmlRichFormat(const QString &text, const QVariantMap &options) {
|
||||
/*QString images;
|
||||
QStringList imageFormat;
|
||||
for(auto format : QImageReader::supportedImageFormats())
|
||||
imageFormat.append(QString::fromLatin1(format).toUpper());
|
||||
*/
|
||||
QStringList formattedText;
|
||||
bool lastWasUrl = false;
|
||||
|
||||
if (options.contains("noLink") && options["noLink"].toBool()) {
|
||||
formattedText.append(encodeEmojiToQmlRichFormat(text));
|
||||
} else {
|
||||
auto primaryColor = getDefaultStyleColor("info_500_main");
|
||||
auto iriParsed = UriTools::parseIri(text);
|
||||
|
||||
for (int i = 0; i < iriParsed.size(); ++i) {
|
||||
QString iri = iriParsed[i]
|
||||
.second.replace('&', "&")
|
||||
.replace('<', "\u2063<")
|
||||
.replace('>', "\u2063>")
|
||||
.replace('"', """)
|
||||
.replace('\'', "'");
|
||||
if (!iriParsed[i].first) {
|
||||
if (lastWasUrl) {
|
||||
lastWasUrl = false;
|
||||
if (iri.front() != ' ') iri.push_front(' ');
|
||||
}
|
||||
formattedText.append(encodeEmojiToQmlRichFormat(iri));
|
||||
} else {
|
||||
QString uri =
|
||||
iriParsed[i].second.left(3) == "www" ? "http://" + iriParsed[i].second : iriParsed[i].second;
|
||||
/* TODO : preview from link
|
||||
int extIndex = iriParsed[i].second.lastIndexOf('.');
|
||||
QString ext;
|
||||
if( extIndex >= 0)
|
||||
ext = iriParsed[i].second.mid(extIndex+1).toUpper();
|
||||
if(imageFormat.contains(ext.toLatin1())){// imagesHeight is not used because of bugs on display (blank
|
||||
image if set without width) images += "<a href=\"" + uri + "\"><img" + ( options.contains("imagesWidth")
|
||||
? QString(" width='") + options["imagesWidth"].toString() + "'"
|
||||
: ""
|
||||
) + (
|
||||
options.contains("imagesWidth")
|
||||
? QString(" height='auto'")
|
||||
: ""
|
||||
) + " src=\"" + iriParsed[i].second + "\" />"+uri+"</a>";
|
||||
}else{
|
||||
*/
|
||||
formattedText.append("<a style=\"color:" + primaryColor.name() + ";\" href=\"" + uri + "\">" + iri +
|
||||
"</a>");
|
||||
lastWasUrl = true;
|
||||
/*}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lastWasUrl && formattedText.last().back() != ' ') {
|
||||
formattedText.push_back(" ");
|
||||
}
|
||||
return "<p style=\"white-space:pre-wrap;\">" + formattedText.join("") + "</p>";
|
||||
}
|
||||
|
||||
QString Utils::encodeEmojiToQmlRichFormat(const QString &body) {
|
||||
QString fmtBody = "";
|
||||
QVector<uint> utf32_string = body.toUcs4();
|
||||
|
||||
bool insideFontBlock = false;
|
||||
for (auto &code : utf32_string) {
|
||||
if (Utils::codepointIsEmoji(code)) {
|
||||
if (!insideFontBlock) {
|
||||
auto font = App::getInstance()->getSettings()->getEmojiFont().family();
|
||||
fmtBody += QString("<font face=\"" + font + "\">");
|
||||
insideFontBlock = true;
|
||||
}
|
||||
} else {
|
||||
if (insideFontBlock) {
|
||||
fmtBody += "</font>";
|
||||
insideFontBlock = false;
|
||||
}
|
||||
}
|
||||
fmtBody += QString::fromUcs4(reinterpret_cast<const char32_t *>(&code), 1);
|
||||
}
|
||||
if (insideFontBlock) {
|
||||
fmtBody += "</font>";
|
||||
}
|
||||
return fmtBody;
|
||||
}
|
||||
|
||||
bool Utils::codepointIsEmoji(uint code) {
|
||||
return (code >= 0x2600 && code <= 0x27bf) || (code >= 0x2b00 && code <= 0x2bff) ||
|
||||
(code >= 0x1f000 && code <= 0x1faff) || code == 0x200d || code == 0xfe0f;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,6 +149,11 @@ public:
|
|||
Q_INVOKABLE static VariantObject *getCurrentCallChat(CallGui *call);
|
||||
Q_INVOKABLE static VariantObject *getChatForAddress(QString address);
|
||||
Q_INVOKABLE static bool isEmptyMessage(QString message);
|
||||
Q_INVOKABLE static QString encodeTextToQmlRichFormat(const QString &text,
|
||||
const QVariantMap &options = QVariantMap());
|
||||
Q_INVOKABLE static QString encodeEmojiToQmlRichFormat(const QString &body);
|
||||
static bool codepointIsEmoji(uint code);
|
||||
|
||||
// QDir findDirectoryByName(QString startPath, QString name);
|
||||
|
||||
static QString getApplicationProduct();
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@ Control.Control {
|
|||
property bool isRemoteMessage: chatMessage? chatMessage.core.isRemoteMessage : false
|
||||
property bool isFromChatGroup: chatMessage? chatMessage.core.isFromChatGroup : false
|
||||
property var msgState: chatMessage ? chatMessage.core.messageState : LinphoneEnums.ChatMessageState.StateIdle
|
||||
property string processedText: UtilsCpp.encodeTextToQmlRichFormat(modelData.core.text)
|
||||
hoverEnabled: true
|
||||
property bool linkHovered: false
|
||||
|
||||
signal messageDeletionRequested()
|
||||
|
||||
|
|
@ -75,6 +77,7 @@ Control.Control {
|
|||
optionsMenu.open()
|
||||
}
|
||||
}
|
||||
cursorShape: mainItem.linkHovered ? Qt.PointingHandCursor : Qt.IBeamCursor
|
||||
}
|
||||
|
||||
background: Item {
|
||||
|
|
@ -107,18 +110,30 @@ Control.Control {
|
|||
visible: mainItem.imgUrl != undefined
|
||||
id: contentimage
|
||||
}
|
||||
Text {
|
||||
visible: modelData.core.text != undefined
|
||||
text: modelData.core.text
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
horizontalAlignment: modelData.core.isRemoteMessage ? Text.AlignLeft : Text.AlignRight
|
||||
color: DefaultStyle.main2_700
|
||||
font {
|
||||
pixelSize: Typography.p1.pixelSize
|
||||
weight: Typography.p1.weight
|
||||
}
|
||||
}
|
||||
Text {
|
||||
id: textElement
|
||||
visible: mainItem.processedText !== ""
|
||||
text: mainItem.processedText
|
||||
textFormat: Text.RichText
|
||||
wrapMode: Text.Wrap
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
horizontalAlignment: modelData.core.isRemoteMessage ? Text.AlignLeft : Text.AlignRight
|
||||
color: DefaultStyle.main2_700
|
||||
font {
|
||||
pixelSize: Typography.p1.pixelSize
|
||||
weight: Typography.p1.weight
|
||||
}
|
||||
onLinkActivated: {
|
||||
if (link.startsWith('sip'))
|
||||
UtilsCpp.createCall(link)
|
||||
else
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
onHoveredLinkChanged: {
|
||||
mainItem.linkHovered = hoveredLink !== ""
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Text {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue