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

Merge pull request #175 from krepa098/smileys

Smileys
This commit is contained in:
Tux3 / Mlkj / !Lev.uXFMLA 2014-07-31 18:04:35 +02:00
commit 2a04fcfcf9
6 changed files with 156 additions and 43 deletions

View File

@ -34,11 +34,36 @@ SmileyPack& SmileyPack::getInstance()
return smileyPack; return smileyPack;
} }
QList<QPair<QString, QString> > SmileyPack::listSmileyPacks(const QString &path)
{
QList<QPair<QString, QString> > smileyPacks;
QDir dir(path);
foreach (const QString& subdirectory, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
{
dir.cd(subdirectory);
QFileInfoList entries = dir.entryInfoList(QStringList() << "emoticons.xml", QDir::Files);
if (entries.size() > 0) // does it contain a file called emoticons.xml?
{
QString packageName = dir.dirName();
QString relPath = QDir(QCoreApplication::applicationDirPath()).relativeFilePath(entries[0].absoluteFilePath());
smileyPacks << QPair<QString, QString>(packageName, relPath);
}
dir.cdUp();
}
return smileyPacks;
}
bool SmileyPack::load(const QString& filename) bool SmileyPack::load(const QString& filename)
{ {
// discard old data // discard old data
assignmentTable.clear(); filenameTable.clear();
cache.clear(); imgCache.clear();
emoticons.clear();
path.clear();
// open emoticons.xml // open emoticons.xml
QFile xmlFile(filename); QFile xmlFile(filename);
@ -60,6 +85,8 @@ bool SmileyPack::load(const QString& filename)
* </messaging-emoticon-map> * </messaging-emoticon-map>
*/ */
path = QFileInfo(filename).absolutePath();
QDomDocument doc; QDomDocument doc;
doc.setContent(xmlFile.readAll()); doc.setContent(xmlFile.readAll());
@ -69,42 +96,39 @@ bool SmileyPack::load(const QString& filename)
QString file = emoticonElements.at(i).attributes().namedItem("file").nodeValue(); QString file = emoticonElements.at(i).attributes().namedItem("file").nodeValue();
QDomElement stringElement = emoticonElements.at(i).firstChildElement("string"); QDomElement stringElement = emoticonElements.at(i).firstChildElement("string");
QStringList emoticonSet; // { ":)", ":-)" } etc.
while (!stringElement.isNull()) while (!stringElement.isNull())
{ {
QString rune = stringElement.text(); QString emoticon = stringElement.text();
assignmentTable.insert(rune, file); filenameTable.insert(emoticon, file);
emoticonSet.push_back(emoticon);
cacheSmiley(file); // preload all smileys
stringElement = stringElement.nextSibling().toElement(); stringElement = stringElement.nextSibling().toElement();
} }
emoticons.push_back(emoticonSet);
} }
path = QFileInfo(filename).absolutePath();
// success! // success!
return true; return true;
} }
QString SmileyPack::replaceEmoticons(QString msg) QString SmileyPack::smileyfied(QString msg)
{ {
QRegExp exp("\\S+"); // matches words QRegExp exp("\\S+"); // matches words
int index = msg.indexOf(exp); int index = msg.indexOf(exp);
int offset = 0;
// if a word is key of a smiley, replace it by its corresponding image in Rich Text // if a word is key of a smiley, replace it by its corresponding image in Rich Text
while (index >= 0) while (index >= 0)
{ {
QString key = exp.cap(); QString key = exp.cap();
if (assignmentTable.contains(key)) if (filenameTable.contains(key))
{ {
QString file = assignmentTable[key]; QString imgRichText = getAsRichText(key);
if (!cache.contains(file)) {
loadSmiley(file);
}
QString imgRichText = "<img src=\"data:image/png;base64," % cache[file] % "\">"; msg.replace(index, key.length(), imgRichText);
msg.replace(index + offset, key.length(), imgRichText);
index += imgRichText.length() - key.length(); index += imgRichText.length() - key.length();
} }
index = msg.indexOf(exp, index + key.length()); index = msg.indexOf(exp, index + key.length());
@ -113,7 +137,25 @@ QString SmileyPack::replaceEmoticons(QString msg)
return msg; return msg;
} }
void SmileyPack::loadSmiley(const QString &name) QList<QStringList> SmileyPack::getEmoticons() const
{
return emoticons;
}
QString SmileyPack::getAsRichText(const QString &key)
{
return "<img src=\"data:image/png;base64," % QString(getCachedSmiley(key).toBase64()) % "\">";
}
QIcon SmileyPack::getAsIcon(const QString &key)
{
QPixmap pm;
pm.loadFromData(getCachedSmiley(key), "PNG");
return QIcon(pm);
}
void SmileyPack::cacheSmiley(const QString &name)
{ {
QSize size(16, 16); // TODO: adapt to text size QSize size(16, 16); // TODO: adapt to text size
QString filename = path % '/' % name; QString filename = path % '/' % name;
@ -127,10 +169,25 @@ void SmileyPack::loadSmiley(const QString &name)
QBuffer buffer(&scaledImgData); QBuffer buffer(&scaledImgData);
scaledImg.save(&buffer, "PNG"); scaledImg.save(&buffer, "PNG");
cache.insert(name, scaledImgData.toBase64()); imgCache.insert(name, scaledImgData);
} }
} }
QByteArray SmileyPack::getCachedSmiley(const QString &key)
{
// valid key?
if (!filenameTable.contains(key))
return QByteArray();
// cache it if needed
QString file = filenameTable.value(key);
if (!imgCache.contains(file)) {
cacheSmiley(file);
}
return imgCache.value(file);
}
void SmileyPack::onSmileyPackChanged() void SmileyPack::onSmileyPackChanged()
{ {
load(Settings::getInstance().getSmileyPack()); load(Settings::getInstance().getSmileyPack());

View File

@ -18,8 +18,9 @@
#define SMILEYPACK_H #define SMILEYPACK_H
#include <QHash> #include <QHash>
#include <QString>
#include <QObject> #include <QObject>
#include <QString>
#include <QStringList>
//maps emoticons to smileys //maps emoticons to smileys
class SmileyPack : public QObject class SmileyPack : public QObject
@ -27,9 +28,13 @@ class SmileyPack : public QObject
Q_OBJECT Q_OBJECT
public: public:
static SmileyPack& getInstance(); static SmileyPack& getInstance();
static QList<QPair<QString, QString>> listSmileyPacks(const QString& path);
bool load(const QString &filename); bool load(const QString &filename);
QString replaceEmoticons(QString msg); QString smileyfied(QString msg);
QList<QStringList> getEmoticons() const;
QString getAsRichText(const QString& key);
QIcon getAsIcon(const QString& key);
private slots: private slots:
void onSmileyPackChanged(); void onSmileyPackChanged();
@ -39,11 +44,13 @@ private:
SmileyPack(SmileyPack&) = delete; SmileyPack(SmileyPack&) = delete;
SmileyPack& operator=(const SmileyPack&) = delete; SmileyPack& operator=(const SmileyPack&) = delete;
void loadSmiley(const QString& name); void cacheSmiley(const QString& name);
QByteArray getCachedSmiley(const QString& key);
QHash<QString, QString> assignmentTable; // matches an emoticon to its corresponding smiley QHash<QString, QString> filenameTable; // matches an emoticon to its corresponding smiley ie. ":)" -> "happy.png"
QHash<QString, QString> cache; // base64 representation of a smiley QHash<QString, QByteArray> imgCache; // (scaled) representation of a smiley ie. "happy.png" -> data
QString path; // directory containing the cfg file QList<QStringList> emoticons; // {{ ":)", ":-)" }, {":(", ...}, ... }
QString path; // directory containing the cfg and image files
}; };
#endif // SMILEYPACK_H #endif // SMILEYPACK_H

