From 121361e37bc2cc8ab1eae2f2aea92975a62e5511 Mon Sep 17 00:00:00 2001
From: Tobias Markmann <tm@ayena.de>
Date: Tue, 7 Mar 2017 20:32:56 +0100
Subject: Use text-based emoticons on Linux and Windows

The new unicode-based emoji dialog currently only provides
a good experience on macOS.
This commit enables the unicode-based emoji dialog on macOS
and uses the text-based emoticons on Linux and Windows.

Test-Information:

Tested on macOS 10.12.3 with Qt 5.5.1 and Windows 8 with Qt
5.5.1.

Change-Id: Ibee20eacafa5788bcdf5a46e1ceac713a28a0383

diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index 345139c..f57b83f 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -6,7 +6,9 @@
 
 #include <Swift/QtUI/QtChatWindow.h>
 
+#include <map>
 #include <memory>
+#include <string>
 
 #include <boost/cstdint.hpp>
 #include <boost/lexical_cast.hpp>
@@ -38,6 +40,7 @@
 #include <QUrl>
 
 #include <Swiften/Base/Log.h>
+#include <Swiften/Base/Platform.h>
 
 #include <Swift/Controllers/Roster/ContactRosterItem.h>
 #include <Swift/Controllers/Roster/Roster.h>
