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

Merge pull request #4322

noavarice (4):
      refactor: SmileyPack refactoring
      refactor: removed meaningless 'isValid' method
      docs: added docs for SmileyPack
      refactor: returned correct code format and some other small changes
This commit is contained in:
Diadlo 2017-04-19 21:27:03 +03:00
commit 17abfe3753
No known key found for this signature in database
GPG Key ID: 5AF9F2E29107C727
3 changed files with 134 additions and 168 deletions

View File

@ -232,8 +232,9 @@ void Settings::loadGlobal()
const QString DEFAULT_SMILEYS = ":/smileys/emojione/emoticons.xml"; const QString DEFAULT_SMILEYS = ":/smileys/emojione/emoticons.xml";
smileyPack = s.value("smileyPack", DEFAULT_SMILEYS).toString(); smileyPack = s.value("smileyPack", DEFAULT_SMILEYS).toString();
if (!SmileyPack::isValid(smileyPack)) if (!QFile::exists(smileyPack)) {
smileyPack = DEFAULT_SMILEYS; smileyPack = DEFAULT_SMILEYS;
}
emojiFontPointSize = s.value("emojiFontPointSize", 24).toInt(); emojiFontPointSize = s.value("emojiFontPointSize", 24).toInt();
firstColumnHandlePos = s.value("firstColumnHandlePos", 50).toInt(); firstColumnHandlePos = s.value("firstColumnHandlePos", 50).toInt();

View File