View File

@ -25,6 +25,8 @@
#include <QScrollBar> #include <QScrollBar>
#include <QFileDialog> #include <QFileDialog>
#include <QMenu> #include <QMenu>
#include <QWidgetAction>
#include <QGridLayout>
ChatForm::ChatForm(Friend* chatFriend) ChatForm::ChatForm(Friend* chatFriend)
: f(chatFriend), curRow{0}, lockSliderToBottom{true} : f(chatFriend), curRow{0}, lockSliderToBottom{true}
@ -188,6 +190,7 @@ ChatForm::ChatForm(Friend* chatFriend)
connect(msgEdit, SIGNAL(enterPressed()), this, SLOT(onSendTriggered())); connect(msgEdit, SIGNAL(enterPressed()), this, SLOT(onSendTriggered()));
connect(chatArea->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(onSliderRangeChanged())); connect(chatArea->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(onSliderRangeChanged()));
connect(chatArea, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint))); connect(chatArea, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
connect(emoteButton, SIGNAL(clicked()), this, SLOT(onEmoteButtonClicked()));
} }
ChatForm::~ChatForm() ChatForm::~ChatForm()
@ -239,7 +242,7 @@ void ChatForm::addFriendMessage(QString message)
void ChatForm::addMessage(QString author, QString message, QString date) void ChatForm::addMessage(QString author, QString message, QString date)
{ {
message = SmileyPack::getInstance().replaceEmoticons(message); message = SmileyPack::getInstance().smileyfied(message);
addMessage(new QLabel(author), new QLabel(message), new QLabel(date)); addMessage(new QLabel(author), new QLabel(message), new QLabel(date));
} }
@ -652,3 +655,53 @@ void ChatForm::onSaveLogClicked()
file.write(log.toUtf8()); file.write(log.toUtf8());
file.close(); file.close();
} }
void ChatForm::onEmoteButtonClicked()
{
QList<QStringList> emoticons = SmileyPack::getInstance().getEmoticons();
QMenu menu;
QGridLayout* gridLayout = new QGridLayout;
menu.setLayout(gridLayout);
int colCount = sqrt(emoticons.size()) + 1;
int row = 0;
int col = 0;
for (const QStringList& set : emoticons)
{
QPushButton* button = new QPushButton;
button->setIcon(SmileyPack::getInstance().getAsIcon(set[0]));
button->setToolTip(set.join(" "));
button->setProperty("sequence", set[0]);
connect(button, &QPushButton::clicked, this, &ChatForm::onAddEmote);
gridLayout->addWidget(button, row, ++col);
if (col >= colCount)
{
col = 0;
row++;
}
}
QWidget* sender = qobject_cast<QWidget*>(QObject::sender());
if (sender)
{
QPoint pos(gridLayout->totalSizeHint().width() / 2, gridLayout->totalSizeHint().height());
menu.exec(sender->mapToGlobal(-pos));
}
}
void ChatForm::onAddEmote()
{
// hide the QMenu
QMenu* menu = qobject_cast<QMenu*>(QObject::sender()->parent());
if (menu)
menu->hide();
// insert the emoticon
QWidget* sender = qobject_cast<QWidget*>(QObject::sender());
if (sender)
msgEdit->insertPlainText(' ' + sender->property("sequence").toString() + ' ');
msgEdit->setFocus(); // refocus so that you can continue typing
}

