diff --git a/qtox.pro b/qtox.pro index 77a6fe9b7..f1d64f544 100644 --- a/qtox.pro +++ b/qtox.pro @@ -20,7 +20,7 @@ # See the COPYING file for more details. -QT += core gui network multimedia multimediawidgets +QT += core gui network multimedia multimediawidgets xml greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = qtox @@ -83,7 +83,8 @@ HEADERS += widget/form/addfriendform.h \ widget/videosurface.h \ widget/camera.h \ widget/netcamview.h \ - widget/tool/clickablelabel.h + widget/tool/clickablelabel.h \ + smileypack.h SOURCES += \ widget/form/addfriendform.cpp \ @@ -115,4 +116,5 @@ SOURCES += \ widget/videosurface.cpp \ widget/camera.cpp \ widget/netcamview.cpp \ - widget/tool/clickablelabel.cpp + widget/tool/clickablelabel.cpp \ + smileypack.cpp diff --git a/settings.cpp b/settings.cpp index 888d840a5..42e759f3e 100644 --- a/settings.cpp +++ b/settings.cpp @@ -258,12 +258,12 @@ void Settings::setAnimationEnabled(bool newValue) enableSmoothAnimation = newValue; } -QByteArray Settings::getSmileyPack() const +QString Settings::getSmileyPack() const { return smileyPack; } -void Settings::setSmileyPack(const QByteArray &value) +void Settings::setSmileyPack(const QString &value) { smileyPack = value; emit smileyPackChanged(); diff --git a/settings.h b/settings.h index d4f015635..95e16620f 100644 --- a/settings.h +++ b/settings.h @@ -83,8 +83,8 @@ public: bool isAnimationEnabled() const; void setAnimationEnabled(bool newValue); - QByteArray getSmileyPack() const; - void setSmileyPack(const QByteArray &value); + QString getSmileyPack() const; + void setSmileyPack(const QString &value); bool isCurstomEmojiFont() const; void setCurstomEmojiFont(bool value); @@ -142,7 +142,7 @@ private: // GUI bool enableSmoothAnimation; - QByteArray smileyPack; + QString smileyPack; bool customEmojiFont; QString emojiFontFamily; int emojiFontPointSize; diff --git a/smileypack.cpp b/smileypack.cpp new file mode 100644 index 000000000..f035704e7 --- /dev/null +++ b/smileypack.cpp @@ -0,0 +1,122 @@ +/* + Copyright (C) 2013 by Maxim Biro + + This file is part of Tox Qt GUI. + + 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 COPYING file for more details. +*/ + +#include "smileypack.h" +#include "settings.h" + +#include +#include +#include +#include + +SmileyPack::SmileyPack() +{ + load(Settings::getInstance().getSmileyPack()); + connect(&Settings::getInstance(), &Settings::smileyPackChanged, this, &SmileyPack::onSmileyPackChanged); +} + +SmileyPack& SmileyPack::getInstance() +{ + static SmileyPack smileyPack; + return smileyPack; +} + +bool SmileyPack::load(const QString& filename) +{ + // discard old data + lookupTable.clear(); + QDir::setSearchPaths("smiley", QStringList()); + + // open emoticons.xml + QFile xmlFile(filename); + if(!xmlFile.open(QIODevice::ReadOnly)) + return false; // cannot open file + + /* parse the cfg document + * sample: + * + * + * + * :) + * :-) + * + * + * :( + * :-( + * + * + */ + + QDomDocument doc; + doc.setContent(xmlFile.readAll()); + + QDomNodeList emoticonElements = doc.elementsByTagName("emoticon"); + for (int i = 0; i < emoticonElements.size(); ++i) + { + QString file = emoticonElements.at(i).attributes().namedItem("file").nodeValue(); + QDomElement stringElement = emoticonElements.at(i).firstChildElement("string"); + + while (!stringElement.isNull()) + { + QString rune = stringElement.text(); + lookupTable.insert(rune, file); // add it to the map + + stringElement = stringElement.nextSibling().toElement(); + } + } + + // Rich Text makes use of Qt's resource system, so + // let Qt know about our smilies + QFileInfo info(filename); + QDir::setSearchPaths("smiley", QStringList() << info.absolutePath()); + + // success! + return true; +} + +QString SmileyPack::replaceEmoticons(const QString &msg) const +{ + QString out = msg; + QRegExp exp("\\S*"); // matches words + + int index = msg.indexOf(exp); + int offset = 0; + + // if a word is key of a smiley, replace it by it's corresponding image in Rich Text + while (index >= 0 || exp.matchedLength() > 0) + { + QString key = exp.cap(); + if (lookupTable.contains(key)) + { + QString width = QString::number(16); + QString height = QString::number(16); + + QString img = lookupTable[key]; + QString imgRt = ""; + + out.replace(index + offset, key.length(), imgRt); + offset += imgRt.length() - key.length(); + } + index = msg.indexOf(exp, index + exp.matchedLength() + 1); + } + + return out; +} + +void SmileyPack::onSmileyPackChanged() +{ + load(Settings::getInstance().getSmileyPack()); +} diff --git a/smileypack.h b/smileypack.h new file mode 100644 index 000000000..559abb491 --- /dev/null +++ b/smileypack.h @@ -0,0 +1,43 @@ +/* + Copyright (C) 2013 by Maxim Biro + + This file is part of Tox Qt GUI. + + 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 COPYING file for more details. +*/ + +#ifndef SMILEYPACK_H +#define SMILEYPACK_H + +#include +#include +#include + +//maps emoticons to smilies +class SmileyPack : public QObject +{ + Q_OBJECT +public: + explicit SmileyPack(); + SmileyPack(SmileyPack&) = delete; + SmileyPack& operator=(const SmileyPack&) = delete; + + static SmileyPack& getInstance(); + + bool load(const QString &filename); + QString replaceEmoticons(const QString& msg) const; +protected: + QHash lookupTable; // matches an emoticon with it's corresponding smiley +private slots: + void onSmileyPackChanged(); +}; + +#endif // SMILEYPACK_H diff --git a/widget/form/chatform.cpp b/widget/form/chatform.cpp index 13d4b5271..07e7d9e4b 100644 --- a/widget/form/chatform.cpp +++ b/widget/form/chatform.cpp @@ -16,6 +16,7 @@ #include "chatform.h" #include "friend.h" +#include "smileypack.h" #include "widget/friendwidget.h" #include "widget/widget.h" #include "widget/filetransfertwidget.h" @@ -238,6 +239,7 @@ void ChatForm::addFriendMessage(QString message) void ChatForm::addMessage(QString author, QString message, QString date) { + message = SmileyPack::getInstance().replaceEmoticons(message); addMessage(new QLabel(author), new QLabel(message), new QLabel(date)); } diff --git a/widget/form/settingsform.cpp b/widget/form/settingsform.cpp index 1179497c5..9d2c71d00 100644 --- a/widget/form/settingsform.cpp +++ b/widget/form/settingsform.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include SettingsForm::SettingsForm() : QObject() @@ -49,6 +51,9 @@ SettingsForm::SettingsForm() makeToxPortable.setText(tr("Make Tox portable","Text on a checkbox to make qTox a portable application")); makeToxPortable.setChecked(Settings::getInstance().getMakeToxPortable()); + smileyPackLabel.setText(tr("Smiley Pack", "Text on smiley pack label")); + smileyPackFilename.setText(Settings::getInstance().getSmileyPack()); + main->setLayout(&layout); layout.addWidget(&nameLabel); layout.addWidget(&name); @@ -60,6 +65,9 @@ SettingsForm::SettingsForm() layout.addWidget(&enableIPv6); layout.addWidget(&useTranslations); layout.addWidget(&makeToxPortable); + layout.addWidget(&smileyPackLabel); + layout.addWidget(&smileyPackFilename); + layout.addWidget(&smileyBrowseFileButton); layout.addStretch(); head->setLayout(&headLayout); @@ -70,6 +78,7 @@ SettingsForm::SettingsForm() connect(&useTranslations, SIGNAL(stateChanged(int)), this, SLOT(onUseTranslationUpdated())); connect(&makeToxPortable, SIGNAL(stateChanged(int)), this, SLOT(onMakeToxPortableUpdated())); connect(&idLabel, SIGNAL(clicked()), this, SLOT(copyIdClicked())); + connect(&smileyBrowseFileButton, SIGNAL(clicked()), this, SLOT(onBrowseSmileyFilename())); } SettingsForm::~SettingsForm() @@ -116,3 +125,17 @@ void SettingsForm::onMakeToxPortableUpdated() { Settings::getInstance().setMakeToxPortable(makeToxPortable.isChecked()); } + +void SettingsForm::onBrowseSmileyFilename() +{ + // directory containing a file called emoticons.xml + QString filename = QFileDialog::getOpenFileName(nullptr, tr("Select smiley pack"), QDir::currentPath(), "emoticons.xml"); + + // get relative path to app's local directory + QString relPath = QDir::current().relativeFilePath(filename); + + // save + Settings::getInstance().setSmileyPack(relPath); + smileyPackFilename.setText(relPath); +} + diff --git a/widget/form/settingsform.h b/widget/form/settingsform.h index 3342c5667..5cf2c5532 100644 --- a/widget/form/settingsform.h +++ b/widget/form/settingsform.h @@ -47,17 +47,19 @@ private slots: void onEnableIPv6Updated(); void onUseTranslationUpdated(); void onMakeToxPortableUpdated(); + void onBrowseSmileyFilename(); void copyIdClicked(); private: - QLabel headLabel, nameLabel, statusTextLabel; + QLabel headLabel, nameLabel, statusTextLabel, smileyPackLabel; QTextEdit id; ClickableLabel idLabel; QPushButton videoTest; QCheckBox enableIPv6, useTranslations, makeToxPortable; QVBoxLayout layout, headLayout; QWidget *main, *head; - + QLineEdit smileyPackFilename; + QToolButton smileyBrowseFileButton; public: QLineEdit name, statusText; };