@@ -64,7 +67,7 @@
 
 namespace Swift {
 
-QtChatWindow::QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QtSettingsProvider* qtOnlySettings) : QtTabbable(), id_(Q2PSTRING(contact)), contact_(contact), nextAlertId_(0), eventStream_(eventStream), settings_(settings), qtOnlySettings_(qtOnlySettings), blockingState_(BlockingUnsupported), isMUC_(false), supportsImpromptuChat_(false), roomBookmarkState_(RoomNotBookmarked) {
+QtChatWindow::QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QtSettingsProvider* qtOnlySettings, const std::map<std::string, std::string>& emoticonsMap) : QtTabbable(), id_(Q2PSTRING(contact)), contact_(contact), nextAlertId_(0), eventStream_(eventStream), settings_(settings), qtOnlySettings_(qtOnlySettings), blockingState_(BlockingUnsupported), isMUC_(false), supportsImpromptuChat_(false), roomBookmarkState_(RoomNotBookmarked), emoticonsMap_(emoticonsMap) {
     unreadCount_ = 0;
     isOnline_ = true;
     completer_ = nullptr;
@@ -147,7 +150,12 @@ QtChatWindow::QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventSt
     connect(input_, SIGNAL(receivedFocus()), this, SLOT(handleTextInputReceivedFocus()));
     connect(input_, SIGNAL(lostFocus()), this, SLOT(handleTextInputLostFocus()));
     QPushButton* emojisButton_ = new QPushButton(this);
+
+#ifdef SWIFTEN_PLATFORM_MACOSX
     emojisButton_->setText("\xF0\x9F\x98\x83");
+#else
+    emojisButton_->setIcon(QIcon(":/emoticons/smile.png"));
+#endif
     connect(emojisButton_, SIGNAL(clicked()), this, SLOT(handleEmojisButtonClicked()));
 
     // using an extra layout to work around Qt margin glitches on OS X
@@ -662,7 +670,7 @@ void QtChatWindow::setSubject(const std::string& subject) {
 
 void QtChatWindow::handleEmojisButtonClicked() {
     // Create QtEmojisSelector and QMenu
-    emojisGrid_ = new QtEmojisSelector(qtOnlySettings_->getQSettings());
+    emojisGrid_ = new QtEmojisSelector(qtOnlySettings_->getQSettings(), emoticonsMap_);
     auto emojisLayout = new QVBoxLayout();
     emojisLayout->setContentsMargins(style()->pixelMetric(QStyle::PM_MenuHMargin),style()->pixelMetric(QStyle::PM_MenuVMargin),
                                      style()->pixelMetric(QStyle::PM_MenuHMargin),style()->pixelMetric(QStyle::PM_MenuVMargin));
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index 4c64c84..fca4aec 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -7,6 +7,7 @@
 #pragma once
 
 #include <map>
+#include <string>
 
 #include <QMap>
 #include <QMenu>
@@ -81,7 +82,7 @@ namespace Swift {
         Q_OBJECT
 
         public:
-            QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QtSettingsProvider* qtOnlySettings);
+            QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QtSettingsProvider* qtOnlySettings, const std::map<std::string, std::string>& emoticonsMap);
             virtual ~QtChatWindow();
             std::string addMessage(const ChatMessage& message, const std::string &senderName, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time);
             std::string addAction(const ChatMessage& message, const std::string &senderName, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time);
@@ -236,5 +237,6 @@ namespace Swift {
             RoomBookmarkState roomBookmarkState_;
             std::unique_ptr<QMenu> emojisMenu_;
             QPointer<QtEmojisSelector> emojisGrid_;
+            std::map<std::string, std::string> emoticonsMap_;
     };
 }
diff --git a/Swift/QtUI/QtChatWindowFactory.cpp b/Swift/QtUI/QtChatWindowFactory.cpp
index 5a4a501..49cfe4d 100644
--- a/Swift/QtUI/QtChatWindowFactory.cpp
+++ b/Swift/QtUI/QtChatWindowFactory.cpp
@@ -23,7 +23,7 @@ namespace Swift {
 static const QString SPLITTER_STATE = "mucSplitterState";
 static const QString CHAT_TABS_GEOMETRY = "chatTabsGeometry";
 
-QtChatWindowFactory::QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabsBase* tabs, const QString& themePath) : themePath_(themePath) {
+QtChatWindowFactory::QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabsBase* tabs, const QString& themePath, const std::map<std::string, std::string>& emoticonsMap) : themePath_(themePath), emoticonsMap_(emoticonsMap) {
     qtOnlySettings_ = qtSettings;
     settings_ = settings;
     tabs_ = tabs;
@@ -54,7 +54,7 @@ ChatWindow* QtChatWindowFactory::createChatWindow(const JID &contact,UIEventStre
         }
     }
 
-    QtChatWindow* chatWindow = new QtChatWindow(P2QSTRING(contact.toString()), theme_, eventStream, settings_, qtOnlySettings_);
+    QtChatWindow* chatWindow = new QtChatWindow(P2QSTRING(contact.toString()), theme_, eventStream, settings_, qtOnlySettings_, emoticonsMap_);
 
     connect(chatWindow, SIGNAL(splitterMoved()), this, SLOT(handleSplitterMoved()));
     connect(this, SIGNAL(changeSplitterState(QByteArray)), chatWindow, SLOT(handleChangeSplitterState(QByteArray)));
diff --git a/Swift/QtUI/QtChatWindowFactory.h b/Swift/QtUI/QtChatWindowFactory.h
index a217215..2bb6a90 100644
--- a/Swift/QtUI/QtChatWindowFactory.h
+++ b/Swift/QtUI/QtChatWindowFactory.h
@@ -6,6 +6,9 @@
 
 #pragma once
 
+#include <map>
+#include <string>
+
 #include <QMenu>
 #include <QObject>
 #include <QSplitter>
@@ -29,7 +32,7 @@ namespace Swift {
     class QtChatWindowFactory : public QObject, public ChatWindowFactory {
         Q_OBJECT
         public:
-            QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabsBase* tabs, const QString& themePath);
+            QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabsBase* tabs, const QString& themePath, const std::map<std::string, std::string>& emoticonsMap);
             ~QtChatWindowFactory();
             ChatWindow* createChatWindow(const JID &contact, UIEventStream* eventStream);
         signals:
@@ -43,5 +46,6 @@ namespace Swift {
             QtSettingsProvider* qtOnlySettings_;
             QtChatTabsBase* tabs_;
             QtChatTheme* theme_;
+            std::map<std::string, std::string> emoticonsMap_;
     };
 }
diff --git a/Swift/QtUI/QtEmojiCell.cpp b/Swift/QtUI/QtEmojiCell.cpp
index 3f2c652..865f1f6 100644
--- a/Swift/QtUI/QtEmojiCell.cpp
+++ b/Swift/QtUI/QtEmojiCell.cpp
@@ -30,14 +30,27 @@ namespace Swift {
         connect(this, SIGNAL(clicked()), this, SLOT(handleEmojiClicked()));
     }
 