View File

@ -83,6 +83,8 @@ private slots:
void onCancelCallTriggered(); void onCancelCallTriggered();
void onChatContextMenuRequested(QPoint pos); void onChatContextMenuRequested(QPoint pos);
void onSaveLogClicked(); void onSaveLogClicked();
void onEmoteButtonClicked();
void onAddEmote();
private: private:
Friend* f; Friend* f;

View File

@ -17,6 +17,7 @@
#include "settingsform.h" #include "settingsform.h"
#include "widget/widget.h" #include "widget/widget.h"
#include "settings.h" #include "settings.h"
#include "smileypack.h"
#include <QFont> #include <QFont>
#include <QClipboard> #include <QClipboard>
#include <QApplication> #include <QApplication>
@ -53,7 +54,9 @@ SettingsForm::SettingsForm()
makeToxPortable.setToolTip(tr("Save settings to the working directory instead of the usual conf dir","describes makeToxPortable checkbox")); makeToxPortable.setToolTip(tr("Save settings to the working directory instead of the usual conf dir","describes makeToxPortable checkbox"));
smileyPackLabel.setText(tr("Smiley Pack", "Text on smiley pack label")); smileyPackLabel.setText(tr("Smiley Pack", "Text on smiley pack label"));
smileyPackFilename.setText(Settings::getInstance().getSmileyPack()); for (auto entry : SmileyPack::listSmileyPacks("./smileys"))
smileyPackBrowser.addItem(entry.first, entry.second);
smileyPackBrowser.setCurrentIndex(smileyPackBrowser.findData(Settings::getInstance().getSmileyPack()));
main->setLayout(&layout); main->setLayout(&layout);
layout.addWidget(&nameLabel); layout.addWidget(&nameLabel);
@ -67,8 +70,7 @@ SettingsForm::SettingsForm()
layout.addWidget(&useTranslations); layout.addWidget(&useTranslations);
layout.addWidget(&makeToxPortable); layout.addWidget(&makeToxPortable);
layout.addWidget(&smileyPackLabel); layout.addWidget(&smileyPackLabel);
layout.addWidget(&smileyPackFilename); layout.addWidget(&smileyPackBrowser);
layout.addWidget(&smileyBrowseFileButton);
layout.addStretch(); layout.addStretch();
head->setLayout(&headLayout); head->setLayout(&headLayout);
@ -79,7 +81,7 @@ SettingsForm::SettingsForm()
connect(&useTranslations, SIGNAL(stateChanged(int)), this, SLOT(onUseTranslationUpdated())); connect(&useTranslations, SIGNAL(stateChanged(int)), this, SLOT(onUseTranslationUpdated()));
connect(&makeToxPortable, SIGNAL(stateChanged(int)), this, SLOT(onMakeToxPortableUpdated())); connect(&makeToxPortable, SIGNAL(stateChanged(int)), this, SLOT(onMakeToxPortableUpdated()));
connect(&idLabel, SIGNAL(clicked()), this, SLOT(copyIdClicked())); connect(&idLabel, SIGNAL(clicked()), this, SLOT(copyIdClicked()));
connect(&smileyBrowseFileButton, SIGNAL(clicked()), this, SLOT(onBrowseSmileyFilename())); connect(&smileyPackBrowser, SIGNAL(currentIndexChanged(int)), this, SLOT(onSmileyBrowserIndexChanged(int)));
} }
SettingsForm::~SettingsForm() SettingsForm::~SettingsForm()
@ -127,16 +129,8 @@ void SettingsForm::onMakeToxPortableUpdated()
Settings::getInstance().setMakeToxPortable(makeToxPortable.isChecked()); Settings::getInstance().setMakeToxPortable(makeToxPortable.isChecked());
} }
void SettingsForm::onBrowseSmileyFilename() void SettingsForm::onSmileyBrowserIndexChanged(int index)
{ {
// directory containing a file called emoticons.xml QString filename = smileyPackBrowser.itemData(index).toString();
QString filename = QFileDialog::getOpenFileName(nullptr, tr("Select smiley pack"), QDir::currentPath(), "emoticons.xml"); Settings::getInstance().setSmileyPack(filename);
// get relative path to app's local directory
QString relPath = QDir::current().relativeFilePath(filename);
// save
Settings::getInstance().setSmileyPack(relPath);
smileyPackFilename.setText(relPath);
} }