@ -1,5 +1,5 @@
/* /*
Copyright © 2014-2015 by The qTox Project Contributors Copyright © 2014-2017 by The qTox Project Contributors
This file is part of qTox, a Qt-based graphical interface for Tox. This file is part of qTox, a Qt-based graphical interface for Tox.
@ -19,34 +19,17 @@
#include "smileypack.h" #include "smileypack.h"
#include "src/persistence/settings.h" #include "src/persistence/settings.h"
#include "src/widget/style.h"
#include <QBuffer>
#include <QBuffer>
#include <QCoreApplication>
#include <QCoreApplication>
#include <QDir> #include <QDir>
#include <QDir>
#include <QDomDocument>
#include <QDomDocument>
#include <QDomElement> #include <QDomElement>
#include <QDomElement> #include <QRegularExpression>
#include <QFile>
#include <QFileInfo>
#include <QFont>
#include <QFontInfo>
#include <QIcon>
#include <QPixmap>
#include <QStandardPaths> #include <QStandardPaths>
#include <QStringBuilder>
#include <QtConcurrent/QtConcurrentRun> #include <QtConcurrent/QtConcurrentRun>
#if defined(Q_OS_FREEBSD) #if defined(Q_OS_FREEBSD)
#include <locale.h> #include <locale.h>
#endif #endif
#define EMOTICONS_SUB_DIR QStringLiteral("emoticons")
/** /**
* @class SmileyPack * @class SmileyPack
* @brief Maps emoticons to smileys. * @brief Maps emoticons to smileys.
@ -67,7 +50,62 @@
* @brief Contains all directories where smileys could be found * @brief Contains all directories where smileys could be found
*/ */
QStringList SmileyPack::defaultPaths = loadDefaultPaths(); QStringList loadDefaultPaths();
static const QStringList DEFAULT_PATHS = loadDefaultPaths();
static const QString RICH_TEXT_PATTERN = QStringLiteral("<img title=\"%1\" src=\"key:%1\"\\>");
static const QString EMOTICONS_FILE_NAME = QStringLiteral("emoticons.xml");
/**
* @brief Construct list of standard directories with "emoticons" sub dir, whether these directories
* exist or not
* @return Constructed list of default emoticons directories
*/
QStringList loadDefaultPaths()
{
#if defined(Q_OS_FREEBSD)
// TODO: Remove when will be fixed.
// Workaround to fix https://bugreports.qt.io/browse/QTBUG-57522
setlocale(LC_ALL, "");
#endif
const QString EMOTICONS_SUB_PATH = QDir::separator() + QStringLiteral("emoticons");
QStringList paths{":/smileys", "~/.kde4/share/emoticons", "~/.kde/share/emoticons",
EMOTICONS_SUB_PATH};
// qTox exclusive emoticons
QStandardPaths::StandardLocation location;
#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
location = QStandardPaths::AppDataLocation;
#else
#warning "Qt < 5.4.0 has a trouble with unicode symbols in path on few systems"
location = QStandardPaths::DataLocation;
#endif
QStringList locations = QStandardPaths::standardLocations(location);
// system wide emoticons
locations.append(QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation));
for (QString qtoxPath : locations) {
qtoxPath.append(EMOTICONS_SUB_PATH);
if (!paths.contains(qtoxPath)) {
paths.append(qtoxPath);
}
}
return paths;
}
/**
* @brief Wraps passed string into smiley HTML image reference
* @param key Describes which smiley is needed
* @return Key that wrapped into image ref
*/
QString getAsRichText(const QString& key)
{
return RICH_TEXT_PATTERN.arg(key);
}
SmileyPack::SmileyPack() SmileyPack::SmileyPack()
{ {
@ -86,56 +124,26 @@ SmileyPack& SmileyPack::getInstance()
return smileyPack; return smileyPack;
} }
QStringList SmileyPack::loadDefaultPaths() /**
{ * @brief Does the same as listSmileyPaths, but with default paths
#if defined(Q_OS_FREEBSD) */
// TODO: Remove when will be fixed.
// Workaround to fix https://bugreports.qt.io/browse/QTBUG-57522
setlocale(LC_ALL, "");
#endif
QStringList paths =
QStringList{":/smileys", "~/.kde4/share/emoticons", "~/.kde/share/emoticons"};
// qTox should find emoticons next to the binary
paths.append('.' + QDir::separator() + EMOTICONS_SUB_DIR);
// qTox exclusive emoticons
QStandardPaths::StandardLocation location;
#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
location = QStandardPaths::AppDataLocation;
#else
#warning "Qt < 5.4.0 has a trouble with unicode symbols in path on few systems"
location = QStandardPaths::DataLocation;
#endif
for (auto qtoxPath : QStandardPaths::standardLocations(location)) {
qtoxPath += QDir::separator() + EMOTICONS_SUB_DIR;
if (!paths.contains(qtoxPath)) {
paths << qtoxPath;
}
}
// system wide emoticons
for (auto genericPath : QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation)) {
genericPath += QDir::separator() + EMOTICONS_SUB_DIR;
if (!paths.contains(genericPath)) {
paths << genericPath;
}
}
return paths;
}
QList<QPair<QString, QString>> SmileyPack::listSmileyPacks() QList<QPair<QString, QString>> SmileyPack::listSmileyPacks()
{ {
return listSmileyPacks(defaultPaths); return listSmileyPacks(DEFAULT_PATHS);
} }
/**
* @brief Searches all files called "emoticons.xml" within the every passed path in the depth of 2
* @param paths Paths where to search for file
* @return Vector of pairs: {directoryName, absolutePathToFile}
*/
QList<QPair<QString, QString>> SmileyPack::listSmileyPacks(const QStringList& paths) QList<QPair<QString, QString>> SmileyPack::listSmileyPacks(const QStringList& paths)
{ {
QList<QPair<QString, QString>> smileyPacks; QList<QPair<QString, QString>> smileyPacks;
const QString homePath = QDir::homePath();
for (QString path : paths) { for (QString path : paths) {
if (path.leftRef(1) == "~") { if (path.startsWith('~')) {
path.replace(0, 1, QDir::homePath()); path.replace(0, 1, homePath);
} }
QDir dir(path); QDir dir(path);
@ -145,14 +153,11 @@ QList<QPair<QString, QString>> SmileyPack::listSmileyPacks(const QStringList& pa
for (const QString& subdirectory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { for (const QString& subdirectory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
dir.cd(subdirectory); dir.cd(subdirectory);
if (dir.exists(EMOTICONS_FILE_NAME)) {
QFileInfoList entries = dir.entryInfoList(QStringList() << "emoticons.xml", QDir::Files); QString absPath = dir.absolutePath() + QDir::separator() + EMOTICONS_FILE_NAME;
// Does it contain a file called emoticons.xml? QPair<QString, QString> p{dir.dirName(), absPath};
if (entries.size() > 0) { if (!smileyPacks.contains(p)) {
QString packageName = dir.dirName(); smileyPacks.append(p);
QString absPath = entries[0].absoluteFilePath();
if (!smileyPacks.contains(QPair<QString, QString>(packageName, absPath))) {
smileyPacks << QPair<QString, QString>(packageName, absPath);
} }
} }
@ -163,11 +168,6 @@ QList<QPair<QString, QString>> SmileyPack::listSmileyPacks(const QStringList& pa
return smileyPacks; return smileyPacks;
} }
bool SmileyPack::isValid(const QString& filename)
{
return QFile(filename).exists();
}
/** /**
* @brief Load smile pack * @brief Load smile pack
* @note The caller must lock loadingMutex and should run it in a thread * @note The caller must lock loadingMutex and should run it in a thread
@ -176,19 +176,16 @@ bool SmileyPack::isValid(const QString& filename)
*/ */
bool SmileyPack::load(const QString& filename) bool SmileyPack::load(const QString& filename)
{ {
// discard old data
filenameTable.clear();
iconCache.clear();
emoticons.clear();
path.clear();
// open emoticons.xml
QFile xmlFile(filename); QFile xmlFile(filename);
if (!xmlFile.open(QIODevice::ReadOnly)) { if (!xmlFile.exists() || !xmlFile.open(QIODevice::ReadOnly)) {
loadingMutex.unlock(); loadingMutex.unlock();
return false; // cannot open file return false;
} }
QDomDocument doc;
doc.setContent(xmlFile.readAll());
xmlFile.close();
/* parse the cfg file /* parse the cfg file
* sample: * sample:
* <?xml version='1.0'?> * <?xml version='1.0'?>
@ -205,103 +202,80 @@ bool SmileyPack::load(const QString& filename)
*/ */
path = QFileInfo(filename).absolutePath(); path = QFileInfo(filename).absolutePath();
QDomDocument doc;
doc.setContent(xmlFile.readAll());
QDomNodeList emoticonElements = doc.elementsByTagName("emoticon"); QDomNodeList emoticonElements = doc.elementsByTagName("emoticon");
for (int i = 0; i < emoticonElements.size(); ++i) { const QString itemName = QStringLiteral("file");
QString file = emoticonElements.at(i).attributes().namedItem("file").nodeValue(); const QString childName = QStringLiteral("string");
QDomElement stringElement = emoticonElements.at(i).firstChildElement("string"); const int iconsCount = emoticonElements.size();
emoticons.clear();
QStringList emoticonSet; // { ":)", ":-)" } etc. emoticonToIcon.clear();
icons.clear();
icons.reserve(iconsCount);
for (int i = 0; i < iconsCount; ++i) {
QDomNode node = emoticonElements.at(i);
QString iconName = node.attributes().namedItem(itemName).nodeValue();
QString iconPath = QDir{path}.filePath(iconName);
icons.append(QIcon{iconPath});
QDomElement stringElement = node.firstChildElement(childName);
QStringList emoticonList;
while (!stringElement.isNull()) { while (!stringElement.isNull()) {
QString emoticon = stringElement.text().replace("<", "&lt;").replace(">", "&gt;"); QString emoticon = stringElement.text().replace("<", "&lt;").replace(">", "&gt;");
filenameTable.insert(emoticon, file); emoticonToIcon.insert(emoticon, &icons[i]);
emoticonList.append(emoticon);
cacheSmiley(file); // preload all smileys
if (!getCachedSmiley(emoticon).isNull()) {
emoticonSet.push_back(emoticon);
}
stringElement = stringElement.nextSibling().toElement(); stringElement = stringElement.nextSibling().toElement();
} }
if (emoticonSet.size() > 0) { emoticons.append(emoticonList);
emoticons.push_back(emoticonSet);
}
} }
// success!
loadingMutex.unlock(); loadingMutex.unlock();
return true; return true;
} }
QString SmileyPack::smileyfied(QString msg) /**
* @brief Replaces all found text emoticons to HTML reference with its according icon filename
* @param msg Message where to search for emoticons
* @return Formatted copy of message
*/
QString SmileyPack::smileyfied(const QString& msg)
{ {
QMutexLocker locker(&loadingMutex); QMutexLocker locker(&loadingMutex);
QString result(msg);
QRegExp exp("\\S+"); // matches words QRegularExpression exp("\\S+");
QRegularExpressionMatchIterator iter = exp.globalMatch(result);
int index = msg.indexOf(exp); int replaceDiff = 0;
while (iter.hasNext()) {
// if a word is key of a smiley, replace it by its corresponding image in Rich Text QRegularExpressionMatch match = iter.next();
while (index >= 0) { QString key = match.captured();
QString key = exp.cap(); int startPos = match.capturedStart();
if (filenameTable.contains(key)) { int keyLength = key.length();
if (emoticonToIcon.contains(key)) {
QString imgRichText = getAsRichText(key); QString imgRichText = getAsRichText(key);
result.replace(startPos + replaceDiff, keyLength, imgRichText);
msg.replace(index, key.length(), imgRichText); replaceDiff += imgRichText.length() - keyLength;
index += imgRichText.length() - key.length();
} }
index = msg.indexOf(exp, index + key.length());
} }
return msg; return result;
} }
/**
* @brief Returns all emoticons that was extracted from files, grouped by according icon file
*/
QList<QStringList> SmileyPack::getEmoticons() const QList<QStringList> SmileyPack::getEmoticons() const
{ {
QMutexLocker locker(&loadingMutex); QMutexLocker locker(&loadingMutex);
return emoticons; return emoticons;
} }
QString SmileyPack::getAsRichText(const QString& key) /**
{ * @brief Gets icon accoring to passed emoticon
return QString("<img title=\"%1\" src=\"key:%1\"\\>").arg(key); * @param emoticon Passed emoticon
} * @return Returns cached icon according to passed emoticon, null if no icon mapped to this emoticon
*/
QIcon SmileyPack::getAsIcon(const QString& key) QIcon SmileyPack::getAsIcon(const QString& emoticon)
{ {
QMutexLocker locker(&loadingMutex); QMutexLocker locker(&loadingMutex);
return getCachedSmiley(key); return emoticonToIcon.contains(emoticon) ? *(emoticonToIcon[emoticon]) : QIcon();
}
void SmileyPack::cacheSmiley(const QString& name)
{
QString filename = QDir(path).filePath(name);
QIcon icon;
icon.addFile(filename);
iconCache.insert(name, icon);
}
QIcon SmileyPack::getCachedSmiley(const QString& key)
{
// valid key?
if (!filenameTable.contains(key)) {
return QPixmap();
}
// cache it if needed
QString file = filenameTable.value(key);
if (!iconCache.contains(file)) {
cacheSmiley(file);
}
return iconCache.value(file);
} }
void SmileyPack::onSmileyPackChanged() void SmileyPack::onSmileyPackChanged()

View File

@ -1,5 +1,5 @@
/* /*
Copyright © 2014-2015 by The qTox Project Contributors Copyright © 2014-2017 by The qTox Project Contributors
This file is part of qTox, a Qt-based graphical interface for Tox. This file is part of qTox, a Qt-based graphical interface for Tox.
@ -20,12 +20,9 @@
#ifndef SMILEYPACK_H #ifndef SMILEYPACK_H
#define SMILEYPACK_H #define SMILEYPACK_H
#include <QHash>
#include <QIcon> #include <QIcon>
#include <QMap>
#include <QMutex> #include <QMutex>
#include <QObject>
#include <QString>
#include <QStringList>
class SmileyPack : public QObject class SmileyPack : public QObject
{ {
@ -35,11 +32,9 @@ public:
static SmileyPack& getInstance(); static SmileyPack& getInstance();
static QList<QPair<QString, QString>> listSmileyPacks(const QStringList& paths); static QList<QPair<QString, QString>> listSmileyPacks(const QStringList& paths);
static QList<QPair<QString, QString>> listSmileyPacks(); static QList<QPair<QString, QString>> listSmileyPacks();
static bool isValid(const QString& filename);
QString smileyfied(QString msg); QString smileyfied(const QString& msg);
QList<QStringList> getEmoticons() const; QList<QStringList> getEmoticons() const;
QString getAsRichText(const QString& key);
QIcon getAsIcon(const QString& key); QIcon getAsIcon(const QString& key);
private slots: private slots:
@ -51,15 +46,11 @@ private:
SmileyPack& operator=(const SmileyPack&) = delete; SmileyPack& operator=(const SmileyPack&) = delete;
bool load(const QString& filename); bool load(const QString& filename);
void cacheSmiley(const QString& name);
QIcon getCachedSmiley(const QString& key);
static QStringList loadDefaultPaths();
QHash<QString, QString> filenameTable; QVector<QIcon> icons;
QHash<QString, QIcon> iconCache; QMap<QString, const QIcon*> emoticonToIcon;
QList<QStringList> emoticons; QList<QStringList> emoticons;
QString path; QString path;
static QStringList defaultPaths;
mutable QMutex loadingMutex; mutable QMutex loadingMutex;
}; };