+    QtEmojiCell::QtEmojiCell(QIcon icon, QString text, QWidget* parent) : QPushButton(parent), text_(text) {
+        setIcon(icon);
+        setFlat(true);
+        connect(this, SIGNAL(clicked()), this, SLOT(handleEmojiClicked()));
+        setFixedSize(icon.availableSizes()[0] * 1.5);
+    }
+
     QtEmojiCell::QtEmojiCell(const QtEmojiCell& b) : QtEmojiCell(b.toolTip(), b.text()) {
+
     }
 
     QtEmojiCell::~QtEmojiCell() {
     }
 
     void QtEmojiCell::handleEmojiClicked () {
-        emit emojiClicked(text());
+        if (text_.isEmpty()) {
+            emit emojiClicked(text());
+        }
+        else {
+            emit emojiClicked(text_);
+        }
     }
 
 }
diff --git a/Swift/QtUI/QtEmojiCell.h b/Swift/QtUI/QtEmojiCell.h
index 62f989b..250b10d 100644
--- a/Swift/QtUI/QtEmojiCell.h
+++ b/Swift/QtUI/QtEmojiCell.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016 Isode Limited.
+ * Copyright (c) 2016-2017 Isode Limited.
  * All rights reserved.
  * See the COPYING file for more information.
  */
@@ -20,11 +20,16 @@ namespace Swift {
         public:
             QtEmojiCell(const QtEmojiCell& b);
             QtEmojiCell(QString shortname, QString text, QWidget* parent = nullptr);
+            QtEmojiCell(QIcon icon, QString text, QWidget* parent = nullptr);
             ~QtEmojiCell();
+
         signals:
             void emojiClicked(QString emojiAsText);
 
         private slots:
             void handleEmojiClicked();
+
+        private:
+            QString text_;
     };
 }
diff --git a/Swift/QtUI/QtEmojisGrid.cpp b/Swift/QtUI/QtEmojisGrid.cpp
index 6064a66..981dd67 100644
--- a/Swift/QtUI/QtEmojisGrid.cpp
+++ b/Swift/QtUI/QtEmojisGrid.cpp
@@ -47,6 +47,12 @@ namespace Swift {
         }
     }
 