View File

@ -26,6 +26,7 @@
#include <QCheckBox> #include <QCheckBox>
#include <QPushButton> #include <QPushButton>
#include <QTextEdit> #include <QTextEdit>
#include <QComboBox>
#include "widget/tool/clickablelabel.h" #include "widget/tool/clickablelabel.h"
#include "ui_widget.h" #include "ui_widget.h"
#include "widget/selfcamview.h" #include "widget/selfcamview.h"
@ -47,7 +48,7 @@ private slots:
void onEnableIPv6Updated(); void onEnableIPv6Updated();
void onUseTranslationUpdated(); void onUseTranslationUpdated();
void onMakeToxPortableUpdated(); void onMakeToxPortableUpdated();
void onBrowseSmileyFilename(); void onSmileyBrowserIndexChanged(int index);
void copyIdClicked(); void copyIdClicked();
private: private:
@ -58,8 +59,7 @@ private:
QCheckBox enableIPv6, useTranslations, makeToxPortable; QCheckBox enableIPv6, useTranslations, makeToxPortable;
QVBoxLayout layout, headLayout; QVBoxLayout layout, headLayout;
QWidget *main, *head; QWidget *main, *head;
QLineEdit smileyPackFilename; QComboBox smileyPackBrowser;
QToolButton smileyBrowseFileButton;
public: public:
QLineEdit name, statusText; QLineEdit name, statusText;
}; };