+    void QtEmojisGrid::addEmoticon(QIcon icon, QString text) {
+        auto emoji = new QtEmojiCell(icon, text);
+        connect(emoji, SIGNAL(emojiClicked(QString)), this, SIGNAL(onEmojiSelected(QString)));
+        addItem(new QWidgetItem(emoji));
+    }
+
     void QtEmojisGrid::clearEmojis() {
         QLayoutItem* child = nullptr;
         while ((child = this->takeAt(0)) != nullptr) {
diff --git a/Swift/QtUI/QtEmojisGrid.h b/Swift/QtUI/QtEmojisGrid.h
index b7ba87f..5e6c770 100644
--- a/Swift/QtUI/QtEmojisGrid.h
+++ b/Swift/QtUI/QtEmojisGrid.h
@@ -22,6 +22,7 @@ namespace Swift {
 
         protected:
             void setEmojis(const QVector<QString>& emojis);
+            void addEmoticon(QIcon icon, QString text);
 
         private:
             void clearEmojis();
diff --git a/Swift/QtUI/QtEmojisScroll.cpp b/Swift/QtUI/QtEmojisScroll.cpp
index 2c347bb..be505b6 100644
--- a/Swift/QtUI/QtEmojisScroll.cpp
+++ b/Swift/QtUI/QtEmojisScroll.cpp
@@ -10,6 +10,8 @@
 #include <QScrollArea>
 #include <QStyle>
 
+#include <Swiften/Base/Platform.h>
+
 #include <Swift/QtUI/QtEmojisGrid.h>
 #include <Swift/QtUI/QtRecentEmojisGrid.h>
 
@@ -26,7 +28,11 @@ namespace Swift {
         this->layout()->setContentsMargins(0,0,0,0);
 
         if (emojiLayout->itemAt(0)) {
+#ifdef SWIFTEN_PLATFORM_MACOSX
             setMinimumHeight(emojiLayout->itemAt(0)->minimumSize().height() * 8);
+#else
+            setMinimumHeight(emojiLayout->itemAt(0)->minimumSize().height() * 2);
+#endif
         }
     }
 }
diff --git a/Swift/QtUI/QtEmojisSelector.cpp b/Swift/QtUI/QtEmojisSelector.cpp
index 8b21380..62ed862 100644
--- a/Swift/QtUI/QtEmojisSelector.cpp
+++ b/Swift/QtUI/QtEmojisSelector.cpp
@@ -9,17 +9,22 @@
 #include <QScrollArea>
 #include <QSettings>
 #include <QString>
+#include <QTabBar>
 #include <QWidget>
 
+#include <Swiften/Base/Platform.h>
+
 #include <SwifTools/EmojiMapper.h>
 
 #include <Swift/QtUI/QtEmojisGrid.h>
 #include <Swift/QtUI/QtEmojisScroll.h>
+#include <Swift/QtUI/QtEmoticonsGrid.h>
 #include <Swift/QtUI/QtRecentEmojisGrid.h>
 #include <Swift/QtUI/QtSwiftUtil.h>
 
 namespace Swift {
-    QtEmojisSelector::QtEmojisSelector(QSettings* settings, QWidget* parent) : QTabWidget(parent), settings_(settings) {
+    QtEmojisSelector::QtEmojisSelector(QSettings* settings, const std::map<std::string, std::string>& emoticonsMap, QWidget* parent) : QTabWidget(parent), settings_(settings), emoticonsMap_(emoticonsMap) {
+#ifdef SWIFTEN_PLATFORM_MACOSX
         recentEmojisGrid_ = addRecentTab();
         connect(recentEmojisGrid_, SIGNAL(onEmojiSelected(QString)), this, SLOT(emojiClickedSlot(QString)));
 
@@ -31,10 +36,15 @@ namespace Swift {
         }
 
         loadSettings();
+#else
+        setupEmoticonsTab();
+#endif
     }
 
     QtEmojisSelector::~QtEmojisSelector() {
+#ifdef SWIFTEN_PLATFORM_MACOSX
         writeSettings();
+#endif
     }
 
     QtRecentEmojisGrid* QtEmojisSelector::addRecentTab() {
@@ -68,8 +78,18 @@ namespace Swift {
         settings_->setValue("currentEmojiTab", currentIndex());
     }
 
+    void QtEmojisSelector::setupEmoticonsTab() {
+        QtEmojisGrid* grid = new QtEmoticonsGrid(emoticonsMap_);
+        QtEmojisScroll* scroll = new QtEmojisScroll(grid);
+        QTabWidget::addTab(scroll, QIcon(":/emoticons/smile.png"),"");
+        connect(grid, SIGNAL(onEmojiSelected(QString)), this, SLOT(emojiClickedSlot(QString)));
+        tabBar()->hide();
+    }
+
     void QtEmojisSelector::emojiClickedSlot(QString emoji) {
-        recentEmojisGrid_->handleEmojiClicked(emoji);
+        if (recentEmojisGrid_) {
+            recentEmojisGrid_->handleEmojiClicked(emoji);
+        }
         emit emojiClicked(emoji);
     }
 }
diff --git a/Swift/QtUI/QtEmojisSelector.h b/Swift/QtUI/QtEmojisSelector.h
index 1065aa0..7ac11d0 100644
--- a/Swift/QtUI/QtEmojisSelector.h
+++ b/Swift/QtUI/QtEmojisSelector.h
@@ -6,6 +6,9 @@
 
 #pragma once
 
+#include <map>
+#include <string>
+
 #include <QSettings>
 #include <QTabWidget>
 
@@ -17,7 +20,7 @@ namespace Swift {
 class QtEmojisSelector : public QTabWidget {
     Q_OBJECT
     public:
-        QtEmojisSelector(QSettings* settings, QWidget * parent = 0);
+        QtEmojisSelector(QSettings* settings, const std::map<std::string, std::string>& emoticonsMap, QWidget * parent = 0);
         ~QtEmojisSelector();
 
     public slots:
@@ -32,9 +35,12 @@ class QtEmojisSelector : public QTabWidget {
         void loadSettings();
         void writeSettings();
 
+        void setupEmoticonsTab();
+
     private:
         QSettings* settings_ = nullptr;
         QtRecentEmojisGrid* recentEmojisGrid_ = nullptr;
+        std::map<std::string, std::string> emoticonsMap_;
 };
 
 }
diff --git a/Swift/QtUI/QtEmoticonsGrid.cpp b/Swift/QtUI/QtEmoticonsGrid.cpp
new file mode 100644
index 0000000..a592e90
--- /dev/null
+++ b/Swift/QtUI/QtEmoticonsGrid.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/QtEmoticonsGrid.h>
+
+#include <unordered_set>
+
+#include <SwifTools/EmojiMapper.h>
+
+#include <Swift/QtUI/QtEmojiCell.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+
+    QtEmoticonsGrid::QtEmoticonsGrid(const std::map<std::string, std::string>& emoticonsMap) : QtEmojisGrid() {
+        std::unordered_set<std::string> usedEmoticons;
+        for (const auto& emoticonPair : emoticonsMap) {
+            if (usedEmoticons.find(emoticonPair.second) == usedEmoticons.end()) {
+                usedEmoticons.insert(emoticonPair.second);
+
+                auto filePath = P2QSTRING(emoticonPair.second);
+                if (filePath.startsWith("qrc:/")) {
+                    filePath.remove(0, 3);
+                }
+                auto icon = QIcon(filePath);
+                auto text = P2QSTRING(emoticonPair.first);
+
+                addEmoticon(icon, text);
+            }
+        }
+    }
+
+    QtEmoticonsGrid::~QtEmoticonsGrid() {
+
+    }
+
+}
diff --git a/Swift/QtUI/QtEmoticonsGrid.h b/Swift/QtUI/QtEmoticonsGrid.h
new file mode 100644
index 0000000..2fc2907
--- /dev/null
+++ b/Swift/QtUI/QtEmoticonsGrid.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include <Swift/QtUI/QtEmojisGrid.h>
+
+namespace Swift {
+    class QtEmoticonsGrid : public QtEmojisGrid {
+        Q_OBJECT
+    public:
+        explicit QtEmoticonsGrid(const std::map<std::string, std::string>& emoticonsMap);
+        ~QtEmoticonsGrid();
+
+    };
+}
diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp
index b61147d..7b4e2c3 100644
--- a/Swift/QtUI/QtSwift.cpp
+++ b/Swift/QtUI/QtSwift.cpp
@@ -229,7 +229,7 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
     applicationPathProvider_ = new PlatformApplicationPathProvider(SWIFT_APPLICATION_NAME);
     storagesFactory_ = new FileStoragesFactory(applicationPathProvider_->getDataDir(), networkFactories_.getCryptoProvider());
     certificateStorageFactory_ = new CertificateFileStorageFactory(applicationPathProvider_->getDataDir(), tlsFactories_.getCertificateFactory(), networkFactories_.getCryptoProvider());
-    chatWindowFactory_ = new QtChatWindowFactory(splitter_, settingsHierachy_, qtSettings_, tabs_, ":/themes/Default/");
+    chatWindowFactory_ = new QtChatWindowFactory(splitter_, settingsHierachy_, qtSettings_, tabs_, ":/themes/Default/", emoticons);
     soundPlayer_ = new QtSoundPlayer(applicationPathProvider_);
 
     // Ugly, because the dock depends on the tray, but the temporary
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 69e99e7..1bcba64 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -142,6 +142,7 @@ sources = [
     "QtEmojisScroll.cpp",
     "QtEmojisSelector.cpp",
     "QtRecentEmojisGrid.cpp",
+    "QtEmoticonsGrid.cpp",
     "QtContactEditWindow.cpp",
     "QtContactEditWidget.cpp",
     "QtSingleWindow.cpp",
-- 
cgit v0.10.2-6-g49f6