From 32c83405771b2f7a751783529d17e1b84dad4224 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Thu, 30 Nov 2017 13:53:28 +0200 Subject: Create directories for related files --- CMakeLists.txt | 143 ++-- include/EmojiCategory.h | 59 -- include/EmojiItemDelegate.h | 40 - include/EmojiPanel.h | 55 -- include/EmojiPickButton.h | 48 -- include/EmojiProvider.h | 41 - include/FileItem.h | 98 --- include/ImageItem.h | 78 -- include/ImageOverlayDialog.h | 46 - include/JoinRoomDialog.h | 22 - include/LeaveRoomDialog.h | 22 - include/LogoutDialog.h | 39 - include/TextInputWidget.h | 3 +- include/TimelineItem.h | 155 ---- include/TimelineView.h | 310 ------- include/TimelineViewManager.h | 80 -- include/dialogs/ImageOverlayDialog.h | 46 + include/dialogs/JoinRoomDialog.h | 22 + include/dialogs/LeaveRoomDialog.h | 22 + include/dialogs/LogoutDialog.h | 39 + include/emoji/EmojiCategory.h | 59 ++ include/emoji/EmojiItemDelegate.h | 40 + include/emoji/EmojiPanel.h | 55 ++ include/emoji/EmojiPickButton.h | 48 ++ include/emoji/EmojiProvider.h | 41 + include/timeline/TimelineItem.h | 155 ++++ include/timeline/TimelineView.h | 310 +++++++ include/timeline/TimelineViewManager.h | 80 ++ include/timeline/widgets/FileItem.h | 98 +++ include/timeline/widgets/ImageItem.h | 78 ++ src/AvatarProvider.cc | 3 +- src/ChatPage.cc | 3 +- src/EmojiCategory.cc | 89 -- src/EmojiItemDelegate.cc | 47 - src/EmojiPanel.cc | 244 ------ src/EmojiPickButton.cc | 65 -- src/EmojiProvider.cc | 1465 -------------------------------- src/FileItem.cc | 209 ----- src/ImageItem.cc | 223 ----- src/ImageOverlayDialog.cc | 129 --- src/JoinRoomDialog.cc | 48 -- src/LeaveRoomDialog.cc | 53 -- src/LogoutDialog.cc | 71 -- src/RoomList.cc | 5 +- src/TimelineItem.cc | 506 ----------- src/TimelineView.cc | 560 ------------ src/TimelineViewManager.cc | 300 ------- src/TopRoomBar.cc | 3 +- src/UserInfoWidget.cc | 3 +- src/dialogs/ImageOverlayDialog.cc | 129 +++ src/dialogs/JoinRoomDialog.cc | 49 ++ src/dialogs/LeaveRoomDialog.cc | 54 ++ src/dialogs/LogoutDialog.cc | 72 ++ src/emoji/EmojiCategory.cc | 90 ++ src/emoji/EmojiItemDelegate.cc | 47 + src/emoji/EmojiPanel.cc | 245 ++++++ src/emoji/EmojiPickButton.cc | 65 ++ src/emoji/EmojiProvider.cc | 1465 ++++++++++++++++++++++++++++++++ src/timeline/TimelineItem.cc | 507 +++++++++++ src/timeline/TimelineView.cc | 561 ++++++++++++ src/timeline/TimelineViewManager.cc | 301 +++++++ src/timeline/widgets/FileItem.cc | 208 +++++ src/timeline/widgets/ImageItem.cc | 223 +++++ 63 files changed, 5201 insertions(+), 5173 deletions(-) delete mode 100644 include/EmojiCategory.h delete mode 100644 include/EmojiItemDelegate.h delete mode 100644 include/EmojiPanel.h delete mode 100644 include/EmojiPickButton.h delete mode 100644 include/EmojiProvider.h delete mode 100644 include/FileItem.h delete mode 100644 include/ImageItem.h delete mode 100644 include/ImageOverlayDialog.h delete mode 100644 include/JoinRoomDialog.h delete mode 100644 include/LeaveRoomDialog.h delete mode 100644 include/LogoutDialog.h delete mode 100644 include/TimelineItem.h delete mode 100644 include/TimelineView.h delete mode 100644 include/TimelineViewManager.h create mode 100644 include/dialogs/ImageOverlayDialog.h create mode 100644 include/dialogs/JoinRoomDialog.h create mode 100644 include/dialogs/LeaveRoomDialog.h create mode 100644 include/dialogs/LogoutDialog.h create mode 100644 include/emoji/EmojiCategory.h create mode 100644 include/emoji/EmojiItemDelegate.h create mode 100644 include/emoji/EmojiPanel.h create mode 100644 include/emoji/EmojiPickButton.h create mode 100644 include/emoji/EmojiProvider.h create mode 100644 include/timeline/TimelineItem.h create mode 100644 include/timeline/TimelineView.h create mode 100644 include/timeline/TimelineViewManager.h create mode 100644 include/timeline/widgets/FileItem.h create mode 100644 include/timeline/widgets/ImageItem.h delete mode 100644 src/EmojiCategory.cc delete mode 100644 src/EmojiItemDelegate.cc delete mode 100644 src/EmojiPanel.cc delete mode 100644 src/EmojiPickButton.cc delete mode 100644 src/EmojiProvider.cc delete mode 100644 src/FileItem.cc delete mode 100644 src/ImageItem.cc delete mode 100644 src/ImageOverlayDialog.cc delete mode 100644 src/JoinRoomDialog.cc delete mode 100644 src/LeaveRoomDialog.cc delete mode 100644 src/LogoutDialog.cc delete mode 100644 src/TimelineItem.cc delete mode 100644 src/TimelineView.cc delete mode 100644 src/TimelineViewManager.cc create mode 100644 src/dialogs/ImageOverlayDialog.cc create mode 100644 src/dialogs/JoinRoomDialog.cc create mode 100644 src/dialogs/LeaveRoomDialog.cc create mode 100644 src/dialogs/LogoutDialog.cc create mode 100644 src/emoji/EmojiCategory.cc create mode 100644 src/emoji/EmojiItemDelegate.cc create mode 100644 src/emoji/EmojiPanel.cc create mode 100644 src/emoji/EmojiPickButton.cc create mode 100644 src/emoji/EmojiProvider.cc create mode 100644 src/timeline/TimelineItem.cc create mode 100644 src/timeline/TimelineView.cc create mode 100644 src/timeline/TimelineViewManager.cc create mode 100644 src/timeline/widgets/FileItem.cc create mode 100644 src/timeline/widgets/ImageItem.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 81dc2ca5..9b13def9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,24 +140,52 @@ endif() # Declare source and header files. # set(SRC_FILES + # Dialogs + src/dialogs/ImageOverlayDialog.cc + src/dialogs/JoinRoomDialog.cc + src/dialogs/LeaveRoomDialog.cc + src/dialogs/LogoutDialog.cc + + # Emoji + src/emoji/EmojiCategory.cc + src/emoji/EmojiItemDelegate.cc + src/emoji/EmojiPanel.cc + src/emoji/EmojiPickButton.cc + src/emoji/EmojiProvider.cc + + # Timeline + src/timeline/TimelineViewManager.cc + src/timeline/TimelineItem.cc + src/timeline/TimelineView.cc + src/timeline/widgets/FileItem.cc + src/timeline/widgets/ImageItem.cc + + # UI components + src/ui/Avatar.cc + src/ui/Badge.cc + src/ui/LoadingIndicator.cc + src/ui/FlatButton.cc + src/ui/FloatingButton.cc + src/ui/Label.cc + src/ui/OverlayModal.cc + src/ui/ScrollBar.cc + src/ui/SnackBar.cc + src/ui/RaisedButton.cc + src/ui/Ripple.cc + src/ui/RippleOverlay.cc + src/ui/OverlayWidget.cc + src/ui/TextField.cc + src/ui/ToggleButton.cc + src/ui/Theme.cc + src/ui/ThemeManager.cc + src/AvatarProvider.cc src/Cache.cc src/ChatPage.cc src/Deserializable.cc - src/EmojiCategory.cc - src/EmojiItemDelegate.cc - src/EmojiPanel.cc - src/EmojiPickButton.cc - src/EmojiProvider.cc - src/FileItem.cc - src/ImageItem.cc - src/ImageOverlayDialog.cc src/InputValidator.cc - src/JoinRoomDialog.cc - src/LeaveRoomDialog.cc src/Login.cc src/LoginPage.cc - src/LogoutDialog.cc src/MainWindow.cc src/MatrixClient.cc src/Profile.cc @@ -169,38 +197,17 @@ set(SRC_FILES src/RoomMessages.cc src/RoomState.cc src/SideBarActions.cc - src/UserSettingsPage.cc src/Splitter.cc src/Sync.cc src/TextInputWidget.cc - src/TimelineItem.cc - src/TimelineView.cc - src/TimelineViewManager.cc src/TopRoomBar.cc src/TrayIcon.cc src/TypingDisplay.cc src/UserInfoWidget.cc + src/UserSettingsPage.cc src/Versions.cc src/WelcomePage.cc src/main.cc - - src/ui/Avatar.cc - src/ui/Badge.cc - src/ui/LoadingIndicator.cc - src/ui/FlatButton.cc - src/ui/FloatingButton.cc - src/ui/Label.cc - src/ui/OverlayModal.cc - src/ui/ScrollBar.cc - src/ui/SnackBar.cc - src/ui/RaisedButton.cc - src/ui/Ripple.cc - src/ui/RippleOverlay.cc - src/ui/OverlayWidget.cc - src/ui/TextField.cc - src/ui/ToggleButton.cc - src/ui/Theme.cc - src/ui/ThemeManager.cc ) set(MATRIX_EVENTS @@ -237,20 +244,47 @@ include_directories(libs/lmdbxx) include_directories(${LMDB_INCLUDE_DIR}) qt5_wrap_cpp(MOC_HEADERS + # Dialogs + include/dialogs/ImageOverlayDialog.h + include/dialogs/JoinRoomDialog.h + include/dialogs/LeaveRoomDialog.h + include/dialogs/LogoutDialog.h + + # Emoji + include/emoji/EmojiCategory.h + include/emoji/EmojiItemDelegate.h + include/emoji/EmojiPanel.h + include/emoji/EmojiPickButton.h + include/emoji/EmojiProvider.h + + # Timeline + include/timeline/TimelineItem.h + include/timeline/TimelineView.h + include/timeline/TimelineViewManager.h + include/timeline/widgets/FileItem.h + include/timeline/widgets/ImageItem.h + + # UI components + include/ui/Avatar.h + include/ui/Badge.h + include/ui/LoadingIndicator.h + include/ui/FlatButton.h + include/ui/Label.h + include/ui/FloatingButton.h + include/ui/OverlayWidget.h + include/ui/ScrollBar.h + include/ui/SnackBar.h + include/ui/RaisedButton.h + include/ui/Ripple.h + include/ui/RippleOverlay.h + include/ui/TextField.h + include/ui/ToggleButton.h + include/ui/Theme.h + include/ui/ThemeManager.h + include/AvatarProvider.h include/ChatPage.h - include/EmojiCategory.h - include/EmojiItemDelegate.h - include/EmojiPanel.h - include/EmojiPickButton.h - include/ui/FloatingButton.h - include/FileItem.h - include/ImageItem.h - include/ImageOverlayDialog.h - include/JoinRoomDialog.h - include/LeaveRoomDialog.h include/LoginPage.h - include/LogoutDialog.h include/MainWindow.h include/MatrixClient.h include/QuickSwitcher.h @@ -258,33 +292,14 @@ qt5_wrap_cpp(MOC_HEADERS include/RoomInfoListItem.h include/RoomList.h include/SideBarActions.h - include/UserSettingsPage.h include/Splitter.h include/TextInputWidget.h - include/TimelineItem.h - include/TimelineView.h - include/TimelineViewManager.h include/TopRoomBar.h include/TrayIcon.h include/TypingDisplay.h include/UserInfoWidget.h + include/UserSettingsPage.h include/WelcomePage.h - - include/ui/Avatar.h - include/ui/Badge.h - include/ui/LoadingIndicator.h - include/ui/FlatButton.h - include/ui/Label.h - include/ui/OverlayWidget.h - include/ui/ScrollBar.h - include/ui/SnackBar.h - include/ui/RaisedButton.h - include/ui/Ripple.h - include/ui/RippleOverlay.h - include/ui/TextField.h - include/ui/ToggleButton.h - include/ui/Theme.h - include/ui/ThemeManager.h ) # diff --git a/include/EmojiCategory.h b/include/EmojiCategory.h deleted file mode 100644 index be0110f2..00000000 --- a/include/EmojiCategory.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#pragma once - -#include -#include -#include -#include - -#include "EmojiItemDelegate.h" - -class EmojiProvider; - -class EmojiCategory : public QWidget -{ - Q_OBJECT - -public: - EmojiCategory(QString category, QList emoji, QWidget *parent = nullptr); - ~EmojiCategory(); - -signals: - void emojiSelected(const QString &emoji); - -protected: - void paintEvent(QPaintEvent *event) override; - -private slots: - void clickIndex(const QModelIndex &index) - { - emit emojiSelected(index.data(Qt::UserRole).toString()); - }; - -private: - QVBoxLayout *mainLayout_; - - QStandardItemModel *itemModel_; - QListView *emojiListView_; - - Emoji *data_; - EmojiItemDelegate *delegate_; - - QLabel *category_; -}; diff --git a/include/EmojiItemDelegate.h b/include/EmojiItemDelegate.h deleted file mode 100644 index 15d61f49..00000000 --- a/include/EmojiItemDelegate.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#pragma once - -#include -#include -#include - -#include "EmojiProvider.h" - -class EmojiItemDelegate : public QStyledItemDelegate -{ - Q_OBJECT - -public: - explicit EmojiItemDelegate(QObject *parent = nullptr); - ~EmojiItemDelegate(); - - void paint(QPainter *painter, - const QStyleOptionViewItem &option, - const QModelIndex &index) const override; - -private: - Emoji *data_; -}; diff --git a/include/EmojiPanel.h b/include/EmojiPanel.h deleted file mode 100644 index 9eecc8e0..00000000 --- a/include/EmojiPanel.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#pragma once - -#include - -#include "EmojiProvider.h" - -class EmojiCategory; - -class EmojiPanel : public QWidget -{ - Q_OBJECT - -public: - EmojiPanel(QWidget *parent = nullptr); - -signals: - void mouseLeft(); - void emojiSelected(const QString &emoji); - -protected: - void leaveEvent(QEvent *event); - void paintEvent(QPaintEvent *event); - -private: - void showEmojiCategory(const EmojiCategory *category); - - EmojiProvider emoji_provider_; - - QScrollArea *scrollArea_; - - int shadowMargin_; - - // Panel dimensions. - int width_; - int height_; - - int categoryIconSize_; -}; diff --git a/include/EmojiPickButton.h b/include/EmojiPickButton.h deleted file mode 100644 index 8ef9be9e..00000000 --- a/include/EmojiPickButton.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#pragma once - -#include -#include - -#include "FlatButton.h" - -class EmojiPanel; - -class EmojiPickButton : public FlatButton -{ - Q_OBJECT -public: - explicit EmojiPickButton(QWidget *parent = nullptr); - -signals: - void emojiSelected(const QString &emoji); - -protected: - void enterEvent(QEvent *e) override; - void leaveEvent(QEvent *e) override; - -private: - // Vertical distance from panel's bottom. - int vertical_distance_ = 10; - - // Horizontal distance from panel's bottom right corner. - int horizontal_distance_ = 70; - - QSharedPointer panel_; -}; diff --git a/include/EmojiProvider.h b/include/EmojiProvider.h deleted file mode 100644 index 847157fd..00000000 --- a/include/EmojiProvider.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#pragma once - -#include - -struct Emoji -{ - // Unicode code. - QString unicode; - // Keyboard shortcut e.g :emoji: - QString shortname; -}; - -class EmojiProvider -{ -public: - static const QList people; - static const QList nature; - static const QList food; - static const QList activity; - static const QList travel; - static const QList objects; - static const QList symbols; - static const QList flags; -}; diff --git a/include/FileItem.h b/include/FileItem.h deleted file mode 100644 index ebb18111..00000000 --- a/include/FileItem.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#pragma once - -#include -#include -#include -#include -#include - -#include "File.h" -#include "MatrixClient.h" -#include "MessageEvent.h" - -namespace events = matrix::events; -namespace msgs = matrix::events::messages; - -constexpr int MaxWidth = 400; -constexpr int Height = 70; -constexpr int IconRadius = 22; -constexpr int IconDiameter = IconRadius * 2; -constexpr int HorizontalPadding = 12; -constexpr int TextPadding = 15; -constexpr int DownloadIconRadius = IconRadius - 4; - -constexpr double VerticalPadding = Height - 2 * IconRadius; -constexpr double IconYCenter = Height / 2; -constexpr double IconXCenter = HorizontalPadding + IconRadius; - -class FileItem : public QWidget -{ - Q_OBJECT - - Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor) - Q_PROPERTY(QColor iconColor WRITE setIconColor READ iconColor) - Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor) - -public: - FileItem(QSharedPointer client, - const events::MessageEvent &event, - QWidget *parent = nullptr); - - FileItem(QSharedPointer client, - const QString &url, - const QString &filename, - QWidget *parent = nullptr); - - QSize sizeHint() const override; - - void setTextColor(const QColor &color) { textColor_ = color; } - void setIconColor(const QColor &color) { iconColor_ = color; } - void setBackgroundColor(const QColor &color) { backgroundColor_ = color; } - - QColor textColor() const { return textColor_; } - QColor iconColor() const { return iconColor_; } - QColor backgroundColor() const { return backgroundColor_; } - -protected: - void paintEvent(QPaintEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; - -private slots: - void fileDownloaded(const QString &event_id, const QByteArray &data); - -private: - QString calculateFileSize(int nbytes) const; - void openUrl(); - void init(); - - QUrl url_; - QString text_; - QString readableFileSize_; - QString filenameToSave_; - - events::MessageEvent event_; - QSharedPointer client_; - - QIcon icon_; - - QColor textColor_ = QColor("white"); - QColor iconColor_ = QColor("#38A3D8"); - QColor backgroundColor_ = QColor("#333"); -}; diff --git a/include/ImageItem.h b/include/ImageItem.h deleted file mode 100644 index c4f6998a..00000000 --- a/include/ImageItem.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#pragma once - -#include -#include -#include -#include - -#include "Image.h" -#include "MatrixClient.h" -#include "MessageEvent.h" - -namespace events = matrix::events; -namespace msgs = matrix::events::messages; - -class ImageItem : public QWidget -{ - Q_OBJECT -public: - ImageItem(QSharedPointer client, - const events::MessageEvent &event, - QWidget *parent = nullptr); - - ImageItem(QSharedPointer client, - const QString &url, - const QString &filename, - QWidget *parent = nullptr); - - void setImage(const QPixmap &image); - - QSize sizeHint() const override; - -protected: - void paintEvent(QPaintEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; - void resizeEvent(QResizeEvent *event) override; - -private slots: - void imageDownloaded(const QString &event_id, const QPixmap &img); - -private: - void scaleImage(); - void openUrl(); - - int max_width_ = 500; - int max_height_ = 300; - - int width_; - int height_; - - QPixmap scaled_image_; - QPixmap image_; - - QUrl url_; - QString text_; - - int bottom_height_ = 30; - - events::MessageEvent event_; - - QSharedPointer client_; -}; diff --git a/include/ImageOverlayDialog.h b/include/ImageOverlayDialog.h deleted file mode 100644 index 5159c665..00000000 --- a/include/ImageOverlayDialog.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#pragma once - -#include -#include -#include - -class ImageOverlayDialog : public QWidget -{ - Q_OBJECT -public: - ImageOverlayDialog(QPixmap image, QWidget *parent = nullptr); - -protected: - void mousePressEvent(QMouseEvent *event) override; - void paintEvent(QPaintEvent *event) override; - -signals: - void closing(); - -private: - void scaleImage(int width, int height); - - QPixmap originalImage_; - QPixmap image_; - - QRect content_; - QRect close_button_; - QRect screen_; -}; diff --git a/include/JoinRoomDialog.h b/include/JoinRoomDialog.h deleted file mode 100644 index 84184733..00000000 --- a/include/JoinRoomDialog.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include -#include - -class FlatButton; - -class JoinRoomDialog : public QFrame -{ - Q_OBJECT -public: - JoinRoomDialog(QWidget *parent = nullptr); - -signals: - void closing(bool isJoining, QString roomAlias); - -private: - FlatButton *confirmBtn_; - FlatButton *cancelBtn_; - - QLineEdit *roomAliasEdit_; -}; diff --git a/include/LeaveRoomDialog.h b/include/LeaveRoomDialog.h deleted file mode 100644 index eab022f7..00000000 --- a/include/LeaveRoomDialog.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include - -class FlatButton; - -class LeaveRoomDialog : public QFrame -{ - Q_OBJECT -public: - explicit LeaveRoomDialog(QWidget *parent = nullptr); - -protected: - void paintEvent(QPaintEvent *event) override; - -signals: - void closing(bool isLeaving); - -private: - FlatButton *confirmBtn_; - FlatButton *cancelBtn_; -}; diff --git a/include/LogoutDialog.h b/include/LogoutDialog.h deleted file mode 100644 index a1a5ee4f..00000000 --- a/include/LogoutDialog.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#pragma once - -#include - -class FlatButton; - -class LogoutDialog : public QFrame -{ - Q_OBJECT -public: - explicit LogoutDialog(QWidget *parent = nullptr); - -protected: - void paintEvent(QPaintEvent *event) override; - -signals: - void closing(bool isLoggingOut); - -private: - FlatButton *confirmBtn_; - FlatButton *cancelBtn_; -}; diff --git a/include/TextInputWidget.h b/include/TextInputWidget.h index 80c16740..ede7b18a 100644 --- a/include/TextInputWidget.h +++ b/include/TextInputWidget.h @@ -24,11 +24,12 @@ #include #include -#include "EmojiPickButton.h" #include "FlatButton.h" #include "Image.h" #include "LoadingIndicator.h" +#include "emoji/EmojiPickButton.h" + namespace msgs = matrix::events::messages; class FilteredTextEdit : public QTextEdit diff --git a/include/TimelineItem.h b/include/TimelineItem.h deleted file mode 100644 index 9646405c..00000000 --- a/include/TimelineItem.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "AvatarProvider.h" -#include "Emote.h" -#include "File.h" -#include "Image.h" -#include "MessageEvent.h" -#include "Notice.h" -#include "RoomInfoListItem.h" -#include "Text.h" -#include "TimelineViewManager.h" - -class ImageItem; -class FileItem; -class Avatar; - -namespace events = matrix::events; -namespace msgs = matrix::events::messages; - -class TimelineItem : public QWidget -{ - Q_OBJECT -public: - TimelineItem(const events::MessageEvent &e, - bool with_sender, - QWidget *parent = 0); - TimelineItem(const events::MessageEvent &e, - bool with_sender, - QWidget *parent = 0); - TimelineItem(const events::MessageEvent &e, - bool with_sender, - QWidget *parent = 0); - - // For local messages. - // m.text & m.emote - TimelineItem(events::MessageEventType ty, - const QString &userid, - QString body, - bool withSender, - QWidget *parent = 0); - // m.image - TimelineItem(ImageItem *item, const QString &userid, bool withSender, QWidget *parent = 0); - TimelineItem(FileItem *item, const QString &userid, bool withSender, QWidget *parent = 0); - - TimelineItem(ImageItem *img, - const events::MessageEvent &e, - bool with_sender, - QWidget *parent); - TimelineItem(FileItem *file, - const events::MessageEvent &e, - bool with_sender, - QWidget *parent); - - void setUserAvatar(const QImage &pixmap); - DescInfo descriptionMessage() const { return descriptionMsg_; } - QString eventId() const { return event_id_; } - - ~TimelineItem(); - -protected: - void paintEvent(QPaintEvent *event) override; - -private: - void init(); - - template - void setupLocalWidgetLayout(Widget *widget, - const QString &userid, - const QString &msgDescription, - bool withSender); - - void generateBody(const QString &body); - void generateBody(const QString &userid, const QString &body); - void generateTimestamp(const QDateTime &time); - QString descriptiveTime(const QDateTime &then); - - void setupAvatarLayout(const QString &userName); - void setupSimpleLayout(); - - QString replaceEmoji(const QString &body); - QString event_id_; - - DescInfo descriptionMsg_; - - QHBoxLayout *topLayout_; - QVBoxLayout *sideLayout_; // Avatar or Timestamp - QVBoxLayout *mainLayout_; // Header & Message body - - QHBoxLayout *headerLayout_; // Username (&) Timestamp - - Avatar *userAvatar_; - - QFont font_; - - QLabel *timestamp_; - QLabel *userName_; - QLabel *body_; -}; - -template -void -TimelineItem::setupLocalWidgetLayout(Widget *widget, - const QString &userid, - const QString &msgDescription, - bool withSender) -{ - auto displayName = TimelineViewManager::displayName(userid); - auto timestamp = QDateTime::currentDateTime(); - - descriptionMsg_ = { - "You", userid, QString(" %1").arg(msgDescription), descriptiveTime(timestamp)}; - - generateTimestamp(timestamp); - - auto widgetLayout = new QHBoxLayout(); - widgetLayout->setContentsMargins(0, 5, 0, 0); - widgetLayout->addWidget(widget); - widgetLayout->addStretch(1); - - if (withSender) { - generateBody(displayName, ""); - setupAvatarLayout(displayName); - mainLayout_->addLayout(headerLayout_); - - AvatarProvider::resolve(userid, this); - } else { - setupSimpleLayout(); - } - - mainLayout_->addLayout(widgetLayout); -} diff --git a/include/TimelineView.h b/include/TimelineView.h deleted file mode 100644 index 898a304e..00000000 --- a/include/TimelineView.h +++ /dev/null @@ -1,310 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Emote.h" -#include "File.h" -#include "Image.h" -#include "MatrixClient.h" -#include "MessageEvent.h" -#include "Notice.h" -#include "Text.h" -#include "TimelineItem.h" - -class FloatingButton; -class RoomMessages; -class ScrollBar; -class Timeline; -struct DescInfo; - -namespace msgs = matrix::events::messages; -namespace events = matrix::events; - -// Contains info about a message shown in the history view -// but not yet confirmed by the homeserver through sync. -struct PendingMessage -{ - matrix::events::MessageEventType ty; - int txn_id; - QString body; - QString filename; - QString event_id; - TimelineItem *widget; - - PendingMessage(matrix::events::MessageEventType ty, - int txn_id, - QString body, - QString filename, - QString event_id, - TimelineItem *widget) - : ty(ty) - , txn_id(txn_id) - , body(body) - , filename(filename) - , event_id(event_id) - , widget(widget) - {} -}; - -// In which place new TimelineItems should be inserted. -enum class TimelineDirection -{ - Top, - Bottom, -}; - -class TimelineView : public QWidget -{ - Q_OBJECT - -public: - TimelineView(const Timeline &timeline, - QSharedPointer client, - const QString &room_id, - QWidget *parent = 0); - TimelineView(QSharedPointer client, - const QString &room_id, - QWidget *parent = 0); - - // Add new events at the end of the timeline. - int addEvents(const Timeline &timeline); - void addUserMessage(matrix::events::MessageEventType ty, const QString &msg); - - template - void addUserMessage(const QString &url, const QString &filename); - void updatePendingMessage(int txn_id, QString event_id); - void scrollDown(); - -public slots: - void sliderRangeChanged(int min, int max); - void sliderMoved(int position); - void fetchHistory(); - - // Add old events at the top of the timeline. - void addBackwardsEvents(const QString &room_id, const RoomMessages &msgs); - - // Whether or not the initial batch has been loaded. - bool hasLoaded() { return scroll_layout_->count() > 1 || isTimelineFinished; } - - void handleFailedMessage(int txnid); - -private slots: - void sendNextPendingMessage(); - -signals: - void updateLastTimelineMessage(const QString &user, const DescInfo &info); - void clearUnreadMessageCount(const QString &room_id); - -protected: - void paintEvent(QPaintEvent *event) override; - void showEvent(QShowEvent *event) override; - bool event(QEvent *event) override; - -private: - void init(); - void addTimelineItem(TimelineItem *item, TimelineDirection direction); - void updateLastSender(const QString &user_id, TimelineDirection direction); - void notifyForLastEvent(); - void readLastEvent() const; - QString getLastEventId() const; - - template - TimelineItem *processMessageEvent(const QJsonObject &event, TimelineDirection direction); - - // TODO: Remove this eventually. - template - TimelineItem *processMessageEvent(const QJsonObject &event, TimelineDirection direction); - - // For events with custom display widgets. - template - TimelineItem *createTimelineItem(const Event &event, bool withSender); - - // For events without custom display widgets. - // TODO: All events should have custom widgets. - template - TimelineItem *createTimelineItem(const Event &event, bool withSender); - - // Used to determine whether or not we should prefix a message with the - // sender's name. - bool isSenderRendered(const QString &user_id, TimelineDirection direction); - - bool isPendingMessage(const QString &txnid, const QString &sender, const QString &userid); - void removePendingMessage(const QString &txnid); - - bool isDuplicate(const QString &event_id) { return eventIds_.contains(event_id); } - - void handleNewUserMessage(PendingMessage msg); - - // Return nullptr if the event couldn't be parsed. - TimelineItem *parseMessageEvent(const QJsonObject &event, TimelineDirection direction); - - QVBoxLayout *top_layout_; - QVBoxLayout *scroll_layout_; - - QScrollArea *scroll_area_; - ScrollBar *scrollbar_; - QWidget *scroll_widget_; - - QString lastSender_; - QString firstSender_; - QString room_id_; - QString prev_batch_token_; - QString local_user_; - - bool isPaginationInProgress_ = false; - - // Keeps track whether or not the user has visited the view. - bool isInitialized = false; - bool isTimelineFinished = false; - bool isInitialSync = true; - - const int SCROLL_BAR_GAP = 200; - - QTimer *paginationTimer_; - - int scroll_height_ = 0; - int previous_max_height_ = 0; - - int oldPosition_; - int oldHeight_; - - FloatingButton *scrollDownBtn_; - - TimelineDirection lastMessageDirection_; - - // The events currently rendered. Used for duplicate detection. - QMap eventIds_; - QQueue pending_msgs_; - QList pending_sent_msgs_; - QSharedPointer client_; -}; - -template -void -TimelineView::addUserMessage(const QString &url, const QString &filename) -{ - QSettings settings; - auto user_id = settings.value("auth/user_id").toString(); - auto with_sender = lastSender_ != user_id; - - auto widget = new Widget(client_, url, filename, this); - - TimelineItem *view_item = new TimelineItem(widget, user_id, with_sender, scroll_widget_); - scroll_layout_->addWidget(view_item); - - lastMessageDirection_ = TimelineDirection::Bottom; - - QApplication::processEvents(); - - lastSender_ = user_id; - - int txn_id = client_->incrementTransactionId(); - - PendingMessage message(MsgType, txn_id, url, filename, "", view_item); - handleNewUserMessage(message); -} - -template -TimelineItem * -TimelineView::createTimelineItem(const Event &event, bool withSender) -{ - TimelineItem *item = new TimelineItem(event, withSender, scroll_widget_); - return item; -} - -template -TimelineItem * -TimelineView::createTimelineItem(const Event &event, bool withSender) -{ - auto eventWidget = new Widget(client_, event); - auto item = new TimelineItem(eventWidget, event, withSender, scroll_widget_); - - return item; -} - -template -TimelineItem * -TimelineView::processMessageEvent(const QJsonObject &data, TimelineDirection direction) -{ - Event event; - - try { - event.deserialize(data); - } catch (const DeserializationException &e) { - qWarning() << e.what() << data; - return nullptr; - } - - if (isDuplicate(event.eventId())) - return nullptr; - - eventIds_[event.eventId()] = true; - - QString txnid = event.unsignedData().transactionId(); - if (!txnid.isEmpty() && isPendingMessage(txnid, event.sender(), local_user_)) { - removePendingMessage(txnid); - return nullptr; - } - - auto with_sender = isSenderRendered(event.sender(), direction); - - updateLastSender(event.sender(), direction); - - return createTimelineItem(event, with_sender); -} - -template -TimelineItem * -TimelineView::processMessageEvent(const QJsonObject &data, TimelineDirection direction) -{ - Event event; - - try { - event.deserialize(data); - } catch (const DeserializationException &e) { - qWarning() << e.what() << data; - return nullptr; - } - - if (isDuplicate(event.eventId())) - return nullptr; - - eventIds_[event.eventId()] = true; - - QString txnid = event.unsignedData().transactionId(); - if (!txnid.isEmpty() && isPendingMessage(txnid, event.sender(), local_user_)) { - removePendingMessage(txnid); - return nullptr; - } - - auto with_sender = isSenderRendered(event.sender(), direction); - - updateLastSender(event.sender(), direction); - - return createTimelineItem(event, with_sender); -} diff --git a/include/TimelineViewManager.h b/include/TimelineViewManager.h deleted file mode 100644 index 854c2550..00000000 --- a/include/TimelineViewManager.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#pragma once - -#include -#include -#include - -#include "MessageEvent.h" - -class JoinedRoom; -class MatrixClient; -class RoomInfoListItem; -class Rooms; -class TimelineView; -struct DescInfo; - -class TimelineViewManager : public QStackedWidget -{ - Q_OBJECT - -public: - TimelineViewManager(QSharedPointer client, QWidget *parent); - ~TimelineViewManager(); - - // Initialize with timeline events. - void initialize(const Rooms &rooms); - // Empty initialization. - void initialize(const QList &rooms); - - void addRoom(const JoinedRoom &room, const QString &room_id); - void addRoom(const QString &room_id); - - void sync(const Rooms &rooms); - void clearAll(); - - // Check if all the timelines have been loaded. - bool hasLoaded() const; - - static QString chooseRandomColor(); - static QString displayName(const QString &userid); - - static QMap DISPLAY_NAMES; - -signals: - void clearRoomMessageCount(QString roomid); - void unreadMessages(QString roomid, int count); - void updateRoomsLastMessage(const QString &user, const DescInfo &info); - -public slots: - void setHistoryView(const QString &room_id); - void queueTextMessage(const QString &msg); - void queueEmoteMessage(const QString &msg); - void queueImageMessage(const QString &roomid, const QString &filename, const QString &url); - void queueFileMessage(const QString &roomid, const QString &filename, const QString &url); - -private slots: - void messageSent(const QString &eventid, const QString &roomid, int txnid); - void messageSendFailed(const QString &roomid, int txnid); - -private: - QString active_room_; - QMap> views_; - QSharedPointer client_; -}; diff --git a/include/dialogs/ImageOverlayDialog.h b/include/dialogs/ImageOverlayDialog.h new file mode 100644 index 00000000..5159c665 --- /dev/null +++ b/include/dialogs/ImageOverlayDialog.h @@ -0,0 +1,46 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#pragma once + +#include +#include +#include + +class ImageOverlayDialog : public QWidget +{ + Q_OBJECT +public: + ImageOverlayDialog(QPixmap image, QWidget *parent = nullptr); + +protected: + void mousePressEvent(QMouseEvent *event) override; + void paintEvent(QPaintEvent *event) override; + +signals: + void closing(); + +private: + void scaleImage(int width, int height); + + QPixmap originalImage_; + QPixmap image_; + + QRect content_; + QRect close_button_; + QRect screen_; +}; diff --git a/include/dialogs/JoinRoomDialog.h b/include/dialogs/JoinRoomDialog.h new file mode 100644 index 00000000..84184733 --- /dev/null +++ b/include/dialogs/JoinRoomDialog.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +class FlatButton; + +class JoinRoomDialog : public QFrame +{ + Q_OBJECT +public: + JoinRoomDialog(QWidget *parent = nullptr); + +signals: + void closing(bool isJoining, QString roomAlias); + +private: + FlatButton *confirmBtn_; + FlatButton *cancelBtn_; + + QLineEdit *roomAliasEdit_; +}; diff --git a/include/dialogs/LeaveRoomDialog.h b/include/dialogs/LeaveRoomDialog.h new file mode 100644 index 00000000..eab022f7 --- /dev/null +++ b/include/dialogs/LeaveRoomDialog.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +class FlatButton; + +class LeaveRoomDialog : public QFrame +{ + Q_OBJECT +public: + explicit LeaveRoomDialog(QWidget *parent = nullptr); + +protected: + void paintEvent(QPaintEvent *event) override; + +signals: + void closing(bool isLeaving); + +private: + FlatButton *confirmBtn_; + FlatButton *cancelBtn_; +}; diff --git a/include/dialogs/LogoutDialog.h b/include/dialogs/LogoutDialog.h new file mode 100644 index 00000000..a1a5ee4f --- /dev/null +++ b/include/dialogs/LogoutDialog.h @@ -0,0 +1,39 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#pragma once + +#include + +class FlatButton; + +class LogoutDialog : public QFrame +{ + Q_OBJECT +public: + explicit LogoutDialog(QWidget *parent = nullptr); + +protected: + void paintEvent(QPaintEvent *event) override; + +signals: + void closing(bool isLoggingOut); + +private: + FlatButton *confirmBtn_; + FlatButton *cancelBtn_; +}; diff --git a/include/emoji/EmojiCategory.h b/include/emoji/EmojiCategory.h new file mode 100644 index 00000000..be0110f2 --- /dev/null +++ b/include/emoji/EmojiCategory.h @@ -0,0 +1,59 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#pragma once + +#include +#include +#include +#include + +#include "EmojiItemDelegate.h" + +class EmojiProvider; + +class EmojiCategory : public QWidget +{ + Q_OBJECT + +public: + EmojiCategory(QString category, QList emoji, QWidget *parent = nullptr); + ~EmojiCategory(); + +signals: + void emojiSelected(const QString &emoji); + +protected: + void paintEvent(QPaintEvent *event) override; + +private slots: + void clickIndex(const QModelIndex &index) + { + emit emojiSelected(index.data(Qt::UserRole).toString()); + }; + +private: + QVBoxLayout *mainLayout_; + + QStandardItemModel *itemModel_; + QListView *emojiListView_; + + Emoji *data_; + EmojiItemDelegate *delegate_; + + QLabel *category_; +}; diff --git a/include/emoji/EmojiItemDelegate.h b/include/emoji/EmojiItemDelegate.h new file mode 100644 index 00000000..15d61f49 --- /dev/null +++ b/include/emoji/EmojiItemDelegate.h @@ -0,0 +1,40 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#pragma once + +#include +#include +#include + +#include "EmojiProvider.h" + +class EmojiItemDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + explicit EmojiItemDelegate(QObject *parent = nullptr); + ~EmojiItemDelegate(); + + void paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + +private: + Emoji *data_; +}; diff --git a/include/emoji/EmojiPanel.h b/include/emoji/EmojiPanel.h new file mode 100644 index 00000000..9eecc8e0 --- /dev/null +++ b/include/emoji/EmojiPanel.h @@ -0,0 +1,55 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#pragma once + +#include + +#include "EmojiProvider.h" + +class EmojiCategory; + +class EmojiPanel : public QWidget +{ + Q_OBJECT + +public: + EmojiPanel(QWidget *parent = nullptr); + +signals: + void mouseLeft(); + void emojiSelected(const QString &emoji); + +protected: + void leaveEvent(QEvent *event); + void paintEvent(QPaintEvent *event); + +private: + void showEmojiCategory(const EmojiCategory *category); + + EmojiProvider emoji_provider_; + + QScrollArea *scrollArea_; + + int shadowMargin_; + + // Panel dimensions. + int width_; + int height_; + + int categoryIconSize_; +}; diff --git a/include/emoji/EmojiPickButton.h b/include/emoji/EmojiPickButton.h new file mode 100644 index 00000000..8ef9be9e --- /dev/null +++ b/include/emoji/EmojiPickButton.h @@ -0,0 +1,48 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#pragma once + +#include +#include + +#include "FlatButton.h" + +class EmojiPanel; + +class EmojiPickButton : public FlatButton +{ + Q_OBJECT +public: + explicit EmojiPickButton(QWidget *parent = nullptr); + +signals: + void emojiSelected(const QString &emoji); + +protected: + void enterEvent(QEvent *e) override; + void leaveEvent(QEvent *e) override; + +private: + // Vertical distance from panel's bottom. + int vertical_distance_ = 10; + + // Horizontal distance from panel's bottom right corner. + int horizontal_distance_ = 70; + + QSharedPointer panel_; +}; diff --git a/include/emoji/EmojiProvider.h b/include/emoji/EmojiProvider.h new file mode 100644 index 00000000..847157fd --- /dev/null +++ b/include/emoji/EmojiProvider.h @@ -0,0 +1,41 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#pragma once + +#include + +struct Emoji +{ + // Unicode code. + QString unicode; + // Keyboard shortcut e.g :emoji: + QString shortname; +}; + +class EmojiProvider +{ +public: + static const QList people; + static const QList nature; + static const QList food; + static const QList activity; + static const QList travel; + static const QList objects; + static const QList symbols; + static const QList flags; +}; diff --git a/include/timeline/TimelineItem.h b/include/timeline/TimelineItem.h new file mode 100644 index 00000000..9646405c --- /dev/null +++ b/include/timeline/TimelineItem.h @@ -0,0 +1,155 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "AvatarProvider.h" +#include "Emote.h" +#include "File.h" +#include "Image.h" +#include "MessageEvent.h" +#include "Notice.h" +#include "RoomInfoListItem.h" +#include "Text.h" +#include "TimelineViewManager.h" + +class ImageItem; +class FileItem; +class Avatar; + +namespace events = matrix::events; +namespace msgs = matrix::events::messages; + +class TimelineItem : public QWidget +{ + Q_OBJECT +public: + TimelineItem(const events::MessageEvent &e, + bool with_sender, + QWidget *parent = 0); + TimelineItem(const events::MessageEvent &e, + bool with_sender, + QWidget *parent = 0); + TimelineItem(const events::MessageEvent &e, + bool with_sender, + QWidget *parent = 0); + + // For local messages. + // m.text & m.emote + TimelineItem(events::MessageEventType ty, + const QString &userid, + QString body, + bool withSender, + QWidget *parent = 0); + // m.image + TimelineItem(ImageItem *item, const QString &userid, bool withSender, QWidget *parent = 0); + TimelineItem(FileItem *item, const QString &userid, bool withSender, QWidget *parent = 0); + + TimelineItem(ImageItem *img, + const events::MessageEvent &e, + bool with_sender, + QWidget *parent); + TimelineItem(FileItem *file, + const events::MessageEvent &e, + bool with_sender, + QWidget *parent); + + void setUserAvatar(const QImage &pixmap); + DescInfo descriptionMessage() const { return descriptionMsg_; } + QString eventId() const { return event_id_; } + + ~TimelineItem(); + +protected: + void paintEvent(QPaintEvent *event) override; + +private: + void init(); + + template + void setupLocalWidgetLayout(Widget *widget, + const QString &userid, + const QString &msgDescription, + bool withSender); + + void generateBody(const QString &body); + void generateBody(const QString &userid, const QString &body); + void generateTimestamp(const QDateTime &time); + QString descriptiveTime(const QDateTime &then); + + void setupAvatarLayout(const QString &userName); + void setupSimpleLayout(); + + QString replaceEmoji(const QString &body); + QString event_id_; + + DescInfo descriptionMsg_; + + QHBoxLayout *topLayout_; + QVBoxLayout *sideLayout_; // Avatar or Timestamp + QVBoxLayout *mainLayout_; // Header & Message body + + QHBoxLayout *headerLayout_; // Username (&) Timestamp + + Avatar *userAvatar_; + + QFont font_; + + QLabel *timestamp_; + QLabel *userName_; + QLabel *body_; +}; + +template +void +TimelineItem::setupLocalWidgetLayout(Widget *widget, + const QString &userid, + const QString &msgDescription, + bool withSender) +{ + auto displayName = TimelineViewManager::displayName(userid); + auto timestamp = QDateTime::currentDateTime(); + + descriptionMsg_ = { + "You", userid, QString(" %1").arg(msgDescription), descriptiveTime(timestamp)}; + + generateTimestamp(timestamp); + + auto widgetLayout = new QHBoxLayout(); + widgetLayout->setContentsMargins(0, 5, 0, 0); + widgetLayout->addWidget(widget); + widgetLayout->addStretch(1); + + if (withSender) { + generateBody(displayName, ""); + setupAvatarLayout(displayName); + mainLayout_->addLayout(headerLayout_); + + AvatarProvider::resolve(userid, this); + } else { + setupSimpleLayout(); + } + + mainLayout_->addLayout(widgetLayout); +} diff --git a/include/timeline/TimelineView.h b/include/timeline/TimelineView.h new file mode 100644 index 00000000..898a304e --- /dev/null +++ b/include/timeline/TimelineView.h @@ -0,0 +1,310 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Emote.h" +#include "File.h" +#include "Image.h" +#include "MatrixClient.h" +#include "MessageEvent.h" +#include "Notice.h" +#include "Text.h" +#include "TimelineItem.h" + +class FloatingButton; +class RoomMessages; +class ScrollBar; +class Timeline; +struct DescInfo; + +namespace msgs = matrix::events::messages; +namespace events = matrix::events; + +// Contains info about a message shown in the history view +// but not yet confirmed by the homeserver through sync. +struct PendingMessage +{ + matrix::events::MessageEventType ty; + int txn_id; + QString body; + QString filename; + QString event_id; + TimelineItem *widget; + + PendingMessage(matrix::events::MessageEventType ty, + int txn_id, + QString body, + QString filename, + QString event_id, + TimelineItem *widget) + : ty(ty) + , txn_id(txn_id) + , body(body) + , filename(filename) + , event_id(event_id) + , widget(widget) + {} +}; + +// In which place new TimelineItems should be inserted. +enum class TimelineDirection +{ + Top, + Bottom, +}; + +class TimelineView : public QWidget +{ + Q_OBJECT + +public: + TimelineView(const Timeline &timeline, + QSharedPointer client, + const QString &room_id, + QWidget *parent = 0); + TimelineView(QSharedPointer client, + const QString &room_id, + QWidget *parent = 0); + + // Add new events at the end of the timeline. + int addEvents(const Timeline &timeline); + void addUserMessage(matrix::events::MessageEventType ty, const QString &msg); + + template + void addUserMessage(const QString &url, const QString &filename); + void updatePendingMessage(int txn_id, QString event_id); + void scrollDown(); + +public slots: + void sliderRangeChanged(int min, int max); + void sliderMoved(int position); + void fetchHistory(); + + // Add old events at the top of the timeline. + void addBackwardsEvents(const QString &room_id, const RoomMessages &msgs); + + // Whether or not the initial batch has been loaded. + bool hasLoaded() { return scroll_layout_->count() > 1 || isTimelineFinished; } + + void handleFailedMessage(int txnid); + +private slots: + void sendNextPendingMessage(); + +signals: + void updateLastTimelineMessage(const QString &user, const DescInfo &info); + void clearUnreadMessageCount(const QString &room_id); + +protected: + void paintEvent(QPaintEvent *event) override; + void showEvent(QShowEvent *event) override; + bool event(QEvent *event) override; + +private: + void init(); + void addTimelineItem(TimelineItem *item, TimelineDirection direction); + void updateLastSender(const QString &user_id, TimelineDirection direction); + void notifyForLastEvent(); + void readLastEvent() const; + QString getLastEventId() const; + + template + TimelineItem *processMessageEvent(const QJsonObject &event, TimelineDirection direction); + + // TODO: Remove this eventually. + template + TimelineItem *processMessageEvent(const QJsonObject &event, TimelineDirection direction); + + // For events with custom display widgets. + template + TimelineItem *createTimelineItem(const Event &event, bool withSender); + + // For events without custom display widgets. + // TODO: All events should have custom widgets. + template + TimelineItem *createTimelineItem(const Event &event, bool withSender); + + // Used to determine whether or not we should prefix a message with the + // sender's name. + bool isSenderRendered(const QString &user_id, TimelineDirection direction); + + bool isPendingMessage(const QString &txnid, const QString &sender, const QString &userid); + void removePendingMessage(const QString &txnid); + + bool isDuplicate(const QString &event_id) { return eventIds_.contains(event_id); } + + void handleNewUserMessage(PendingMessage msg); + + // Return nullptr if the event couldn't be parsed. + TimelineItem *parseMessageEvent(const QJsonObject &event, TimelineDirection direction); + + QVBoxLayout *top_layout_; + QVBoxLayout *scroll_layout_; + + QScrollArea *scroll_area_; + ScrollBar *scrollbar_; + QWidget *scroll_widget_; + + QString lastSender_; + QString firstSender_; + QString room_id_; + QString prev_batch_token_; + QString local_user_; + + bool isPaginationInProgress_ = false; + + // Keeps track whether or not the user has visited the view. + bool isInitialized = false; + bool isTimelineFinished = false; + bool isInitialSync = true; + + const int SCROLL_BAR_GAP = 200; + + QTimer *paginationTimer_; + + int scroll_height_ = 0; + int previous_max_height_ = 0; + + int oldPosition_; + int oldHeight_; + + FloatingButton *scrollDownBtn_; + + TimelineDirection lastMessageDirection_; + + // The events currently rendered. Used for duplicate detection. + QMap eventIds_; + QQueue pending_msgs_; + QList pending_sent_msgs_; + QSharedPointer client_; +}; + +template +void +TimelineView::addUserMessage(const QString &url, const QString &filename) +{ + QSettings settings; + auto user_id = settings.value("auth/user_id").toString(); + auto with_sender = lastSender_ != user_id; + + auto widget = new Widget(client_, url, filename, this); + + TimelineItem *view_item = new TimelineItem(widget, user_id, with_sender, scroll_widget_); + scroll_layout_->addWidget(view_item); + + lastMessageDirection_ = TimelineDirection::Bottom; + + QApplication::processEvents(); + + lastSender_ = user_id; + + int txn_id = client_->incrementTransactionId(); + + PendingMessage message(MsgType, txn_id, url, filename, "", view_item); + handleNewUserMessage(message); +} + +template +TimelineItem * +TimelineView::createTimelineItem(const Event &event, bool withSender) +{ + TimelineItem *item = new TimelineItem(event, withSender, scroll_widget_); + return item; +} + +template +TimelineItem * +TimelineView::createTimelineItem(const Event &event, bool withSender) +{ + auto eventWidget = new Widget(client_, event); + auto item = new TimelineItem(eventWidget, event, withSender, scroll_widget_); + + return item; +} + +template +TimelineItem * +TimelineView::processMessageEvent(const QJsonObject &data, TimelineDirection direction) +{ + Event event; + + try { + event.deserialize(data); + } catch (const DeserializationException &e) { + qWarning() << e.what() << data; + return nullptr; + } + + if (isDuplicate(event.eventId())) + return nullptr; + + eventIds_[event.eventId()] = true; + + QString txnid = event.unsignedData().transactionId(); + if (!txnid.isEmpty() && isPendingMessage(txnid, event.sender(), local_user_)) { + removePendingMessage(txnid); + return nullptr; + } + + auto with_sender = isSenderRendered(event.sender(), direction); + + updateLastSender(event.sender(), direction); + + return createTimelineItem(event, with_sender); +} + +template +TimelineItem * +TimelineView::processMessageEvent(const QJsonObject &data, TimelineDirection direction) +{ + Event event; + + try { + event.deserialize(data); + } catch (const DeserializationException &e) { + qWarning() << e.what() << data; + return nullptr; + } + + if (isDuplicate(event.eventId())) + return nullptr; + + eventIds_[event.eventId()] = true; + + QString txnid = event.unsignedData().transactionId(); + if (!txnid.isEmpty() && isPendingMessage(txnid, event.sender(), local_user_)) { + removePendingMessage(txnid); + return nullptr; + } + + auto with_sender = isSenderRendered(event.sender(), direction); + + updateLastSender(event.sender(), direction); + + return createTimelineItem(event, with_sender); +} diff --git a/include/timeline/TimelineViewManager.h b/include/timeline/TimelineViewManager.h new file mode 100644 index 00000000..854c2550 --- /dev/null +++ b/include/timeline/TimelineViewManager.h @@ -0,0 +1,80 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#pragma once + +#include +#include +#include + +#include "MessageEvent.h" + +class JoinedRoom; +class MatrixClient; +class RoomInfoListItem; +class Rooms; +class TimelineView; +struct DescInfo; + +class TimelineViewManager : public QStackedWidget +{ + Q_OBJECT + +public: + TimelineViewManager(QSharedPointer client, QWidget *parent); + ~TimelineViewManager(); + + // Initialize with timeline events. + void initialize(const Rooms &rooms); + // Empty initialization. + void initialize(const QList &rooms); + + void addRoom(const JoinedRoom &room, const QString &room_id); + void addRoom(const QString &room_id); + + void sync(const Rooms &rooms); + void clearAll(); + + // Check if all the timelines have been loaded. + bool hasLoaded() const; + + static QString chooseRandomColor(); + static QString displayName(const QString &userid); + + static QMap DISPLAY_NAMES; + +signals: + void clearRoomMessageCount(QString roomid); + void unreadMessages(QString roomid, int count); + void updateRoomsLastMessage(const QString &user, const DescInfo &info); + +public slots: + void setHistoryView(const QString &room_id); + void queueTextMessage(const QString &msg); + void queueEmoteMessage(const QString &msg); + void queueImageMessage(const QString &roomid, const QString &filename, const QString &url); + void queueFileMessage(const QString &roomid, const QString &filename, const QString &url); + +private slots: + void messageSent(const QString &eventid, const QString &roomid, int txnid); + void messageSendFailed(const QString &roomid, int txnid); + +private: + QString active_room_; + QMap> views_; + QSharedPointer client_; +}; diff --git a/include/timeline/widgets/FileItem.h b/include/timeline/widgets/FileItem.h new file mode 100644 index 00000000..ebb18111 --- /dev/null +++ b/include/timeline/widgets/FileItem.h @@ -0,0 +1,98 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include "File.h" +#include "MatrixClient.h" +#include "MessageEvent.h" + +namespace events = matrix::events; +namespace msgs = matrix::events::messages; + +constexpr int MaxWidth = 400; +constexpr int Height = 70; +constexpr int IconRadius = 22; +constexpr int IconDiameter = IconRadius * 2; +constexpr int HorizontalPadding = 12; +constexpr int TextPadding = 15; +constexpr int DownloadIconRadius = IconRadius - 4; + +constexpr double VerticalPadding = Height - 2 * IconRadius; +constexpr double IconYCenter = Height / 2; +constexpr double IconXCenter = HorizontalPadding + IconRadius; + +class FileItem : public QWidget +{ + Q_OBJECT + + Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor) + Q_PROPERTY(QColor iconColor WRITE setIconColor READ iconColor) + Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor) + +public: + FileItem(QSharedPointer client, + const events::MessageEvent &event, + QWidget *parent = nullptr); + + FileItem(QSharedPointer client, + const QString &url, + const QString &filename, + QWidget *parent = nullptr); + + QSize sizeHint() const override; + + void setTextColor(const QColor &color) { textColor_ = color; } + void setIconColor(const QColor &color) { iconColor_ = color; } + void setBackgroundColor(const QColor &color) { backgroundColor_ = color; } + + QColor textColor() const { return textColor_; } + QColor iconColor() const { return iconColor_; } + QColor backgroundColor() const { return backgroundColor_; } + +protected: + void paintEvent(QPaintEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + +private slots: + void fileDownloaded(const QString &event_id, const QByteArray &data); + +private: + QString calculateFileSize(int nbytes) const; + void openUrl(); + void init(); + + QUrl url_; + QString text_; + QString readableFileSize_; + QString filenameToSave_; + + events::MessageEvent event_; + QSharedPointer client_; + + QIcon icon_; + + QColor textColor_ = QColor("white"); + QColor iconColor_ = QColor("#38A3D8"); + QColor backgroundColor_ = QColor("#333"); +}; diff --git a/include/timeline/widgets/ImageItem.h b/include/timeline/widgets/ImageItem.h new file mode 100644 index 00000000..c4f6998a --- /dev/null +++ b/include/timeline/widgets/ImageItem.h @@ -0,0 +1,78 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#pragma once + +#include +#include +#include +#include + +#include "Image.h" +#include "MatrixClient.h" +#include "MessageEvent.h" + +namespace events = matrix::events; +namespace msgs = matrix::events::messages; + +class ImageItem : public QWidget +{ + Q_OBJECT +public: + ImageItem(QSharedPointer client, + const events::MessageEvent &event, + QWidget *parent = nullptr); + + ImageItem(QSharedPointer client, + const QString &url, + const QString &filename, + QWidget *parent = nullptr); + + void setImage(const QPixmap &image); + + QSize sizeHint() const override; + +protected: + void paintEvent(QPaintEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + +private slots: + void imageDownloaded(const QString &event_id, const QPixmap &img); + +private: + void scaleImage(); + void openUrl(); + + int max_width_ = 500; + int max_height_ = 300; + + int width_; + int height_; + + QPixmap scaled_image_; + QPixmap image_; + + QUrl url_; + QString text_; + + int bottom_height_ = 30; + + events::MessageEvent event_; + + QSharedPointer client_; +}; diff --git a/src/AvatarProvider.cc b/src/AvatarProvider.cc index 7e8c9e49..334f72c3 100644 --- a/src/AvatarProvider.cc +++ b/src/AvatarProvider.cc @@ -17,7 +17,8 @@ #include "AvatarProvider.h" #include "MatrixClient.h" -#include "TimelineItem.h" + +#include "timeline/TimelineItem.h" QSharedPointer AvatarProvider::client_; diff --git a/src/ChatPage.cc b/src/ChatPage.cc index 5214d49a..340e75c3 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -35,11 +35,12 @@ #include "Sync.h" #include "TextInputWidget.h" #include "Theme.h" -#include "TimelineViewManager.h" #include "TopRoomBar.h" #include "TypingDisplay.h" #include "UserInfoWidget.h" +#include "timeline/TimelineViewManager.h" + constexpr int MAX_INITIAL_SYNC_FAILURES = 5; constexpr int SYNC_RETRY_TIMEOUT = 10000; diff --git a/src/EmojiCategory.cc b/src/EmojiCategory.cc deleted file mode 100644 index 98d79cc0..00000000 --- a/src/EmojiCategory.cc +++ /dev/null @@ -1,89 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#include -#include -#include - -#include "Config.h" -#include "EmojiCategory.h" - -EmojiCategory::EmojiCategory(QString category, QList emoji, QWidget *parent) - : QWidget(parent) -{ - mainLayout_ = new QVBoxLayout(this); - mainLayout_->setMargin(0); - mainLayout_->setSpacing(0); - - emojiListView_ = new QListView(); - itemModel_ = new QStandardItemModel(this); - - delegate_ = new EmojiItemDelegate(this); - data_ = new Emoji; - - emojiListView_->setItemDelegate(delegate_); - emojiListView_->setModel(itemModel_); - emojiListView_->setViewMode(QListView::IconMode); - emojiListView_->setFlow(QListView::LeftToRight); - emojiListView_->setResizeMode(QListView::Adjust); - emojiListView_->verticalScrollBar()->setEnabled(false); - emojiListView_->horizontalScrollBar()->setEnabled(false); - - const int cols = 7; - const int rows = emoji.size() / 7; - - // TODO: Be precise here. Take the parent into consideration. - emojiListView_->setFixedSize(cols * 50 + 20, rows * 50 + 20); - emojiListView_->setGridSize(QSize(50, 50)); - emojiListView_->setDragEnabled(false); - emojiListView_->setEditTriggers(QAbstractItemView::NoEditTriggers); - - for (const auto &e : emoji) { - data_->unicode = e.unicode; - - auto item = new QStandardItem; - item->setSizeHint(QSize(24, 24)); - - QVariant unicode(data_->unicode); - item->setData(unicode.toString(), Qt::UserRole); - - itemModel_->appendRow(item); - } - - QFont font("Open Sans SemiBold"); - font.setPixelSize(conf::fontSize); - - category_ = new QLabel(category, this); - category_->setFont(font); - category_->setStyleSheet("margin: 20px 0 20px 8px;"); - - mainLayout_->addWidget(category_); - mainLayout_->addWidget(emojiListView_); - - connect(emojiListView_, &QListView::clicked, this, &EmojiCategory::clickIndex); -} - -void -EmojiCategory::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - -EmojiCategory::~EmojiCategory() {} diff --git a/src/EmojiItemDelegate.cc b/src/EmojiItemDelegate.cc deleted file mode 100644 index 691bee17..00000000 --- a/src/EmojiItemDelegate.cc +++ /dev/null @@ -1,47 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#include -#include - -#include "EmojiItemDelegate.h" - -EmojiItemDelegate::EmojiItemDelegate(QObject *parent) - : QStyledItemDelegate(parent) -{ - data_ = new Emoji; -} - -EmojiItemDelegate::~EmojiItemDelegate() { delete data_; } - -void -EmojiItemDelegate::paint(QPainter *painter, - const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - Q_UNUSED(index); - - QStyleOptionViewItem viewOption(option); - - auto emoji = index.data(Qt::UserRole).toString(); - - QFont font("Emoji One"); - font.setPixelSize(19); - - painter->setFont(font); - painter->drawText(viewOption.rect, Qt::AlignCenter, emoji); -} diff --git a/src/EmojiPanel.cc b/src/EmojiPanel.cc deleted file mode 100644 index 0daaad68..00000000 --- a/src/EmojiPanel.cc +++ /dev/null @@ -1,244 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#include -#include -#include - -#include "DropShadow.h" -#include "EmojiCategory.h" -#include "EmojiPanel.h" -#include "FlatButton.h" - -EmojiPanel::EmojiPanel(QWidget *parent) - : QWidget(parent) - , shadowMargin_{2} - , width_{370} - , height_{350} - , categoryIconSize_{20} -{ - setStyleSheet("QWidget {border: none;}" - "QScrollBar:vertical { width: 0px; margin: 0px; }" - "QScrollBar::handle:vertical { min-height: 30px; }"); - - setAttribute(Qt::WA_TranslucentBackground, true); - setAttribute(Qt::WA_ShowWithoutActivating, true); - setWindowFlags(Qt::FramelessWindowHint | Qt::ToolTip); - - auto mainWidget = new QWidget(this); - mainWidget->setMaximumSize(width_, height_); - - auto topLayout = new QVBoxLayout(this); - topLayout->addWidget(mainWidget); - topLayout->setMargin(shadowMargin_); - topLayout->setSpacing(0); - - auto contentLayout = new QVBoxLayout(mainWidget); - contentLayout->setMargin(0); - contentLayout->setSpacing(0); - - auto emojiCategories = new QFrame(mainWidget); - emojiCategories->setStyleSheet( - QString("background-color: %1") - .arg(palette().color(QPalette::Window).darker(110).name())); - - auto categoriesLayout = new QHBoxLayout(emojiCategories); - categoriesLayout->setSpacing(0); - categoriesLayout->setMargin(0); - - QIcon icon; - - auto peopleCategory = new FlatButton(emojiCategories); - icon.addFile(":/icons/icons/emoji-categories/people.png"); - peopleCategory->setIcon(icon); - peopleCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); - - auto natureCategory_ = new FlatButton(emojiCategories); - icon.addFile(":/icons/icons/emoji-categories/nature.png"); - natureCategory_->setIcon(icon); - natureCategory_->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); - - auto foodCategory_ = new FlatButton(emojiCategories); - icon.addFile(":/icons/icons/emoji-categories/foods.png"); - foodCategory_->setIcon(icon); - foodCategory_->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); - - auto activityCategory = new FlatButton(emojiCategories); - icon.addFile(":/icons/icons/emoji-categories/activity.png"); - activityCategory->setIcon(icon); - activityCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); - - auto travelCategory = new FlatButton(emojiCategories); - icon.addFile(":/icons/icons/emoji-categories/travel.png"); - travelCategory->setIcon(icon); - travelCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); - - auto objectsCategory = new FlatButton(emojiCategories); - icon.addFile(":/icons/icons/emoji-categories/objects.png"); - objectsCategory->setIcon(icon); - objectsCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); - - auto symbolsCategory = new FlatButton(emojiCategories); - icon.addFile(":/icons/icons/emoji-categories/symbols.png"); - symbolsCategory->setIcon(icon); - symbolsCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); - - auto flagsCategory = new FlatButton(emojiCategories); - icon.addFile(":/icons/icons/emoji-categories/flags.png"); - flagsCategory->setIcon(icon); - flagsCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); - - categoriesLayout->addWidget(peopleCategory); - categoriesLayout->addWidget(natureCategory_); - categoriesLayout->addWidget(foodCategory_); - categoriesLayout->addWidget(activityCategory); - categoriesLayout->addWidget(travelCategory); - categoriesLayout->addWidget(objectsCategory); - categoriesLayout->addWidget(symbolsCategory); - categoriesLayout->addWidget(flagsCategory); - - scrollArea_ = new QScrollArea(this); - scrollArea_->setWidgetResizable(true); - scrollArea_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - - auto scrollWidget = new QWidget(this); - auto scrollLayout = new QVBoxLayout(scrollWidget); - - scrollLayout->setMargin(0); - scrollLayout->setSpacing(0); - scrollArea_->setWidget(scrollWidget); - - auto peopleEmoji = - new EmojiCategory(tr("Smileys & People"), emoji_provider_.people, scrollWidget); - scrollLayout->addWidget(peopleEmoji); - - auto natureEmoji = - new EmojiCategory(tr("Animals & Nature"), emoji_provider_.nature, scrollWidget); - scrollLayout->addWidget(natureEmoji); - - auto foodEmoji = new EmojiCategory(tr("Food & Drink"), emoji_provider_.food, scrollWidget); - scrollLayout->addWidget(foodEmoji); - - auto activityEmoji = - new EmojiCategory(tr("Activity"), emoji_provider_.activity, scrollWidget); - scrollLayout->addWidget(activityEmoji); - - auto travelEmoji = - new EmojiCategory(tr("Travel & Places"), emoji_provider_.travel, scrollWidget); - scrollLayout->addWidget(travelEmoji); - - auto objectsEmoji = new EmojiCategory(tr("Objects"), emoji_provider_.objects, scrollWidget); - scrollLayout->addWidget(objectsEmoji); - - auto symbolsEmoji = new EmojiCategory(tr("Symbols"), emoji_provider_.symbols, scrollWidget); - scrollLayout->addWidget(symbolsEmoji); - - auto flagsEmoji = new EmojiCategory(tr("Flags"), emoji_provider_.flags, scrollWidget); - scrollLayout->addWidget(flagsEmoji); - - contentLayout->addWidget(scrollArea_); - contentLayout->addWidget(emojiCategories); - - connect(peopleEmoji, &EmojiCategory::emojiSelected, this, &EmojiPanel::emojiSelected); - connect(peopleCategory, &QPushButton::clicked, [this, peopleEmoji]() { - this->showEmojiCategory(peopleEmoji); - }); - - connect(natureEmoji, &EmojiCategory::emojiSelected, this, &EmojiPanel::emojiSelected); - connect(natureCategory_, &QPushButton::clicked, [this, natureEmoji]() { - this->showEmojiCategory(natureEmoji); - }); - - connect(foodEmoji, &EmojiCategory::emojiSelected, this, &EmojiPanel::emojiSelected); - connect(foodCategory_, &QPushButton::clicked, [this, foodEmoji]() { - this->showEmojiCategory(foodEmoji); - }); - - connect(activityEmoji, &EmojiCategory::emojiSelected, this, &EmojiPanel::emojiSelected); - connect(activityCategory, &QPushButton::clicked, [this, activityEmoji]() { - this->showEmojiCategory(activityEmoji); - }); - - connect(travelEmoji, &EmojiCategory::emojiSelected, this, &EmojiPanel::emojiSelected); - connect(travelCategory, &QPushButton::clicked, [this, travelEmoji]() { - this->showEmojiCategory(travelEmoji); - }); - - connect(objectsEmoji, &EmojiCategory::emojiSelected, this, &EmojiPanel::emojiSelected); - connect(objectsCategory, &QPushButton::clicked, [this, objectsEmoji]() { - this->showEmojiCategory(objectsEmoji); - }); - - connect(symbolsEmoji, &EmojiCategory::emojiSelected, this, &EmojiPanel::emojiSelected); - connect(symbolsCategory, &QPushButton::clicked, [this, symbolsEmoji]() { - this->showEmojiCategory(symbolsEmoji); - }); - - connect(flagsEmoji, &EmojiCategory::emojiSelected, this, &EmojiPanel::emojiSelected); - connect(flagsCategory, &QPushButton::clicked, [this, flagsEmoji]() { - this->showEmojiCategory(flagsEmoji); - }); -} - -void -EmojiPanel::showEmojiCategory(const EmojiCategory *category) -{ - auto posToGo = category->mapToParent(QPoint()).y(); - auto current = scrollArea_->verticalScrollBar()->value(); - - if (current == posToGo) - return; - - // HACK - // If we want to go to a previous category and position the label at the top - // the 6*50 offset won't work because not all the categories have the same - // height. To ensure the category is at the top, we move to the top and go as - // normal to the next category. - if (current > posToGo) - this->scrollArea_->ensureVisible(0, 0, 0, 0); - - posToGo += 6 * 50; - this->scrollArea_->ensureVisible(0, posToGo, 0, 0); -} - -void -EmojiPanel::leaveEvent(QEvent *) -{ - hide(); -} - -void -EmojiPanel::paintEvent(QPaintEvent *event) -{ - Q_UNUSED(event); - - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); - - DropShadow::draw(p, - shadowMargin_, - 4.0, - QColor(120, 120, 120, 92), - QColor(255, 255, 255, 0), - 0.0, - 1.0, - 0.6, - width(), - height()); -} diff --git a/src/EmojiPickButton.cc b/src/EmojiPickButton.cc deleted file mode 100644 index a4f83c46..00000000 --- a/src/EmojiPickButton.cc +++ /dev/null @@ -1,65 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#include "EmojiPickButton.h" -#include "EmojiPanel.h" - -EmojiPickButton::EmojiPickButton(QWidget *parent) - : FlatButton(parent) - , panel_{nullptr} -{} - -void -EmojiPickButton::enterEvent(QEvent *e) -{ - Q_UNUSED(e); - - if (panel_.isNull()) { - panel_ = QSharedPointer(new EmojiPanel(this)); - connect( - panel_.data(), &EmojiPanel::emojiSelected, this, &EmojiPickButton::emojiSelected); - } - - QPoint pos(rect().x(), rect().y()); - pos = this->mapToGlobal(pos); - - auto panel_size = panel_->sizeHint(); - - auto x = pos.x() - panel_size.width() + horizontal_distance_; - auto y = pos.y() - panel_size.height() - vertical_distance_; - - panel_->move(x, y); - panel_->show(); -} - -void -EmojiPickButton::leaveEvent(QEvent *e) -{ - Q_UNUSED(e); - - if (panel_->underMouse()) - return; - - auto pos = QCursor::pos(); - auto panel_geometry = panel_->geometry(); - panel_geometry.adjust(0, 0, 0, vertical_distance_); - - if (panel_geometry.contains(pos)) - return; - - panel_->hide(); -} diff --git a/src/EmojiProvider.cc b/src/EmojiProvider.cc deleted file mode 100644 index ec053046..00000000 --- a/src/EmojiProvider.cc +++ /dev/null @@ -1,1465 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#include - -#include "EmojiProvider.h" - -const QList EmojiProvider::people = { - Emoji{QString::fromUtf8("\xf0\x9f\x98\x80"), ":grinning:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x81"), ":grin:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x82"), ":joy:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa3"), ":rofl:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x83"), ":smiley:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x84"), ":smile:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x85"), ":sweat_smile:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x86"), ":laughing:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x89"), ":wink:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x8a"), ":blush:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x8b"), ":yum:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x8e"), ":sunglasses:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x8d"), ":heart_eyes:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x98"), ":kissing_heart:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x97"), ":kissing:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x99"), ":kissing_smiling_eyes:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x9a"), ":kissing_closed_eyes:"}, - Emoji{QString::fromUtf8("\xe2\x98\xba"), ":relaxed:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x99\x82"), ":slight_smile:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\x97"), ":hugging:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\x94"), ":thinking:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x90"), ":neutral_face:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x91"), ":expressionless:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xb6"), ":no_mouth:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x99\x84"), ":rolling_eyes:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x8f"), ":smirk:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xa3"), ":persevere:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xa5"), ":disappointed_relieved:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xae"), ":open_mouth:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\x90"), ":zipper_mouth:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xaf"), ":hushed:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xaa"), ":sleepy:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xab"), ":tired_face:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xb4"), ":sleeping:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x8c"), ":relieved:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\x93"), ":nerd:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x9b"), ":stuck_out_tongue:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x9c"), ":stuck_out_tongue_winking_eye:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x9d"), ":stuck_out_tongue_closed_eyes:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa4"), ":drooling_face:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x92"), ":unamused:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x93"), ":sweat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x94"), ":pensive:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x95"), ":confused:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x99\x83"), ":upside_down:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\x91"), ":money_mouth:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xb2"), ":astonished:"}, - Emoji{QString::fromUtf8("\xe2\x98\xb9"), ":frowning2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x99\x81"), ":slight_frown:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x96"), ":confounded:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x9e"), ":disappointed:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x9f"), ":worried:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xa4"), ":triumph:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xa2"), ":cry:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xad"), ":sob:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xa6"), ":frowning:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xa7"), ":anguished:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xa8"), ":fearful:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xa9"), ":weary:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xac"), ":grimacing:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xb0"), ":cold_sweat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xb1"), ":scream:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xb3"), ":flushed:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xb5"), ":dizzy_face:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xa1"), ":rage:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xa0"), ":angry:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x87"), ":innocent:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa0"), ":cowboy:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa1"), ":clown:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa5"), ":lying_face:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xb7"), ":mask:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\x92"), ":thermometer_face:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\x95"), ":head_bandage:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa2"), ":nauseated_face:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa7"), ":sneezing_face:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\x88"), ":smiling_imp:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xbf"), ":imp:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xb9"), ":japanese_ogre:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xba"), ":japanese_goblin:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x80"), ":skull:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xbb"), ":ghost:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xbd"), ":alien:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\x96"), ":robot:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xa9"), ":poop:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xba"), ":smiley_cat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xb8"), ":smile_cat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xb9"), ":joy_cat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xbb"), ":heart_eyes_cat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xbc"), ":smirk_cat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xbd"), ":kissing_cat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x99\x80"), ":scream_cat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xbf"), ":crying_cat_face:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x98\xbe"), ":pouting_cat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xa6"), ":boy:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xa7"), ":girl:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xa8"), ":man:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xa9"), ":woman:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xb4"), ":older_man:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xb5"), ":older_woman:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xb6"), ":baby:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xbc"), ":angel:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xae"), ":cop:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\xb5"), ":spy:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x82"), ":guardsman:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xb7"), ":construction_worker:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xb3"), ":man_with_turban:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xb1"), ":person_with_blond_hair:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x85"), ":santa:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb6"), ":mrs_claus:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xb8"), ":princess:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb4"), ":prince:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xb0"), ":bride_with_veil:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb5"), ":man_in_tuxedo:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb0"), ":pregnant_woman:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xb2"), ":man_with_gua_pi_mao:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x99\x8d"), ":person_frowning:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x99\x8e"), ":person_with_pouting_face:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x99\x85"), ":no_good:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x99\x86"), ":ok_woman:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x81"), ":information_desk_person:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x99\x8b"), ":raising_hand:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x99\x87"), ":bow:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa6"), ":face_palm:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb7"), ":shrug:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x86"), ":massage:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x87"), ":haircut:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb6"), ":walking:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x83"), ":runner:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x83"), ":dancer:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\xba"), ":man_dancing:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xaf"), ":dancers:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\xa3"), ":speaking_head:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xa4"), ":bust_in_silhouette:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xa5"), ":busts_in_silhouette:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xab"), ":couple:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xac"), ":two_men_holding_hands:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xad"), ":two_women_holding_hands:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x8f"), ":couplekiss:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x91"), ":couple_with_heart:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xaa"), ":family:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xaa"), ":muscle:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb3"), ":selfie:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x88"), ":point_left:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x89"), ":point_right:"}, - Emoji{QString::fromUtf8("\xe2\x98\x9d"), ":point_up:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x86"), ":point_up_2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x96\x95"), ":middle_finger:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x87"), ":point_down:"}, - Emoji{QString::fromUtf8("\xe2\x9c\x8c"), ":v:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\x9e"), ":fingers_crossed:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x96\x96"), ":vulcan:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\x98"), ":metal:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\x99"), ":call_me:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x96\x90"), ":hand_splayed:"}, - Emoji{QString::fromUtf8("\xe2\x9c\x8b"), ":raised_hand:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x8c"), ":ok_hand:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x8d"), ":thumbsup:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x8e"), ":thumbsdown:"}, - Emoji{QString::fromUtf8("\xe2\x9c\x8a"), ":fist:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x8a"), ":punch:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\x9b"), ":left_facing_fist:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\x9c"), ":right_facing_fist:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\x9a"), ":raised_back_of_hand:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x8b"), ":wave:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x8f"), ":clap:"}, - Emoji{QString::fromUtf8("\xe2\x9c\x8d"), ":writing_hand:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x90"), ":open_hands:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x99\x8c"), ":raised_hands:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x99\x8f"), ":pray:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\x9d"), ":handshake:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x85"), ":nail_care:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x82"), ":ear:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x83"), ":nose:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xa3"), ":footprints:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x80"), ":eyes:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x81"), ":eye:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x85"), ":tongue:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x84"), ":lips:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x8b"), ":kiss:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xa4"), ":zzz:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x93"), ":eyeglasses:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\xb6"), ":dark_sunglasses:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x94"), ":necktie:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x95"), ":shirt:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x96"), ":jeans:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x97"), ":dress:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x98"), ":kimono:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x99"), ":bikini:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x9a"), ":womans_clothes:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x9b"), ":purse:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x9c"), ":handbag:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x9d"), ":pouch:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x92"), ":school_satchel:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x9e"), ":mans_shoe:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x9f"), ":athletic_shoe:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xa0"), ":high_heel:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xa1"), ":sandal:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\xa2"), ":boot:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x91"), ":crown:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x91\x92"), ":womans_hat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa9"), ":tophat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x93"), ":mortar_board:"}, - Emoji{QString::fromUtf8("\xe2\x9b\x91"), ":helmet_with_cross:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x84"), ":lipstick:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x8d"), ":ring:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x82"), ":closed_umbrella:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xbc"), ":briefcase:"}, -}; - -const QList EmojiProvider::nature = { - Emoji{QString::fromUtf8("\xf0\x9f\x99\x88"), ":see_no_evil:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x99\x89"), ":hear_no_evil:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x99\x8a"), ":speak_no_evil:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xa6"), ":sweat_drops:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xa8"), ":dash:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xb5"), ":monkey_face:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x92"), ":monkey:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa6\x8d"), ":gorilla:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xb6"), ":dog:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x95"), ":dog2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xa9"), ":poodle:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xba"), ":wolf:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa6\x8a"), ":fox:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xb1"), ":cat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x88"), ":cat2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa6\x81"), ":lion_face:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xaf"), ":tiger:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x85"), ":tiger2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x86"), ":leopard:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xb4"), ":horse:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x8e"), ":racehorse:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa6\x8c"), ":deer:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa6\x84"), ":unicorn:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xae"), ":cow:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x82"), ":ox:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x83"), ":water_buffalo:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x84"), ":cow2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xb7"), ":pig:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x96"), ":pig2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x97"), ":boar:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xbd"), ":pig_nose:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x8f"), ":ram:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x91"), ":sheep:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x90"), ":goat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xaa"), ":dromedary_camel:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xab"), ":camel:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x98"), ":elephant:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa6\x8f"), ":rhino:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xad"), ":mouse:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x81"), ":mouse2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x80"), ":rat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xb9"), ":hamster:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xb0"), ":rabbit:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x87"), ":rabbit2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xbf"), ":chipmunk:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa6\x87"), ":bat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xbb"), ":bear:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xa8"), ":koala:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xbc"), ":panda_face:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xbe"), ":feet:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa6\x83"), ":turkey:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x94"), ":chicken:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x93"), ":rooster:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xa3"), ":hatching_chick:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xa4"), ":baby_chick:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xa5"), ":hatched_chick:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xa6"), ":bird:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xa7"), ":penguin:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x8a"), ":dove:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa6\x85"), ":eagle:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa6\x86"), ":duck:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa6\x89"), ":owl:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xb8"), ":frog:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x8a"), ":crocodile:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xa2"), ":turtle:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa6\x8e"), ":lizard:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x8d"), ":snake:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xb2"), ":dragon_face:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x89"), ":dragon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xb3"), ":whale:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x8b"), ":whale2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xac"), ":dolphin:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x9f"), ":fish:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xa0"), ":tropical_fish:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\xa1"), ":blowfish:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa6\x88"), ":shark:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x99"), ":octopus:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x9a"), ":shell:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa6\x80"), ":crab:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa6\x90"), ":shrimp:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa6\x91"), ":squid:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa6\x8b"), ":butterfly:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x8c"), ":snail:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x9b"), ":bug:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x9c"), ":ant:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x9d"), ":bee:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x90\x9e"), ":beetle:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\xb7"), ":spider:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\xb8"), ":spider_web:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa6\x82"), ":scorpion:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x90"), ":bouquet:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb8"), ":cherry_blossom:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb5"), ":rosette:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb9"), ":rose:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x80"), ":wilted_rose:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xba"), ":hibiscus:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xbb"), ":sunflower:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xbc"), ":blossom:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb7"), ":tulip:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb1"), ":seedling:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb2"), ":evergreen_tree:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb3"), ":deciduous_tree:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb4"), ":palm_tree:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb5"), ":cactus:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xbe"), ":ear_of_rice:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xbf"), ":herb:"}, - Emoji{QString::fromUtf8("\xe2\x98\x98"), ":shamrock:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x80"), ":four_leaf_clover:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x81"), ":maple_leaf:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x82"), ":fallen_leaf:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x83"), ":leaves:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x84"), ":mushroom:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb0"), ":chestnut:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x8d"), ":earth_africa:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x8e"), ":earth_americas:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x8f"), ":earth_asia:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x91"), ":new_moon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x92"), ":waxing_crescent_moon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x93"), ":first_quarter_moon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x94"), ":waxing_gibbous_moon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x95"), ":full_moon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x96"), ":waning_gibbous_moon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x97"), ":last_quarter_moon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x98"), ":waning_crescent_moon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x99"), ":crescent_moon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x9a"), ":new_moon_with_face:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x9b"), ":first_quarter_moon_with_face:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x9c"), ":last_quarter_moon_with_face:"}, - Emoji{QString::fromUtf8("\xe2\x98\x80"), ":sunny:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x9d"), ":full_moon_with_face:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x9e"), ":sun_with_face:"}, - Emoji{QString::fromUtf8("\xe2\xad\x90"), ":star:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x9f"), ":star2:"}, - Emoji{QString::fromUtf8("\xe2\x98\x81"), ":cloud:"}, - Emoji{QString::fromUtf8("\xe2\x9b\x85"), ":partly_sunny:"}, - Emoji{QString::fromUtf8("\xe2\x9b\x88"), ":thunder_cloud_rain:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa4"), ":white_sun_small_cloud:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa5"), ":white_sun_cloud:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa6"), ":white_sun_rain_cloud:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa7"), ":cloud_rain:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa8"), ":cloud_snow:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa9"), ":cloud_lightning:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xaa"), ":cloud_tornado:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xab"), ":fog:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xac"), ":wind_blowing_face:"}, - Emoji{QString::fromUtf8("\xe2\x98\x82"), ":umbrella2:"}, - Emoji{QString::fromUtf8("\xe2\x98\x94"), ":umbrella:"}, - Emoji{QString::fromUtf8("\xe2\x9a\xa1"), ":zap:"}, - Emoji{QString::fromUtf8("\xe2\x9d\x84"), ":snowflake:"}, - Emoji{QString::fromUtf8("\xe2\x98\x83"), ":snowman2:"}, - Emoji{QString::fromUtf8("\xe2\x9b\x84"), ":snowman:"}, - Emoji{QString::fromUtf8("\xe2\x98\x84"), ":comet:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xa5"), ":fire:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xa7"), ":droplet:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x8a"), ":ocean:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x83"), ":jack_o_lantern:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x84"), ":christmas_tree:"}, - Emoji{QString::fromUtf8("\xe2\x9c\xa8"), ":sparkles:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x8b"), ":tanabata_tree:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x8d"), ":bamboo:"}, -}; - -const QList EmojiProvider::food = { - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x87"), ":grapes:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x88"), ":melon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x89"), ":watermelon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x8a"), ":tangerine:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x8b"), ":lemon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x8c"), ":banana:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x8d"), ":pineapple:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x8e"), ":apple:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x8f"), ":green_apple:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x90"), ":pear:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x91"), ":peach:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x92"), ":cherries:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x93"), ":strawberry:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x9d"), ":kiwi:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x85"), ":tomato:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x91"), ":avocado:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x86"), ":eggplant:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x94"), ":potato:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x95"), ":carrot:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xbd"), ":corn:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb6"), ":hot_pepper:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x92"), ":cucumber:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x9c"), ":peanuts:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x9e"), ":bread:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x90"), ":croissant:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x96"), ":french_bread:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x9e"), ":pancakes:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa7\x80"), ":cheese:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x96"), ":meat_on_bone:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x97"), ":poultry_leg:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x93"), ":bacon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x94"), ":hamburger:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x9f"), ":fries:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x95"), ":pizza:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xad"), ":hotdog:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xae"), ":taco:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xaf"), ":burrito:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x99"), ":stuffed_flatbread:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x9a"), ":egg:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb3"), ":cooking:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x98"), ":shallow_pan_of_food:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb2"), ":stew:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x97"), ":salad:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xbf"), ":popcorn:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb1"), ":bento:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x98"), ":rice_cracker:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x99"), ":rice_ball:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x9a"), ":rice:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x9b"), ":curry:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x9c"), ":ramen:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\x9d"), ":spaghetti:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa0"), ":sweet_potato:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa2"), ":oden:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa3"), ":sushi:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa4"), ":fried_shrimp:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa5"), ":fish_cake:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa1"), ":dango:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa6"), ":icecream:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa7"), ":shaved_ice:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa8"), ":ice_cream:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa9"), ":doughnut:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xaa"), ":cookie:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x82"), ":birthday:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb0"), ":cake:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xab"), ":chocolate_bar:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xac"), ":candy:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xad"), ":lollipop:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xae"), ":custard:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xaf"), ":honey_pot:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xbc"), ":baby_bottle:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x9b"), ":milk:"}, - Emoji{QString::fromUtf8("\xe2\x98\x95"), ":coffee:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb5"), ":tea:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb6"), ":sake:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xbe"), ":champagne:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb7"), ":wine_glass:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb8"), ":cocktail:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb9"), ":tropical_drink:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xba"), ":beer:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xbb"), ":beers:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x82"), ":champagne_glass:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x83"), ":tumbler_glass:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xbd"), ":fork_knife_plate:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb4"), ":fork_and_knife:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x84"), ":spoon:"}, -}; - -const QList EmojiProvider::activity = { - Emoji{QString::fromUtf8("\xf0\x9f\x91\xbe"), ":space_invader:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\xb4"), ":levitate:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xba"), ":fencer:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x87"), ":horse_racing:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbb"), ":horse_racing_tone1:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbc"), ":horse_racing_tone2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbd"), ":horse_racing_tone3:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbe"), ":horse_racing_tone4:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbf"), ":horse_racing_tone5:"}, - Emoji{QString::fromUtf8("\xe2\x9b\xb7"), ":skier:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x82"), ":snowboarder:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8c"), ":golfer:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x84"), ":surfer:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbb"), ":surfer_tone1:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbc"), ":surfer_tone2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbd"), ":surfer_tone3:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbe"), ":surfer_tone4:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbf"), ":surfer_tone5:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa3"), ":rowboat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbb"), ":rowboat_tone1:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbc"), ":rowboat_tone2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbd"), ":rowboat_tone3:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbe"), ":rowboat_tone4:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbf"), ":rowboat_tone5:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8a"), ":swimmer:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbb"), ":swimmer_tone1:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbc"), ":swimmer_tone2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbd"), ":swimmer_tone3:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbe"), ":swimmer_tone4:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbf"), ":swimmer_tone5:"}, - Emoji{QString::fromUtf8("\xe2\x9b\xb9"), ":basketball_player:"}, - Emoji{QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbb"), ":basketball_player_tone1:"}, - Emoji{QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbc"), ":basketball_player_tone2:"}, - Emoji{QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbd"), ":basketball_player_tone3:"}, - Emoji{QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbe"), ":basketball_player_tone4:"}, - Emoji{QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbf"), ":basketball_player_tone5:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8b"), ":lifter:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbb"), ":lifter_tone1:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbc"), ":lifter_tone2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbd"), ":lifter_tone3:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbe"), ":lifter_tone4:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbf"), ":lifter_tone5:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb4"), ":bicyclist:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbb"), ":bicyclist_tone1:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbc"), ":bicyclist_tone2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbd"), ":bicyclist_tone3:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbe"), ":bicyclist_tone4:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbf"), ":bicyclist_tone5:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb5"), ":mountain_bicyclist:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbb"), ":mountain_bicyclist_tone1:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbc"), ":mountain_bicyclist_tone2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbd"), ":mountain_bicyclist_tone3:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbe"), ":mountain_bicyclist_tone4:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbf"), ":mountain_bicyclist_tone5:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb8"), ":cartwheel:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbb"), ":cartwheel_tone1:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbc"), ":cartwheel_tone2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbd"), ":cartwheel_tone3:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbe"), ":cartwheel_tone4:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbf"), ":cartwheel_tone5:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbc"), ":wrestlers:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbb"), ":wrestlers_tone1:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbc"), ":wrestlers_tone2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbd"), ":wrestlers_tone3:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbe"), ":wrestlers_tone4:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbf"), ":wrestlers_tone5:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbd"), ":water_polo:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbb"), ":water_polo_tone1:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbc"), ":water_polo_tone2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbd"), ":water_polo_tone3:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbe"), ":water_polo_tone4:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbf"), ":water_polo_tone5:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbe"), ":handball:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbb"), ":handball_tone1:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbc"), ":handball_tone2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbd"), ":handball_tone3:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbe"), ":handball_tone4:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbf"), ":handball_tone5:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb9"), ":juggling:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbb"), ":juggling_tone1:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbc"), ":juggling_tone2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbd"), ":juggling_tone3:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbe"), ":juggling_tone4:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbf"), ":juggling_tone5:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xaa"), ":circus_tent:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xad"), ":performing_arts:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa8"), ":art:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb0"), ":slot_machine:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\x80"), ":bath:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbb"), ":bath_tone1:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbc"), ":bath_tone2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbd"), ":bath_tone3:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbe"), ":bath_tone4:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbf"), ":bath_tone5:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x97"), ":reminder_ribbon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x9f"), ":tickets:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xab"), ":ticket:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x96"), ":military_medal:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x86"), ":trophy:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x85"), ":medal:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x87"), ":first_place:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x88"), ":second_place:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x89"), ":third_place:"}, - Emoji{QString::fromUtf8("\xe2\x9a\xbd"), ":soccer:"}, - Emoji{QString::fromUtf8("\xe2\x9a\xbe"), ":baseball:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x80"), ":basketball:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x90"), ":volleyball:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x88"), ":football:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x89"), ":rugby_football:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xbe"), ":tennis:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb1"), ":8ball:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb3"), ":bowling:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8f"), ":cricket:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x91"), ":field_hockey:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x92"), ":hockey:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x93"), ":ping_pong:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb8"), ":badminton:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x8a"), ":boxing_glove:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x8b"), ":martial_arts_uniform:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x85"), ":goal:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xaf"), ":dart:"}, - Emoji{QString::fromUtf8("\xe2\x9b\xb3"), ":golf:"}, - Emoji{QString::fromUtf8("\xe2\x9b\xb8"), ":ice_skate:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa3"), ":fishing_pole_and_fish:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xbd"), ":running_shirt_with_sash:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xbf"), ":ski:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xae"), ":video_game:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb2"), ":game_die:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xbc"), ":musical_score:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa4"), ":microphone:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa7"), ":headphones:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb7"), ":saxophone:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb8"), ":guitar:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb9"), ":musical_keyboard:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xba"), ":trumpet:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xbb"), ":violin:"}, - Emoji{QString::fromUtf8("\xf0\x9f\xa5\x81"), ":drum:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xac"), ":clapper:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb9"), ":bow_and_arrow:"}, -}; - -const QList EmojiProvider::travel = { - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8e"), ":race_car:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8d"), ":motorcycle:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\xbe"), ":japan:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x94"), ":mountain_snow:"}, - Emoji{QString::fromUtf8("\xe2\x9b\xb0"), ":mountain:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x8b"), ":volcano:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\xbb"), ":mount_fuji:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x95"), ":camping:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x96"), ":beach:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x9c"), ":desert:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x9d"), ":island:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x9e"), ":park:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x9f"), ":stadium:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x9b"), ":classical_building:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x97"), ":construction_site:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x98"), ":homes:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x99"), ":cityscape:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x9a"), ":house_abandoned:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa0"), ":house:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa1"), ":house_with_garden:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa2"), ":office:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa3"), ":post_office:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa4"), ":european_post_office:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa5"), ":hospital:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa6"), ":bank:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa8"), ":hotel:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa9"), ":love_hotel:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xaa"), ":convenience_store:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xab"), ":school:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xac"), ":department_store:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xad"), ":factory:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xaf"), ":japanese_castle:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb0"), ":european_castle:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x92"), ":wedding:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\xbc"), ":tokyo_tower:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\xbd"), ":statue_of_liberty:"}, - Emoji{QString::fromUtf8("\xe2\x9b\xaa"), ":church:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x8c"), ":mosque:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x8d"), ":synagogue:"}, - Emoji{QString::fromUtf8("\xe2\x9b\xa9"), ":shinto_shrine:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x8b"), ":kaaba:"}, - Emoji{QString::fromUtf8("\xe2\x9b\xb2"), ":fountain:"}, - Emoji{QString::fromUtf8("\xe2\x9b\xba"), ":tent:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x81"), ":foggy:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x83"), ":night_with_stars:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x84"), ":sunrise_over_mountains:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x85"), ":sunrise:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x86"), ":city_dusk:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x87"), ":city_sunset:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x89"), ":bridge_at_night:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x8c"), ":milky_way:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa0"), ":carousel_horse:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa1"), ":ferris_wheel:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa2"), ":roller_coaster:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x82"), ":steam_locomotive:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x83"), ":railway_car:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x84"), ":bullettrain_side:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x85"), ":bullettrain_front:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x86"), ":train2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x87"), ":metro:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x88"), ":light_rail:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x89"), ":station:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x8a"), ":tram:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x9d"), ":monorail:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x9e"), ":mountain_railway:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x8b"), ":train:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x8c"), ":bus:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x8d"), ":oncoming_bus:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x8e"), ":trolleybus:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x90"), ":minibus:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x91"), ":ambulance:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x92"), ":fire_engine:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x93"), ":police_car:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x94"), ":oncoming_police_car:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x95"), ":taxi:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x96"), ":oncoming_taxi:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x97"), ":red_car:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x98"), ":oncoming_automobile:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x99"), ":blue_car:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x9a"), ":truck:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x9b"), ":articulated_lorry:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x9c"), ":tractor:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb2"), ":bike:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\xb4"), ":scooter:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\xb5"), ":motor_scooter:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x8f"), ":busstop:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa3"), ":motorway:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa4"), ":railway_track:"}, - Emoji{QString::fromUtf8("\xe2\x9b\xbd"), ":fuelpump:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa8"), ":rotating_light:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa5"), ":traffic_light:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa6"), ":vertical_traffic_light:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa7"), ":construction:"}, - Emoji{QString::fromUtf8("\xe2\x9a\x93"), ":anchor:"}, - Emoji{QString::fromUtf8("\xe2\x9b\xb5"), ":sailboat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\xb6"), ":canoe:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa4"), ":speedboat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\xb3"), ":cruise_ship:"}, - Emoji{QString::fromUtf8("\xe2\x9b\xb4"), ":ferry:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa5"), ":motorboat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa2"), ":ship:"}, - Emoji{QString::fromUtf8("\xe2\x9c\x88"), ":airplane:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa9"), ":airplane_small:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\xab"), ":airplane_departure:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\xac"), ":airplane_arriving:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xba"), ":seat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x81"), ":helicopter:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x9f"), ":suspension_railway:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa0"), ":mountain_cableway:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa1"), ":aerial_tramway:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\x80"), ":rocket:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\xb0"), ":satellite_orbital:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa0"), ":stars:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x88"), ":rainbow:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x86"), ":fireworks:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x87"), ":sparkler:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x91"), ":rice_scene:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\x81"), ":checkered_flag:"}, -}; - -const QList EmojiProvider::objects = { - Emoji{QString::fromUtf8("\xe2\x98\xa0"), ":skull_crossbones:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x8c"), ":love_letter:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xa3"), ":bomb:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\xb3"), ":hole:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\x8d"), ":shopping_bags:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xbf"), ":prayer_beads:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x8e"), ":gem:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xaa"), ":knife:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xba"), ":amphora:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\xba"), ":map:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x88"), ":barber:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x96\xbc"), ":frame_photo:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\x8e"), ":bellhop:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xaa"), ":door:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\x8c"), ":sleeping_accommodation:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\x8f"), ":bed:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\x8b"), ":couch:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xbd"), ":toilet:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xbf"), ":shower:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\x81"), ":bathtub:"}, - Emoji{QString::fromUtf8("\xe2\x8c\x9b"), ":hourglass:"}, - Emoji{QString::fromUtf8("\xe2\x8f\xb3"), ":hourglass_flowing_sand:"}, - Emoji{QString::fromUtf8("\xe2\x8c\x9a"), ":watch:"}, - Emoji{QString::fromUtf8("\xe2\x8f\xb0"), ":alarm_clock:"}, - Emoji{QString::fromUtf8("\xe2\x8f\xb1"), ":stopwatch:"}, - Emoji{QString::fromUtf8("\xe2\x8f\xb2"), ":timer:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\xb0"), ":clock:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa1"), ":thermometer:"}, - Emoji{QString::fromUtf8("\xe2\x9b\xb1"), ":beach_umbrella:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x88"), ":balloon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x89"), ":tada:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x8a"), ":confetti_ball:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x8e"), ":dolls:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x8f"), ":flags:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x90"), ":wind_chime:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x80"), ":ribbon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x81"), ":gift:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\xb9"), ":joystick:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xaf"), ":postal_horn:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x99"), ":microphone2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x9a"), ":level_slider:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x9b"), ":control_knobs:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xbb"), ":radio:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xb1"), ":iphone:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xb2"), ":calling:"}, - Emoji{QString::fromUtf8("\xe2\x98\x8e"), ":telephone:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x9e"), ":telephone_receiver:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x9f"), ":pager:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xa0"), ":fax:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x8b"), ":battery:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x8c"), ":electric_plug:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xbb"), ":computer:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x96\xa5"), ":desktop:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x96\xa8"), ":printer:"}, - Emoji{QString::fromUtf8("\xe2\x8c\xa8"), ":keyboard:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x96\xb1"), ":mouse_three_button:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x96\xb2"), ":trackball:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xbd"), ":minidisc:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xbe"), ":floppy_disk:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xbf"), ":cd:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x80"), ":dvd:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa5"), ":movie_camera:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x9e"), ":film_frames:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xbd"), ":projector:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xba"), ":tv:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xb7"), ":camera:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xb8"), ":camera_with_flash:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xb9"), ":video_camera:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xbc"), ":vhs:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x8d"), ":mag:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x8e"), ":mag_right:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xac"), ":microscope:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xad"), ":telescope:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xa1"), ":satellite:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\xaf"), ":candle:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xa1"), ":bulb:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xa6"), ":flashlight:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xae"), ":izakaya_lantern:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x94"), ":notebook_with_decorative_cover:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x95"), ":closed_book:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x96"), ":book:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x97"), ":green_book:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x98"), ":blue_book:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x99"), ":orange_book:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x9a"), ":books:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x93"), ":notebook:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x92"), ":ledger:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x83"), ":page_with_curl:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x9c"), ":scroll:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x84"), ":page_facing_up:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xb0"), ":newspaper:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\x9e"), ":newspaper2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x91"), ":bookmark_tabs:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x96"), ":bookmark:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb7"), ":label:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xb0"), ":moneybag:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xb4"), ":yen:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xb5"), ":dollar:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xb6"), ":euro:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xb7"), ":pound:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xb8"), ":money_with_wings:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xb3"), ":credit_card:"}, - Emoji{QString::fromUtf8("\xe2\x9c\x89"), ":envelope:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xa7"), ":e-mail:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xa8"), ":incoming_envelope:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xa9"), ":envelope_with_arrow:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xa4"), ":outbox_tray:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xa5"), ":inbox_tray:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xa6"), ":package:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xab"), ":mailbox:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xaa"), ":mailbox_closed:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xac"), ":mailbox_with_mail:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xad"), ":mailbox_with_no_mail:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xae"), ":postbox:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\xb3"), ":ballot_box:"}, - Emoji{QString::fromUtf8("\xe2\x9c\x8f"), ":pencil2:"}, - Emoji{QString::fromUtf8("\xe2\x9c\x92"), ":black_nib:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x96\x8b"), ":pen_fountain:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x96\x8a"), ":pen_ballpoint:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x96\x8c"), ":paintbrush:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x96\x8d"), ":crayon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x9d"), ":pencil:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x81"), ":file_folder:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x82"), ":open_file_folder:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\x82"), ":dividers:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x85"), ":date:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x86"), ":calendar:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\x92"), ":notepad_spiral:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\x93"), ":calendar_spiral:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x87"), ":card_index:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x88"), ":chart_with_upwards_trend:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x89"), ":chart_with_downwards_trend:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x8a"), ":bar_chart:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x8b"), ":clipboard:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x8c"), ":pushpin:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x8d"), ":round_pushpin:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x8e"), ":paperclip:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x96\x87"), ":paperclips:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x8f"), ":straight_ruler:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x90"), ":triangular_ruler:"}, - Emoji{QString::fromUtf8("\xe2\x9c\x82"), ":scissors:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\x83"), ":card_box:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\x84"), ":file_cabinet:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\x91"), ":wastebasket:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x92"), ":lock:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x93"), ":unlock:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x8f"), ":lock_with_ink_pen:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x90"), ":closed_lock_with_key:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x91"), ":key:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\x9d"), ":key2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xa8"), ":hammer:"}, - Emoji{QString::fromUtf8("\xe2\x9b\x8f"), ":pick:"}, - Emoji{QString::fromUtf8("\xe2\x9a\x92"), ":hammer_pick:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa0"), ":tools:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\xa1"), ":dagger:"}, - Emoji{QString::fromUtf8("\xe2\x9a\x94"), ":crossed_swords:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xab"), ":gun:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa1"), ":shield:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xa7"), ":wrench:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xa9"), ":nut_and_bolt:"}, - Emoji{QString::fromUtf8("\xe2\x9a\x99"), ":gear:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\x9c"), ":compression:"}, - Emoji{QString::fromUtf8("\xe2\x9a\x97"), ":alembic:"}, - Emoji{QString::fromUtf8("\xe2\x9a\x96"), ":scales:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x97"), ":link:"}, - Emoji{QString::fromUtf8("\xe2\x9b\x93"), ":chains:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x89"), ":syringe:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x8a"), ":pill:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xac"), ":smoking:"}, - Emoji{QString::fromUtf8("\xe2\x9a\xb0"), ":coffin:"}, - Emoji{QString::fromUtf8("\xe2\x9a\xb1"), ":urn:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\xbf"), ":moyai:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa2"), ":oil:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xae"), ":crystal_ball:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\x92"), ":shopping_cart:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa9"), ":triangular_flag_on_post:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\x8c"), ":crossed_flags:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb4"), ":flag_black:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb3"), ":flag_white:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb3\xf0\x9f\x8c\x88"), ":rainbow_flag:"}, -}; - -const QList EmojiProvider::symbols = { - Emoji{QString::fromUtf8("\xf0\x9f\x91\x81\xf0\x9f\x97\xa8"), ":eye_in_speech_bubble:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x98"), ":cupid:"}, - Emoji{QString::fromUtf8("\xe2\x9d\xa4"), ":heart:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x93"), ":heartbeat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x94"), ":broken_heart:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x95"), ":two_hearts:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x96"), ":sparkling_heart:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x97"), ":heartpulse:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x99"), ":blue_heart:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x9a"), ":green_heart:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x9b"), ":yellow_heart:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x9c"), ":purple_heart:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x96\xa4"), ":black_heart:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x9d"), ":gift_heart:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x9e"), ":revolving_hearts:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\x9f"), ":heart_decoration:"}, - Emoji{QString::fromUtf8("\xe2\x9d\xa3"), ":heart_exclamation:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xa2"), ":anger:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xa5"), ":boom:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xab"), ":dizzy:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xac"), ":speech_balloon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\xa8"), ":speech_left:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x97\xaf"), ":anger_right:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xad"), ":thought_balloon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xae"), ":white_flower:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x90"), ":globe_with_meridians:"}, - Emoji{QString::fromUtf8("\xe2\x99\xa8"), ":hotsprings:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\x91"), ":octagonal_sign:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x9b"), ":clock12:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\xa7"), ":clock1230:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x90"), ":clock1:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x9c"), ":clock130:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x91"), ":clock2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x9d"), ":clock230:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x92"), ":clock3:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x9e"), ":clock330:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x93"), ":clock4:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x9f"), ":clock430:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x94"), ":clock5:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\xa0"), ":clock530:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x95"), ":clock6:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\xa1"), ":clock630:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x96"), ":clock7:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\xa2"), ":clock730:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x97"), ":clock8:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\xa3"), ":clock830:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x98"), ":clock9:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\xa4"), ":clock930:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x99"), ":clock10:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\xa5"), ":clock1030:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x9a"), ":clock11:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\xa6"), ":clock1130:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8c\x80"), ":cyclone:"}, - Emoji{QString::fromUtf8("\xe2\x99\xa0"), ":spades:"}, - Emoji{QString::fromUtf8("\xe2\x99\xa5"), ":hearts:"}, - Emoji{QString::fromUtf8("\xe2\x99\xa6"), ":diamonds:"}, - Emoji{QString::fromUtf8("\xe2\x99\xa3"), ":clubs:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x83\x8f"), ":black_joker:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x80\x84"), ":mahjong:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb4"), ":flower_playing_cards:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x87"), ":mute:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x88"), ":speaker:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x89"), ":sound:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x8a"), ":loud_sound:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xa2"), ":loudspeaker:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xa3"), ":mega:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x94"), ":bell:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x95"), ":no_bell:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb5"), ":musical_note:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb6"), ":notes:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xb9"), ":chart:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xb1"), ":currency_exchange:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xb2"), ":heavy_dollar_sign:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa7"), ":atm:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xae"), ":put_litter_in_its_place:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb0"), ":potable_water:"}, - Emoji{QString::fromUtf8("\xe2\x99\xbf"), ":wheelchair:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb9"), ":mens:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xba"), ":womens:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xbb"), ":restroom:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xbc"), ":baby_symbol:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xbe"), ":wc:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\x82"), ":passport_control:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\x83"), ":customs:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\x84"), ":baggage_claim:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\x85"), ":left_luggage:"}, - Emoji{QString::fromUtf8("\xe2\x9a\xa0"), ":warning:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb8"), ":children_crossing:"}, - Emoji{QString::fromUtf8("\xe2\x9b\x94"), ":no_entry:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xab"), ":no_entry_sign:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb3"), ":no_bicycles:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xad"), ":no_smoking:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xaf"), ":do_not_litter:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb1"), ":non-potable_water:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb7"), ":no_pedestrians:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xb5"), ":no_mobile_phones:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x9e"), ":underage:"}, - Emoji{QString::fromUtf8("\xe2\x98\xa2"), ":radioactive:"}, - Emoji{QString::fromUtf8("\xe2\x98\xa3"), ":biohazard:"}, - Emoji{QString::fromUtf8("\xe2\xac\x86"), ":arrow_up:"}, - Emoji{QString::fromUtf8("\xe2\x86\x97"), ":arrow_upper_right:"}, - Emoji{QString::fromUtf8("\xe2\x9e\xa1"), ":arrow_right:"}, - Emoji{QString::fromUtf8("\xe2\x86\x98"), ":arrow_lower_right:"}, - Emoji{QString::fromUtf8("\xe2\xac\x87"), ":arrow_down:"}, - Emoji{QString::fromUtf8("\xe2\x86\x99"), ":arrow_lower_left:"}, - Emoji{QString::fromUtf8("\xe2\xac\x85"), ":arrow_left:"}, - Emoji{QString::fromUtf8("\xe2\x86\x96"), ":arrow_upper_left:"}, - Emoji{QString::fromUtf8("\xe2\x86\x95"), ":arrow_up_down:"}, - Emoji{QString::fromUtf8("\xe2\x86\x94"), ":left_right_arrow:"}, - Emoji{QString::fromUtf8("\xe2\x86\xa9"), ":leftwards_arrow_with_hook:"}, - Emoji{QString::fromUtf8("\xe2\x86\xaa"), ":arrow_right_hook:"}, - Emoji{QString::fromUtf8("\xe2\xa4\xb4"), ":arrow_heading_up:"}, - Emoji{QString::fromUtf8("\xe2\xa4\xb5"), ":arrow_heading_down:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x83"), ":arrows_clockwise:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x84"), ":arrows_counterclockwise:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x99"), ":back:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x9a"), ":end:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x9b"), ":on:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x9c"), ":soon:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x9d"), ":top:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x9b\x90"), ":place_of_worship:"}, - Emoji{QString::fromUtf8("\xe2\x9a\x9b"), ":atom:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x89"), ":om_symbol:"}, - Emoji{QString::fromUtf8("\xe2\x9c\xa1"), ":star_of_david:"}, - Emoji{QString::fromUtf8("\xe2\x98\xb8"), ":wheel_of_dharma:"}, - Emoji{QString::fromUtf8("\xe2\x98\xaf"), ":yin_yang:"}, - Emoji{QString::fromUtf8("\xe2\x9c\x9d"), ":cross:"}, - Emoji{QString::fromUtf8("\xe2\x98\xa6"), ":orthodox_cross:"}, - Emoji{QString::fromUtf8("\xe2\x98\xaa"), ":star_and_crescent:"}, - Emoji{QString::fromUtf8("\xe2\x98\xae"), ":peace:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x95\x8e"), ":menorah:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xaf"), ":six_pointed_star:"}, - Emoji{QString::fromUtf8("\xe2\x99\x88"), ":aries:"}, - Emoji{QString::fromUtf8("\xe2\x99\x89"), ":taurus:"}, - Emoji{QString::fromUtf8("\xe2\x99\x8a"), ":gemini:"}, - Emoji{QString::fromUtf8("\xe2\x99\x8b"), ":cancer:"}, - Emoji{QString::fromUtf8("\xe2\x99\x8c"), ":leo:"}, - Emoji{QString::fromUtf8("\xe2\x99\x8d"), ":virgo:"}, - Emoji{QString::fromUtf8("\xe2\x99\x8e"), ":libra:"}, - Emoji{QString::fromUtf8("\xe2\x99\x8f"), ":scorpius:"}, - Emoji{QString::fromUtf8("\xe2\x99\x90"), ":sagittarius:"}, - Emoji{QString::fromUtf8("\xe2\x99\x91"), ":capricorn:"}, - Emoji{QString::fromUtf8("\xe2\x99\x92"), ":aquarius:"}, - Emoji{QString::fromUtf8("\xe2\x99\x93"), ":pisces:"}, - Emoji{QString::fromUtf8("\xe2\x9b\x8e"), ":ophiuchus:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x80"), ":twisted_rightwards_arrows:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x81"), ":repeat:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x82"), ":repeat_one:"}, - Emoji{QString::fromUtf8("\xe2\x96\xb6"), ":arrow_forward:"}, - Emoji{QString::fromUtf8("\xe2\x8f\xa9"), ":fast_forward:"}, - Emoji{QString::fromUtf8("\xe2\x8f\xad"), ":track_next:"}, - Emoji{QString::fromUtf8("\xe2\x8f\xaf"), ":play_pause:"}, - Emoji{QString::fromUtf8("\xe2\x97\x80"), ":arrow_backward:"}, - Emoji{QString::fromUtf8("\xe2\x8f\xaa"), ":rewind:"}, - Emoji{QString::fromUtf8("\xe2\x8f\xae"), ":track_previous:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xbc"), ":arrow_up_small:"}, - Emoji{QString::fromUtf8("\xe2\x8f\xab"), ":arrow_double_up:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xbd"), ":arrow_down_small:"}, - Emoji{QString::fromUtf8("\xe2\x8f\xac"), ":arrow_double_down:"}, - Emoji{QString::fromUtf8("\xe2\x8f\xb8"), ":pause_button:"}, - Emoji{QString::fromUtf8("\xe2\x8f\xb9"), ":stop_button:"}, - Emoji{QString::fromUtf8("\xe2\x8f\xba"), ":record_button:"}, - Emoji{QString::fromUtf8("\xe2\x8f\x8f"), ":eject:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa6"), ":cinema:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x85"), ":low_brightness:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x86"), ":high_brightness:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xb6"), ":signal_strength:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xb3"), ":vibration_mode:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\xb4"), ":mobile_phone_off:"}, - Emoji{QString::fromUtf8("\xe2\x99\xbb"), ":recycle:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x93\x9b"), ":name_badge:"}, - Emoji{QString::fromUtf8("\xe2\x9a\x9c"), ":fleur-de-lis:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xb0"), ":beginner:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xb1"), ":trident:"}, - Emoji{QString::fromUtf8("\xe2\xad\x95"), ":o:"}, - Emoji{QString::fromUtf8("\xe2\x9c\x85"), ":white_check_mark:"}, - Emoji{QString::fromUtf8("\xe2\x98\x91"), ":ballot_box_with_check:"}, - Emoji{QString::fromUtf8("\xe2\x9c\x94"), ":heavy_check_mark:"}, - Emoji{QString::fromUtf8("\xe2\x9c\x96"), ":heavy_multiplication_x:"}, - Emoji{QString::fromUtf8("\xe2\x9d\x8c"), ":x:"}, - Emoji{QString::fromUtf8("\xe2\x9d\x8e"), ":negative_squared_cross_mark:"}, - Emoji{QString::fromUtf8("\xe2\x9e\x95"), ":heavy_plus_sign:"}, - Emoji{QString::fromUtf8("\xe2\x9e\x96"), ":heavy_minus_sign:"}, - Emoji{QString::fromUtf8("\xe2\x9e\x97"), ":heavy_division_sign:"}, - Emoji{QString::fromUtf8("\xe2\x9e\xb0"), ":curly_loop:"}, - Emoji{QString::fromUtf8("\xe2\x9e\xbf"), ":loop:"}, - Emoji{QString::fromUtf8("\xe3\x80\xbd"), ":part_alternation_mark:"}, - Emoji{QString::fromUtf8("\xe2\x9c\xb3"), ":eight_spoked_asterisk:"}, - Emoji{QString::fromUtf8("\xe2\x9c\xb4"), ":eight_pointed_black_star:"}, - Emoji{QString::fromUtf8("\xe2\x9d\x87"), ":sparkle:"}, - Emoji{QString::fromUtf8("\xe2\x80\xbc"), ":bangbang:"}, - Emoji{QString::fromUtf8("\xe2\x81\x89"), ":interrobang:"}, - Emoji{QString::fromUtf8("\xe2\x9d\x93"), ":question:"}, - Emoji{QString::fromUtf8("\xe2\x9d\x94"), ":grey_question:"}, - Emoji{QString::fromUtf8("\xe2\x9d\x95"), ":grey_exclamation:"}, - Emoji{QString::fromUtf8("\xe2\x9d\x97"), ":exclamation:"}, - Emoji{QString::fromUtf8("\xe3\x80\xb0"), ":wavy_dash:"}, - Emoji{QString::fromUtf8("\xc2\xa9"), ":copyright:"}, - Emoji{QString::fromUtf8("\xc2\xae"), ":registered:"}, - Emoji{QString::fromUtf8("\xe2\x84\xa2"), ":tm:"}, - Emoji{QString::fromUtf8("#\xe2\x83\xa3"), ":hash:"}, - Emoji{QString::fromUtf8("*\xe2\x83\xa3"), ":asterisk:"}, - Emoji{QString::fromUtf8("0\xe2\x83\xa3"), ":zero:"}, - Emoji{QString::fromUtf8("1\xe2\x83\xa3"), ":one:"}, - Emoji{QString::fromUtf8("2\xe2\x83\xa3"), ":two:"}, - Emoji{QString::fromUtf8("3\xe2\x83\xa3"), ":three:"}, - Emoji{QString::fromUtf8("4\xe2\x83\xa3"), ":four:"}, - Emoji{QString::fromUtf8("5\xe2\x83\xa3"), ":five:"}, - Emoji{QString::fromUtf8("6\xe2\x83\xa3"), ":six:"}, - Emoji{QString::fromUtf8("7\xe2\x83\xa3"), ":seven:"}, - Emoji{QString::fromUtf8("8\xe2\x83\xa3"), ":eight:"}, - Emoji{QString::fromUtf8("9\xe2\x83\xa3"), ":nine:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x9f"), ":keycap_ten:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xaf"), ":100:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xa0"), ":capital_abcd:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xa1"), ":abcd:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xa2"), ":1234:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xa3"), ":symbols:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xa4"), ":abc:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x85\xb0"), ":a:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x86\x8e"), ":ab:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x85\xb1"), ":b:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x86\x91"), ":cl:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x86\x92"), ":cool:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x86\x93"), ":free:"}, - Emoji{QString::fromUtf8("\xe2\x84\xb9"), ":information_source:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x86\x94"), ":id:"}, - Emoji{QString::fromUtf8("\xe2\x93\x82"), ":m:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x86\x95"), ":new:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x86\x96"), ":ng:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x85\xbe"), ":o2:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x86\x97"), ":ok:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x85\xbf"), ":parking:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x86\x98"), ":sos:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x86\x99"), ":up:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x86\x9a"), ":vs:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x88\x81"), ":koko:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x88\x82"), ":sa:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x88\xb7"), ":u6708:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x88\xb6"), ":u6709:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x88\xaf"), ":u6307:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x89\x90"), ":ideograph_advantage:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x88\xb9"), ":u5272:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x88\x9a"), ":u7121:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x88\xb2"), ":u7981:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x89\x91"), ":accept:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x88\xb8"), ":u7533:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x88\xb4"), ":u5408:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x88\xb3"), ":u7a7a:"}, - Emoji{QString::fromUtf8("\xe3\x8a\x97"), ":congratulations:"}, - Emoji{QString::fromUtf8("\xe3\x8a\x99"), ":secret:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x88\xba"), ":u55b6:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x88\xb5"), ":u6e80:"}, - Emoji{QString::fromUtf8("\xe2\x96\xaa"), ":black_small_square:"}, - Emoji{QString::fromUtf8("\xe2\x96\xab"), ":white_small_square:"}, - Emoji{QString::fromUtf8("\xe2\x97\xbb"), ":white_medium_square:"}, - Emoji{QString::fromUtf8("\xe2\x97\xbc"), ":black_medium_square:"}, - Emoji{QString::fromUtf8("\xe2\x97\xbd"), ":white_medium_small_square:"}, - Emoji{QString::fromUtf8("\xe2\x97\xbe"), ":black_medium_small_square:"}, - Emoji{QString::fromUtf8("\xe2\xac\x9b"), ":black_large_square:"}, - Emoji{QString::fromUtf8("\xe2\xac\x9c"), ":white_large_square:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xb6"), ":large_orange_diamond:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xb7"), ":large_blue_diamond:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xb8"), ":small_orange_diamond:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xb9"), ":small_blue_diamond:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xba"), ":small_red_triangle:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xbb"), ":small_red_triangle_down:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x92\xa0"), ":diamond_shape_with_a_dot_inside:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\x98"), ":radio_button:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xb2"), ":black_square_button:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xb3"), ":white_square_button:"}, - Emoji{QString::fromUtf8("\xe2\x9a\xaa"), ":white_circle:"}, - Emoji{QString::fromUtf8("\xe2\x9a\xab"), ":black_circle:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xb4"), ":red_circle:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x94\xb5"), ":blue_circle:"}, -}; - -const QList EmojiProvider::flags = { - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xa8"), ":flag_ac:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xa9"), ":flag_ad:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xaa"), ":flag_ae:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xab"), ":flag_af:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xac"), ":flag_ag:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xae"), ":flag_ai:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb1"), ":flag_al:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb2"), ":flag_am:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb4"), ":flag_ao:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb6"), ":flag_aq:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb7"), ":flag_ar:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb8"), ":flag_as:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb9"), ":flag_at:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xba"), ":flag_au:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xbc"), ":flag_aw:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xbd"), ":flag_ax:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xbf"), ":flag_az:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xa6"), ":flag_ba:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xa7"), ":flag_bb:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xa9"), ":flag_bd:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xaa"), ":flag_be:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xab"), ":flag_bf:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xac"), ":flag_bg:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xad"), ":flag_bh:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xae"), ":flag_bi:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xaf"), ":flag_bj:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb1"), ":flag_bl:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb2"), ":flag_bm:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb3"), ":flag_bn:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb4"), ":flag_bo:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb6"), ":flag_bq:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb7"), ":flag_br:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb8"), ":flag_bs:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb9"), ":flag_bt:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbb"), ":flag_bv:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbc"), ":flag_bw:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbe"), ":flag_by:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbf"), ":flag_bz:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xa6"), ":flag_ca:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xa8"), ":flag_cc:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xa9"), ":flag_cd:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xab"), ":flag_cf:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xac"), ":flag_cg:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xad"), ":flag_ch:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xae"), ":flag_ci:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb0"), ":flag_ck:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb1"), ":flag_cl:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb2"), ":flag_cm:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb3"), ":flag_cn:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb4"), ":flag_co:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb5"), ":flag_cp:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb7"), ":flag_cr:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xba"), ":flag_cu:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbb"), ":flag_cv:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbc"), ":flag_cw:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbd"), ":flag_cx:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbe"), ":flag_cy:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbf"), ":flag_cz:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xaa"), ":flag_de:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xac"), ":flag_dg:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xaf"), ":flag_dj:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xb0"), ":flag_dk:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xb2"), ":flag_dm:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xb4"), ":flag_do:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xbf"), ":flag_dz:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xa6"), ":flag_ea:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xa8"), ":flag_ec:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xaa"), ":flag_ee:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xac"), ":flag_eg:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xad"), ":flag_eh:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xb7"), ":flag_er:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xb8"), ":flag_es:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xb9"), ":flag_et:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xba"), ":flag_eu:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xae"), ":flag_fi:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xaf"), ":flag_fj:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb0"), ":flag_fk:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb2"), ":flag_fm:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb4"), ":flag_fo:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb7"), ":flag_fr:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xa6"), ":flag_ga:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xa7"), ":flag_gb:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xa9"), ":flag_gd:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xaa"), ":flag_ge:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xab"), ":flag_gf:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xac"), ":flag_gg:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xad"), ":flag_gh:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xae"), ":flag_gi:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb1"), ":flag_gl:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb2"), ":flag_gm:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb3"), ":flag_gn:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb5"), ":flag_gp:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb6"), ":flag_gq:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb7"), ":flag_gr:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb8"), ":flag_gs:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb9"), ":flag_gt:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xba"), ":flag_gu:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xbc"), ":flag_gw:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xbe"), ":flag_gy:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb0"), ":flag_hk:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb2"), ":flag_hm:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb3"), ":flag_hn:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb7"), ":flag_hr:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb9"), ":flag_ht:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xba"), ":flag_hu:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xa8"), ":flag_ic:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xa9"), ":flag_id:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xaa"), ":flag_ie:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb1"), ":flag_il:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb2"), ":flag_im:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb3"), ":flag_in:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb4"), ":flag_io:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb6"), ":flag_iq:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb7"), ":flag_ir:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb8"), ":flag_is:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb9"), ":flag_it:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xaa"), ":flag_je:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xb2"), ":flag_jm:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xb4"), ":flag_jo:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xb5"), ":flag_jp:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xaa"), ":flag_ke:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xac"), ":flag_kg:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xad"), ":flag_kh:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xae"), ":flag_ki:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb2"), ":flag_km:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb3"), ":flag_kn:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb5"), ":flag_kp:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb7"), ":flag_kr:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xbc"), ":flag_kw:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xbe"), ":flag_ky:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xbf"), ":flag_kz:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xa6"), ":flag_la:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xa7"), ":flag_lb:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xa8"), ":flag_lc:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xae"), ":flag_li:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb0"), ":flag_lk:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb7"), ":flag_lr:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb8"), ":flag_ls:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb9"), ":flag_lt:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xba"), ":flag_lu:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xbb"), ":flag_lv:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xbe"), ":flag_ly:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xa6"), ":flag_ma:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xa8"), ":flag_mc:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xa9"), ":flag_md:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xaa"), ":flag_me:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xab"), ":flag_mf:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xac"), ":flag_mg:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xad"), ":flag_mh:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb0"), ":flag_mk:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb1"), ":flag_ml:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb2"), ":flag_mm:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb3"), ":flag_mn:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb4"), ":flag_mo:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb5"), ":flag_mp:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb6"), ":flag_mq:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb7"), ":flag_mr:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb8"), ":flag_ms:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb9"), ":flag_mt:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xba"), ":flag_mu:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbb"), ":flag_mv:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbc"), ":flag_mw:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbd"), ":flag_mx:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbe"), ":flag_my:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbf"), ":flag_mz:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xa6"), ":flag_na:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xa8"), ":flag_nc:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xaa"), ":flag_ne:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xab"), ":flag_nf:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xac"), ":flag_ng:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xae"), ":flag_ni:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb1"), ":flag_nl:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb4"), ":flag_no:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb5"), ":flag_np:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb7"), ":flag_nr:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xba"), ":flag_nu:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xbf"), ":flag_nz:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb4\xf0\x9f\x87\xb2"), ":flag_om:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xa6"), ":flag_pa:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xaa"), ":flag_pe:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xab"), ":flag_pf:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xac"), ":flag_pg:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xad"), ":flag_ph:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb0"), ":flag_pk:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb1"), ":flag_pl:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb2"), ":flag_pm:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb3"), ":flag_pn:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb7"), ":flag_pr:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb8"), ":flag_ps:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb9"), ":flag_pt:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xbc"), ":flag_pw:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xbe"), ":flag_py:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb6\xf0\x9f\x87\xa6"), ":flag_qa:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xaa"), ":flag_re:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xb4"), ":flag_ro:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xb8"), ":flag_rs:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xba"), ":flag_ru:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xbc"), ":flag_rw:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa6"), ":flag_sa:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa7"), ":flag_sb:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa8"), ":flag_sc:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa9"), ":flag_sd:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xaa"), ":flag_se:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xac"), ":flag_sg:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xad"), ":flag_sh:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xae"), ":flag_si:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xaf"), ":flag_sj:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb0"), ":flag_sk:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb1"), ":flag_sl:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb2"), ":flag_sm:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb3"), ":flag_sn:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb4"), ":flag_so:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb7"), ":flag_sr:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb8"), ":flag_ss:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb9"), ":flag_st:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbb"), ":flag_sv:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbd"), ":flag_sx:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbe"), ":flag_sy:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbf"), ":flag_sz:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xa6"), ":flag_ta:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xa8"), ":flag_tc:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xa9"), ":flag_td:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xab"), ":flag_tf:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xac"), ":flag_tg:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xad"), ":flag_th:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xaf"), ":flag_tj:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb0"), ":flag_tk:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb1"), ":flag_tl:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb2"), ":flag_tm:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb3"), ":flag_tn:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb4"), ":flag_to:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb7"), ":flag_tr:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb9"), ":flag_tt:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xbb"), ":flag_tv:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xbc"), ":flag_tw:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xbf"), ":flag_tz:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xa6"), ":flag_ua:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xac"), ":flag_ug:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xb2"), ":flag_um:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xb8"), ":flag_us:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xbe"), ":flag_uy:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xbf"), ":flag_uz:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xa6"), ":flag_va:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xa8"), ":flag_vc:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xaa"), ":flag_ve:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xac"), ":flag_vg:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xae"), ":flag_vi:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xb3"), ":flag_vn:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xba"), ":flag_vu:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xbc\xf0\x9f\x87\xab"), ":flag_wf:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xbc\xf0\x9f\x87\xb8"), ":flag_ws:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xbd\xf0\x9f\x87\xb0"), ":flag_xk:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xbe\xf0\x9f\x87\xaa"), ":flag_ye:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xbe\xf0\x9f\x87\xb9"), ":flag_yt:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xbf\xf0\x9f\x87\xa6"), ":flag_za:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xbf\xf0\x9f\x87\xb2"), ":flag_zm:"}, - Emoji{QString::fromUtf8("\xf0\x9f\x87\xbf\xf0\x9f\x87\xbc"), ":flag_zw:"}, -}; diff --git a/src/FileItem.cc b/src/FileItem.cc deleted file mode 100644 index 96fd9c07..00000000 --- a/src/FileItem.cc +++ /dev/null @@ -1,209 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "FileItem.h" -#include "ImageOverlayDialog.h" - -namespace events = matrix::events; -namespace msgs = matrix::events::messages; - -void -FileItem::init() -{ - setMouseTracking(true); - setCursor(Qt::PointingHandCursor); - setAttribute(Qt::WA_Hover, true); - - icon_.addFile(":/icons/icons/ui/arrow-pointing-down.png"); - - QList url_parts = url_.toString().split("mxc://"); - if (url_parts.size() != 2) { - qDebug() << "Invalid format for image" << url_.toString(); - return; - } - - QString media_params = url_parts[1]; - url_ = QString("%1/_matrix/media/r0/download/%2") - .arg(client_.data()->getHomeServer().toString(), media_params); - - connect(client_.data(), &MatrixClient::fileDownloaded, this, &FileItem::fileDownloaded); -} - -FileItem::FileItem(QSharedPointer client, - const events::MessageEvent &event, - QWidget *parent) - : QWidget(parent) - , url_{event.msgContent().url()} - , text_{event.content().body()} - , event_{event} - , client_{client} -{ - readableFileSize_ = calculateFileSize(event.msgContent().info().size); - - init(); -} - -FileItem::FileItem(QSharedPointer client, - const QString &url, - const QString &filename, - QWidget *parent) - : QWidget(parent) - , url_{url} - , text_{QFileInfo(filename).fileName()} - , client_{client} -{ - readableFileSize_ = calculateFileSize(QFileInfo(filename).size()); - - init(); -} - -QString -FileItem::calculateFileSize(int nbytes) const -{ - if (nbytes < 1024) - return QString("%1 B").arg(nbytes); - - if (nbytes < 1024 * 1024) - return QString("%1 KB").arg(nbytes / 1024); - - return QString("%1 MB").arg(nbytes / 1024 / 1024); -} - -void -FileItem::openUrl() -{ - if (url_.toString().isEmpty()) - return; - - if (!QDesktopServices::openUrl(url_)) - qWarning() << "Could not open url" << url_.toString(); -} - -QSize -FileItem::sizeHint() const -{ - return QSize(MaxWidth, Height); -} - -void -FileItem::mousePressEvent(QMouseEvent *event) -{ - if (event->button() != Qt::LeftButton) - return; - - auto point = event->pos(); - - // Click on the download icon. - if (QRect(HorizontalPadding, VerticalPadding / 2, IconDiameter, IconDiameter) - .contains(point)) { - filenameToSave_ = QFileDialog::getSaveFileName(this, tr("Save File"), text_); - - if (filenameToSave_.isEmpty()) - return; - - client_->downloadFile(event_.eventId(), url_); - } else { - openUrl(); - } -} - -void -FileItem::fileDownloaded(const QString &event_id, const QByteArray &data) -{ - if (event_id != event_.eventId()) - return; - - try { - QFile file(filenameToSave_); - - if (!file.open(QIODevice::WriteOnly)) - return; - - file.write(data); - file.close(); - } catch (const std::exception &ex) { - qDebug() << "Error while saving file to:" << ex.what(); - } -} - -void -FileItem::paintEvent(QPaintEvent *event) -{ - Q_UNUSED(event); - - QPainter painter(this); - painter.setRenderHint(QPainter::Antialiasing); - - QFont font("Open Sans"); - font.setPixelSize(12); - font.setWeight(80); - - QFontMetrics fm(font); - - int computedWidth = std::min( - fm.width(text_) + 2 * IconRadius + VerticalPadding * 2 + TextPadding, (double)MaxWidth); - - QPainterPath path; - path.addRoundedRect(QRectF(0, 0, computedWidth, Height), 10, 10); - - painter.setPen(Qt::NoPen); - painter.fillPath(path, backgroundColor_); - painter.drawPath(path); - - QPainterPath circle; - circle.addEllipse(QPoint(IconXCenter, IconYCenter), IconRadius, IconRadius); - - painter.setPen(Qt::NoPen); - painter.fillPath(circle, iconColor_); - painter.drawPath(circle); - - icon_.paint(&painter, - QRect(IconXCenter - DownloadIconRadius / 2, - IconYCenter - DownloadIconRadius / 2, - DownloadIconRadius, - DownloadIconRadius), - Qt::AlignCenter, - QIcon::Normal); - - const int textStartX = HorizontalPadding + 2 * IconRadius + TextPadding; - const int textStartY = VerticalPadding + fm.ascent() / 2; - - // Draw the filename. - QString elidedText = - fm.elidedText(text_, - Qt::ElideRight, - computedWidth - HorizontalPadding * 2 - TextPadding - 2 * IconRadius); - - painter.setFont(font); - painter.setPen(QPen(textColor_)); - painter.drawText(QPoint(textStartX, textStartY), elidedText); - - // Draw the filesize. - font.setWeight(50); - painter.setFont(font); - painter.setPen(QPen(textColor_)); - painter.drawText(QPoint(textStartX, textStartY + 1.5 * fm.ascent()), readableFileSize_); -} diff --git a/src/ImageItem.cc b/src/ImageItem.cc deleted file mode 100644 index 333fd296..00000000 --- a/src/ImageItem.cc +++ /dev/null @@ -1,223 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#include -#include -#include -#include -#include -#include - -#include "ImageItem.h" -#include "ImageOverlayDialog.h" - -namespace events = matrix::events; -namespace msgs = matrix::events::messages; - -ImageItem::ImageItem(QSharedPointer client, - const events::MessageEvent &event, - QWidget *parent) - : QWidget(parent) - , event_{event} - , client_{client} -{ - setMouseTracking(true); - setCursor(Qt::PointingHandCursor); - setAttribute(Qt::WA_Hover, true); - - url_ = event.msgContent().url(); - text_ = event.content().body(); - - QList url_parts = url_.toString().split("mxc://"); - - if (url_parts.size() != 2) { - qDebug() << "Invalid format for image" << url_.toString(); - return; - } - - QString media_params = url_parts[1]; - url_ = QString("%1/_matrix/media/r0/download/%2") - .arg(client_.data()->getHomeServer().toString(), media_params); - - client_.data()->downloadImage(event.eventId(), url_); - - connect(client_.data(), - SIGNAL(imageDownloaded(const QString &, const QPixmap &)), - this, - SLOT(imageDownloaded(const QString &, const QPixmap &))); -} - -ImageItem::ImageItem(QSharedPointer client, - const QString &url, - const QString &filename, - QWidget *parent) - : QWidget(parent) - , url_{url} - , text_{QFileInfo(filename).fileName()} - , client_{client} -{ - setMouseTracking(true); - setCursor(Qt::PointingHandCursor); - setAttribute(Qt::WA_Hover, true); - - QList url_parts = url_.toString().split("mxc://"); - - if (url_parts.size() != 2) { - qDebug() << "Invalid format for image" << url_.toString(); - return; - } - - QString media_params = url_parts[1]; - url_ = QString("%1/_matrix/media/r0/download/%2") - .arg(client_.data()->getHomeServer().toString(), media_params); - - setImage(QPixmap(filename)); -} - -void -ImageItem::imageDownloaded(const QString &event_id, const QPixmap &img) -{ - if (event_id != event_.eventId()) - return; - - setImage(img); -} - -void -ImageItem::openUrl() -{ - if (url_.toString().isEmpty()) - return; - - if (!QDesktopServices::openUrl(url_)) - qWarning() << "Could not open url" << url_.toString(); -} - -void -ImageItem::scaleImage() -{ - if (image_.isNull()) - return; - - auto width_ratio = (double)max_width_ / (double)image_.width(); - auto height_ratio = (double)max_height_ / (double)image_.height(); - - auto min_aspect_ratio = std::min(width_ratio, height_ratio); - - if (min_aspect_ratio > 1) { - width_ = image_.width(); - height_ = image_.height(); - } else { - width_ = image_.width() * min_aspect_ratio; - height_ = image_.height() * min_aspect_ratio; - } - - setFixedSize(width_, height_); - scaled_image_ = - image_.scaled(width_, height_, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); -} - -QSize -ImageItem::sizeHint() const -{ - if (image_.isNull()) - return QSize(max_width_, bottom_height_); - - return QSize(width_, height_); -} - -void -ImageItem::setImage(const QPixmap &image) -{ - image_ = image; - scaleImage(); - update(); -} - -void -ImageItem::mousePressEvent(QMouseEvent *event) -{ - if (event->button() != Qt::LeftButton) - return; - - if (image_.isNull()) { - openUrl(); - return; - } - - auto point = event->pos(); - - // Click on the text box. - if (QRect(0, height_ - bottom_height_, width_, bottom_height_).contains(point)) { - openUrl(); - } else { - auto image_dialog = new ImageOverlayDialog(image_, this); - image_dialog->show(); - } -} - -void -ImageItem::resizeEvent(QResizeEvent *event) -{ - Q_UNUSED(event); - - scaleImage(); -} - -void -ImageItem::paintEvent(QPaintEvent *event) -{ - Q_UNUSED(event); - - QPainter painter(this); - painter.setRenderHint(QPainter::Antialiasing); - - QFont font("Open Sans"); - font.setPixelSize(12); - - QFontMetrics metrics(font); - int fontHeight = metrics.height(); - - if (image_.isNull()) { - int height = fontHeight + 10; - - QString elidedText = metrics.elidedText(text_, Qt::ElideRight, max_width_ - 10); - - setFixedSize(metrics.width(elidedText), fontHeight + 10); - - painter.setFont(font); - painter.setPen(QPen(QColor(66, 133, 244))); - painter.drawText(QPoint(0, height / 2 + 2), elidedText); - - return; - } - - painter.fillRect(QRect(0, 0, width_, height_), scaled_image_); - - if (underMouse()) { - // Bottom text section - painter.fillRect(QRect(0, height_ - bottom_height_, width_, bottom_height_), - QBrush(QColor(33, 33, 33, 128))); - - QString elidedText = metrics.elidedText(text_, Qt::ElideRight, width_ - 10); - - font.setWeight(80); - painter.setFont(font); - painter.setPen(QPen(QColor("white"))); - painter.drawText(QPoint(5, height_ - fontHeight / 2), elidedText); - } -} diff --git a/src/ImageOverlayDialog.cc b/src/ImageOverlayDialog.cc deleted file mode 100644 index 7dd4a226..00000000 --- a/src/ImageOverlayDialog.cc +++ /dev/null @@ -1,129 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#include -#include -#include - -#include "ImageOverlayDialog.h" - -ImageOverlayDialog::ImageOverlayDialog(QPixmap image, QWidget *parent) - : QWidget{parent} - , originalImage_{image} -{ - setMouseTracking(true); - setParent(0); - - setWindowFlags(windowFlags() | Qt::FramelessWindowHint); - - setAttribute(Qt::WA_NoSystemBackground, true); - setAttribute(Qt::WA_TranslucentBackground, true); - setAttribute(Qt::WA_DeleteOnClose, true); - setWindowState(Qt::WindowFullScreen); - - screen_ = QApplication::desktop()->availableGeometry(); - - move(QApplication::desktop()->mapToGlobal(screen_.topLeft())); - resize(screen_.size()); - - connect(this, SIGNAL(closing()), this, SLOT(close())); - - raise(); -} - -// TODO: Move this into Utils -void -ImageOverlayDialog::scaleImage(int max_width, int max_height) -{ - if (originalImage_.isNull()) - return; - - auto width_ratio = (double)max_width / (double)originalImage_.width(); - auto height_ratio = (double)max_height / (double)originalImage_.height(); - - auto min_aspect_ratio = std::min(width_ratio, height_ratio); - - int final_width = 0; - int final_height = 0; - - if (min_aspect_ratio > 1) { - final_width = originalImage_.width(); - final_height = originalImage_.height(); - } else { - final_width = originalImage_.width() * min_aspect_ratio; - final_height = originalImage_.height() * min_aspect_ratio; - } - - image_ = originalImage_.scaled( - final_width, final_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); -} - -void -ImageOverlayDialog::paintEvent(QPaintEvent *event) -{ - Q_UNUSED(event); - - QPainter painter(this); - painter.setRenderHint(QPainter::Antialiasing); - - // Full screen overlay. - painter.fillRect(QRect(0, 0, screen_.width(), screen_.height()), QColor(55, 55, 55, 170)); - - // Left and Right margins - int outer_margin = screen_.width() * 0.12; - int buttonSize = 36; - int margin = outer_margin * 0.1; - - int max_width = screen_.width() - 2 * outer_margin; - int max_height = screen_.height(); - - scaleImage(max_width, max_height); - - int diff_x = max_width - image_.width(); - int diff_y = max_height - image_.height(); - - content_ = QRect(outer_margin + diff_x / 2, diff_y / 2, image_.width(), image_.height()); - close_button_ = - QRect(screen_.width() - margin - buttonSize, margin, buttonSize, buttonSize); - - // Draw main content_. - painter.drawPixmap(content_, image_); - - // Draw top right corner X. - QPen pen; - pen.setCapStyle(Qt::RoundCap); - pen.setWidthF(5); - pen.setColor("gray"); - - auto center = close_button_.center(); - - painter.setPen(pen); - painter.drawLine(center - QPointF(15, 15), center + QPointF(15, 15)); - painter.drawLine(center + QPointF(15, -15), center - QPointF(15, -15)); -} - -void -ImageOverlayDialog::mousePressEvent(QMouseEvent *event) -{ - if (event->button() != Qt::LeftButton) - return; - - if (close_button_.contains(event->pos())) - emit closing(); - else if (!content_.contains(event->pos())) - emit closing(); -} diff --git a/src/JoinRoomDialog.cc b/src/JoinRoomDialog.cc deleted file mode 100644 index a4d241c0..00000000 --- a/src/JoinRoomDialog.cc +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include - -#include "Config.h" -#include "FlatButton.h" -#include "JoinRoomDialog.h" -#include "Theme.h" - -JoinRoomDialog::JoinRoomDialog(QWidget *parent) - : QFrame(parent) -{ - setMaximumSize(400, 400); - - auto layout = new QVBoxLayout(this); - layout->setSpacing(30); - layout->setMargin(20); - - auto buttonLayout = new QHBoxLayout(); - buttonLayout->setSpacing(0); - buttonLayout->setMargin(0); - - confirmBtn_ = new FlatButton("JOIN", this); - confirmBtn_->setFontSize(conf::btn::fontSize); - - cancelBtn_ = new FlatButton(tr("CANCEL"), this); - cancelBtn_->setFontSize(conf::btn::fontSize); - - buttonLayout->addStretch(1); - buttonLayout->addWidget(confirmBtn_); - buttonLayout->addWidget(cancelBtn_); - - QFont font; - font.setPixelSize(conf::headerFontSize); - - auto label = new QLabel(tr("Room alias to join:"), this); - label->setFont(font); - - roomAliasEdit_ = new QLineEdit(this); - - layout->addWidget(label); - layout->addWidget(roomAliasEdit_); - layout->addLayout(buttonLayout); - - connect(confirmBtn_, &QPushButton::clicked, [=]() { - emit closing(true, roomAliasEdit_->text()); - }); - connect(cancelBtn_, &QPushButton::clicked, [=]() { emit closing(false, nullptr); }); -} diff --git a/src/LeaveRoomDialog.cc b/src/LeaveRoomDialog.cc deleted file mode 100644 index e9877a24..00000000 --- a/src/LeaveRoomDialog.cc +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include -#include - -#include "Config.h" -#include "FlatButton.h" -#include "LeaveRoomDialog.h" -#include "Theme.h" - -LeaveRoomDialog::LeaveRoomDialog(QWidget *parent) - : QFrame(parent) -{ - setMaximumSize(400, 400); - - auto layout = new QVBoxLayout(this); - layout->setSpacing(30); - layout->setMargin(20); - - auto buttonLayout = new QHBoxLayout(); - buttonLayout->setSpacing(0); - buttonLayout->setMargin(0); - - confirmBtn_ = new FlatButton("LEAVE", this); - confirmBtn_->setFontSize(conf::btn::fontSize); - - cancelBtn_ = new FlatButton(tr("CANCEL"), this); - cancelBtn_->setFontSize(conf::btn::fontSize); - - buttonLayout->addStretch(1); - buttonLayout->addWidget(confirmBtn_); - buttonLayout->addWidget(cancelBtn_); - - QFont font; - font.setPixelSize(conf::headerFontSize); - - auto label = new QLabel(tr("Are you sure you want to leave?"), this); - label->setFont(font); - - layout->addWidget(label); - layout->addLayout(buttonLayout); - - connect(confirmBtn_, &QPushButton::clicked, [=]() { emit closing(true); }); - connect(cancelBtn_, &QPushButton::clicked, [=]() { emit closing(false); }); -} - -void -LeaveRoomDialog::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} diff --git a/src/LogoutDialog.cc b/src/LogoutDialog.cc deleted file mode 100644 index cda52016..00000000 --- a/src/LogoutDialog.cc +++ /dev/null @@ -1,71 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#include -#include -#include -#include - -#include "Config.h" -#include "FlatButton.h" -#include "LogoutDialog.h" -#include "Theme.h" - -LogoutDialog::LogoutDialog(QWidget *parent) - : QFrame(parent) -{ - setMaximumSize(400, 400); - - auto layout = new QVBoxLayout(this); - layout->setSpacing(30); - layout->setMargin(20); - - auto buttonLayout = new QHBoxLayout(); - buttonLayout->setSpacing(0); - buttonLayout->setMargin(0); - - confirmBtn_ = new FlatButton("OK", this); - confirmBtn_->setFontSize(conf::btn::fontSize); - - cancelBtn_ = new FlatButton(tr("CANCEL"), this); - cancelBtn_->setFontSize(conf::btn::fontSize); - - buttonLayout->addStretch(1); - buttonLayout->addWidget(confirmBtn_); - buttonLayout->addWidget(cancelBtn_); - - QFont font; - font.setPixelSize(conf::headerFontSize); - - auto label = new QLabel(tr("Logout. Are you sure?"), this); - label->setFont(font); - - layout->addWidget(label); - layout->addLayout(buttonLayout); - - connect(confirmBtn_, &QPushButton::clicked, [=]() { emit closing(true); }); - connect(cancelBtn_, &QPushButton::clicked, [=]() { emit closing(false); }); -} - -void -LogoutDialog::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} diff --git a/src/RoomList.cc b/src/RoomList.cc index 7560a616..6b8867f3 100644 --- a/src/RoomList.cc +++ b/src/RoomList.cc @@ -18,8 +18,9 @@ #include #include -#include "JoinRoomDialog.h" -#include "LeaveRoomDialog.h" +#include "dialogs/JoinRoomDialog.h" +#include "dialogs/LeaveRoomDialog.h" + #include "MainWindow.h" #include "MatrixClient.h" #include "OverlayModal.h" diff --git a/src/TimelineItem.cc b/src/TimelineItem.cc deleted file mode 100644 index 7297abc3..00000000 --- a/src/TimelineItem.cc +++ /dev/null @@ -1,506 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#include -#include -#include -#include - -#include "Avatar.h" -#include "Config.h" -#include "FileItem.h" -#include "ImageItem.h" -#include "Sync.h" -#include "TimelineItem.h" - -static const QRegExp URL_REGEX("((?:https?|ftp)://\\S+)"); -static const QString URL_HTML = "\\1"; - -namespace events = matrix::events; -namespace msgs = matrix::events::messages; - -void -TimelineItem::init() -{ - userAvatar_ = nullptr; - timestamp_ = nullptr; - userName_ = nullptr; - body_ = nullptr; - - font_.setPixelSize(conf::fontSize); - - QFontMetrics fm(font_); - - topLayout_ = new QHBoxLayout(this); - sideLayout_ = new QVBoxLayout(); - mainLayout_ = new QVBoxLayout(); - headerLayout_ = new QHBoxLayout(); - - topLayout_->setContentsMargins(conf::timeline::msgMargin, conf::timeline::msgMargin, 0, 0); - topLayout_->setSpacing(0); - - topLayout_->addLayout(sideLayout_); - topLayout_->addLayout(mainLayout_, 1); - - sideLayout_->setMargin(0); - sideLayout_->setSpacing(0); - - mainLayout_->setContentsMargins(conf::timeline::headerLeftMargin, 0, 0, 0); - mainLayout_->setSpacing(0); - - headerLayout_->setMargin(0); - headerLayout_->setSpacing(conf::timeline::headerSpacing); -} - -/* - * For messages created locally. - */ -TimelineItem::TimelineItem(events::MessageEventType ty, - const QString &userid, - QString body, - bool withSender, - QWidget *parent) - : QWidget(parent) -{ - init(); - - auto displayName = TimelineViewManager::displayName(userid); - auto timestamp = QDateTime::currentDateTime(); - - if (ty == events::MessageEventType::Emote) { - body = QString("* %1 %2").arg(displayName).arg(body); - descriptionMsg_ = {"", userid, body, descriptiveTime(timestamp)}; - } else { - descriptionMsg_ = { - "You: ", userid, body, descriptiveTime(QDateTime::currentDateTime())}; - } - - body = body.toHtmlEscaped(); - body.replace(URL_REGEX, URL_HTML); - body.replace("\n", "
"); - generateTimestamp(timestamp); - - if (withSender) { - generateBody(displayName, body); - setupAvatarLayout(displayName); - mainLayout_->addLayout(headerLayout_); - - AvatarProvider::resolve(userid, this); - } else { - generateBody(body); - setupSimpleLayout(); - } - - mainLayout_->addWidget(body_); -} - -TimelineItem::TimelineItem(ImageItem *image, - const QString &userid, - bool withSender, - QWidget *parent) - : QWidget{parent} -{ - init(); - - setupLocalWidgetLayout(image, userid, "sent an image", withSender); -} - -TimelineItem::TimelineItem(FileItem *file, const QString &userid, bool withSender, QWidget *parent) - : QWidget{parent} -{ - init(); - - setupLocalWidgetLayout(file, userid, "sent a file", withSender); -} - -/* - * Used to display images. The avatar and the username are displayed. - */ -TimelineItem::TimelineItem(ImageItem *image, - const events::MessageEvent &event, - bool with_sender, - QWidget *parent) - : QWidget(parent) -{ - init(); - - event_id_ = event.eventId(); - - auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp()); - auto displayName = TimelineViewManager::displayName(event.sender()); - - QSettings settings; - descriptionMsg_ = {event.sender() == settings.value("auth/user_id") ? "You" : displayName, - event.sender(), - " sent an image", - descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))}; - - generateTimestamp(timestamp); - - auto imageLayout = new QHBoxLayout(); - imageLayout->setContentsMargins(0, 5, 0, 0); - imageLayout->addWidget(image); - imageLayout->addStretch(1); - - if (with_sender) { - generateBody(displayName, ""); - setupAvatarLayout(displayName); - - mainLayout_->addLayout(headerLayout_); - - AvatarProvider::resolve(event.sender(), this); - } else { - setupSimpleLayout(); - } - - mainLayout_->addLayout(imageLayout); -} - -TimelineItem::TimelineItem(FileItem *file, - const events::MessageEvent &event, - bool with_sender, - QWidget *parent) - : QWidget(parent) -{ - init(); - - event_id_ = event.eventId(); - - auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp()); - auto displayName = TimelineViewManager::displayName(event.sender()); - - QSettings settings; - descriptionMsg_ = {event.sender() == settings.value("auth/user_id") ? "You" : displayName, - event.sender(), - " sent a file", - descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))}; - - generateTimestamp(timestamp); - - auto fileLayout = new QHBoxLayout(); - fileLayout->setContentsMargins(0, 5, 0, 0); - fileLayout->addWidget(file); - fileLayout->addStretch(1); - - if (with_sender) { - generateBody(displayName, ""); - setupAvatarLayout(displayName); - - mainLayout_->addLayout(headerLayout_); - - AvatarProvider::resolve(event.sender(), this); - } else { - setupSimpleLayout(); - } - - mainLayout_->addLayout(fileLayout); -} - -/* - * Used to display remote notice messages. - */ -TimelineItem::TimelineItem(const events::MessageEvent &event, - bool with_sender, - QWidget *parent) - : QWidget(parent) -{ - init(); - - event_id_ = event.eventId(); - - descriptionMsg_ = {TimelineViewManager::displayName(event.sender()), - event.sender(), - " sent a notification", - descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))}; - - auto body = event.content().body().trimmed().toHtmlEscaped(); - auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp()); - - generateTimestamp(timestamp); - - body.replace(URL_REGEX, URL_HTML); - body.replace("\n", "
"); - body = "" + body + ""; - - if (with_sender) { - auto displayName = TimelineViewManager::displayName(event.sender()); - - generateBody(displayName, body); - setupAvatarLayout(displayName); - - mainLayout_->addLayout(headerLayout_); - - AvatarProvider::resolve(event.sender(), this); - } else { - generateBody(body); - setupSimpleLayout(); - } - - mainLayout_->addWidget(body_); -} - -/* - * Used to display remote emote messages. - */ -TimelineItem::TimelineItem(const events::MessageEvent &event, - bool with_sender, - QWidget *parent) - : QWidget(parent) -{ - init(); - - event_id_ = event.eventId(); - - auto body = event.content().body().trimmed(); - auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp()); - auto displayName = TimelineViewManager::displayName(event.sender()); - auto emoteMsg = QString("* %1 %2").arg(displayName).arg(body); - - descriptionMsg_ = {"", - event.sender(), - emoteMsg, - descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))}; - - generateTimestamp(timestamp); - emoteMsg = emoteMsg.toHtmlEscaped(); - emoteMsg.replace(URL_REGEX, URL_HTML); - emoteMsg.replace("\n", "
"); - - if (with_sender) { - generateBody(displayName, emoteMsg); - setupAvatarLayout(displayName); - mainLayout_->addLayout(headerLayout_); - - AvatarProvider::resolve(event.sender(), this); - } else { - generateBody(emoteMsg); - setupSimpleLayout(); - } - - mainLayout_->addWidget(body_); -} - -/* - * Used to display remote text messages. - */ -TimelineItem::TimelineItem(const events::MessageEvent &event, - bool with_sender, - QWidget *parent) - : QWidget(parent) -{ - init(); - - event_id_ = event.eventId(); - - auto body = event.content().body().trimmed(); - auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp()); - auto displayName = TimelineViewManager::displayName(event.sender()); - - QSettings settings; - descriptionMsg_ = {event.sender() == settings.value("auth/user_id") ? "You" : displayName, - event.sender(), - QString(": %1").arg(body), - descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))}; - - generateTimestamp(timestamp); - - body = body.toHtmlEscaped(); - body.replace(URL_REGEX, URL_HTML); - body.replace("\n", "
"); - - if (with_sender) { - generateBody(displayName, body); - setupAvatarLayout(displayName); - - mainLayout_->addLayout(headerLayout_); - - AvatarProvider::resolve(event.sender(), this); - } else { - generateBody(body); - setupSimpleLayout(); - } - - mainLayout_->addWidget(body_); -} - -// Only the body is displayed. -void -TimelineItem::generateBody(const QString &body) -{ - QString content(" %1 "); - - body_ = new QLabel(this); - body_->setFont(font_); - body_->setWordWrap(true); - body_->setText(content.arg(replaceEmoji(body))); - body_->setMargin(0); - - body_->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction); - body_->setOpenExternalLinks(true); -} - -// The username/timestamp is displayed along with the message body. -void -TimelineItem::generateBody(const QString &userid, const QString &body) -{ - auto sender = userid; - - if (userid.startsWith("@")) { - // TODO: Fix this by using a UserId type. - if (userid.split(":")[0].split("@").size() > 1) - sender = userid.split(":")[0].split("@")[1]; - } - - QFont usernameFont = font_; - usernameFont.setBold(true); - - userName_ = new QLabel(this); - userName_->setFont(usernameFont); - userName_->setText(sender); - - if (body.isEmpty()) - return; - - body_ = new QLabel(this); - body_->setFont(font_); - body_->setWordWrap(true); - body_->setText(QString(" %1 ").arg(replaceEmoji(body))); - body_->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction); - body_->setOpenExternalLinks(true); - body_->setMargin(0); -} - -void -TimelineItem::generateTimestamp(const QDateTime &time) -{ - QFont timestampFont; - timestampFont.setPixelSize(conf::timeline::fonts::timestamp); - - QFontMetrics fm(timestampFont); - int topMargin = QFontMetrics(font_).ascent() - fm.ascent(); - - timestamp_ = new QLabel(this); - timestamp_->setFont(timestampFont); - timestamp_->setText(time.toString("HH:mm")); - timestamp_->setContentsMargins(0, topMargin, 0, 0); - timestamp_->setStyleSheet( - QString("font-size: %1px;").arg(conf::timeline::fonts::timestamp)); -} - -QString -TimelineItem::replaceEmoji(const QString &body) -{ - QString fmtBody = ""; - - QVector utf32_string = body.toUcs4(); - - for (auto &code : utf32_string) { - // TODO: Be more precise here. - if (code > 9000) - fmtBody += QString("") - .arg(conf::emojiSize) + - QString::fromUcs4(&code, 1) + ""; - else - fmtBody += QString::fromUcs4(&code, 1); - } - - return fmtBody; -} - -void -TimelineItem::setupAvatarLayout(const QString &userName) -{ - topLayout_->setContentsMargins(conf::timeline::msgMargin, conf::timeline::msgMargin, 0, 0); - - userAvatar_ = new Avatar(this); - userAvatar_->setLetter(QChar(userName[0]).toUpper()); - userAvatar_->setSize(conf::timeline::avatarSize); - - // TODO: The provided user name should be a UserId class - if (userName[0] == '@' && userName.size() > 1) - userAvatar_->setLetter(QChar(userName[1]).toUpper()); - - sideLayout_->addWidget(userAvatar_); - sideLayout_->addStretch(1); - - headerLayout_->addWidget(userName_); - headerLayout_->addWidget(timestamp_, 1); -} - -void -TimelineItem::setupSimpleLayout() -{ - sideLayout_->addWidget(timestamp_); - - // Keep only the time in plain text. - QTextEdit htmlText(timestamp_->text()); - QString plainText = htmlText.toPlainText(); - - timestamp_->adjustSize(); - - // Align the end of the avatar bubble with the end of the timestamp for - // messages with and without avatar. Otherwise their bodies would not be - // aligned. - int tsWidth = timestamp_->fontMetrics().width(plainText); - int offset = std::max(0, conf::timeline::avatarSize - tsWidth); - - int defaultFontHeight = QFontMetrics(font_).ascent(); - - timestamp_->setAlignment(Qt::AlignTop); - timestamp_->setContentsMargins( - offset + 1, defaultFontHeight - timestamp_->fontMetrics().ascent(), 0, 0); - topLayout_->setContentsMargins( - conf::timeline::msgMargin, conf::timeline::msgMargin / 3, 0, 0); -} - -void -TimelineItem::setUserAvatar(const QImage &avatar) -{ - if (userAvatar_ == nullptr) - return; - - userAvatar_->setImage(avatar); -} - -QString -TimelineItem::descriptiveTime(const QDateTime &then) -{ - auto now = QDateTime::currentDateTime(); - - auto days = then.daysTo(now); - - if (days == 0) - return then.toString("HH:mm"); - else if (days < 2) - return QString("Yesterday"); - else if (days < 365) - return then.toString("dd/MM"); - - return then.toString("dd/MM/yy"); -} - -TimelineItem::~TimelineItem() {} - -void -TimelineItem::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} diff --git a/src/TimelineView.cc b/src/TimelineView.cc deleted file mode 100644 index 7bbda051..00000000 --- a/src/TimelineView.cc +++ /dev/null @@ -1,560 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#include -#include -#include - -#include "FileItem.h" -#include "FloatingButton.h" -#include "ImageItem.h" -#include "RoomMessages.h" -#include "ScrollBar.h" -#include "Sync.h" -#include "TimelineView.h" - -namespace events = matrix::events; -namespace msgs = matrix::events::messages; - -static bool -isRedactedEvent(const QJsonObject &event) -{ - if (event.contains("redacted_because")) - return true; - - if (event.contains("unsigned") && - event.value("unsigned").toObject().contains("redacted_because")) - return true; - - return false; -} - -TimelineView::TimelineView(const Timeline &timeline, - QSharedPointer client, - const QString &room_id, - QWidget *parent) - : QWidget(parent) - , room_id_{room_id} - , client_{client} -{ - init(); - addEvents(timeline); -} - -TimelineView::TimelineView(QSharedPointer client, - const QString &room_id, - QWidget *parent) - : QWidget(parent) - , room_id_{room_id} - , client_{client} -{ - init(); -} - -void -TimelineView::sliderRangeChanged(int min, int max) -{ - Q_UNUSED(min); - - if (!scroll_area_->verticalScrollBar()->isVisible()) { - scroll_area_->verticalScrollBar()->setValue(max); - return; - } - - // If the scrollbar is close to the bottom and a new message - // is added we move the scrollbar. - if (max - scroll_area_->verticalScrollBar()->value() < SCROLL_BAR_GAP) { - scroll_area_->verticalScrollBar()->setValue(max); - return; - } - - int currentHeight = scroll_widget_->size().height(); - int diff = currentHeight - oldHeight_; - int newPosition = oldPosition_ + diff; - - // Keep the scroll bar to the bottom if it hasn't been activated yet. - if (oldPosition_ == 0 && !scroll_area_->verticalScrollBar()->isVisible()) - newPosition = max; - - if (lastMessageDirection_ == TimelineDirection::Top) - scroll_area_->verticalScrollBar()->setValue(newPosition); -} - -void -TimelineView::fetchHistory() -{ - bool hasEnoughMessages = scroll_area_->verticalScrollBar()->isVisible(); - - if (!hasEnoughMessages && !isTimelineFinished) { - isPaginationInProgress_ = true; - client_->messages(room_id_, prev_batch_token_); - paginationTimer_->start(500); - return; - } - - paginationTimer_->stop(); -} - -void -TimelineView::scrollDown() -{ - int current = scroll_area_->verticalScrollBar()->value(); - int max = scroll_area_->verticalScrollBar()->maximum(); - - // The first time we enter the room move the scroll bar to the bottom. - if (!isInitialized) { - scroll_area_->verticalScrollBar()->setValue(max); - isInitialized = true; - return; - } - - // If the gap is small enough move the scroll bar down. e.g when a new - // message appears. - if (max - current < SCROLL_BAR_GAP) - scroll_area_->verticalScrollBar()->setValue(max); -} - -void -TimelineView::sliderMoved(int position) -{ - if (!scroll_area_->verticalScrollBar()->isVisible()) - return; - - const int maxScroll = scroll_area_->verticalScrollBar()->maximum(); - const int currentScroll = scroll_area_->verticalScrollBar()->value(); - - if (maxScroll - currentScroll > SCROLL_BAR_GAP) { - scrollDownBtn_->show(); - scrollDownBtn_->raise(); - } else { - scrollDownBtn_->hide(); - } - - // The scrollbar is high enough so we can start retrieving old events. - if (position < SCROLL_BAR_GAP) { - if (isTimelineFinished) - return; - - // Prevent user from moving up when there is pagination in - // progress. - // TODO: Keep a map of the event ids to filter out duplicates. - if (isPaginationInProgress_) - return; - - isPaginationInProgress_ = true; - - // FIXME: Maybe move this to TimelineViewManager to remove the - // extra calls? - client_->messages(room_id_, prev_batch_token_); - } -} - -void -TimelineView::addBackwardsEvents(const QString &room_id, const RoomMessages &msgs) -{ - if (room_id_ != room_id) - return; - - if (msgs.chunk().count() == 0) { - isTimelineFinished = true; - return; - } - - isTimelineFinished = false; - QList items; - - // Reset the sender of the first message in the timeline - // cause we're about to insert a new one. - firstSender_.clear(); - - // Parse in reverse order to determine where we should not show sender's - // name. - auto ii = msgs.chunk().size(); - while (ii != 0) { - --ii; - - TimelineItem *item = - parseMessageEvent(msgs.chunk().at(ii).toObject(), TimelineDirection::Top); - - if (item != nullptr) - items.push_back(item); - } - - // Reverse again to render them. - std::reverse(items.begin(), items.end()); - - oldPosition_ = scroll_area_->verticalScrollBar()->value(); - oldHeight_ = scroll_widget_->size().height(); - - for (const auto &item : items) - addTimelineItem(item, TimelineDirection::Top); - - lastMessageDirection_ = TimelineDirection::Top; - - QApplication::processEvents(); - - prev_batch_token_ = msgs.end(); - isPaginationInProgress_ = false; - - // Exclude the top stretch. - if (!msgs.chunk().isEmpty() && scroll_layout_->count() > 1) - notifyForLastEvent(); - - // If this batch is the first being rendered (i.e the first and the last - // events originate from this batch), set the last sender. - if (lastSender_.isEmpty() && !items.isEmpty()) - lastSender_ = items.constFirst()->descriptionMessage().userid; -} - -TimelineItem * -TimelineView::parseMessageEvent(const QJsonObject &event, TimelineDirection direction) -{ - events::EventType ty = events::extractEventType(event); - - if (ty == events::EventType::RoomMessage) { - events::MessageEventType msg_type = events::extractMessageEventType(event); - - using Emote = events::MessageEvent; - using File = events::MessageEvent; - using Image = events::MessageEvent; - using Notice = events::MessageEvent; - using Text = events::MessageEvent; - - if (msg_type == events::MessageEventType::Text) { - return processMessageEvent(event, direction); - } else if (msg_type == events::MessageEventType::Notice) { - return processMessageEvent(event, direction); - } else if (msg_type == events::MessageEventType::Image) { - return processMessageEvent(event, direction); - } else if (msg_type == events::MessageEventType::Emote) { - return processMessageEvent(event, direction); - } else if (msg_type == events::MessageEventType::File) { - return processMessageEvent(event, direction); - } else if (msg_type == events::MessageEventType::Unknown) { - // TODO Handle redacted messages. - // Silenced for now. - if (!isRedactedEvent(event)) - qWarning() << "Unknown message type" << event; - - return nullptr; - } - } - - return nullptr; -} - -int -TimelineView::addEvents(const Timeline &timeline) -{ - int message_count = 0; - - QSettings settings; - QString localUser = settings.value("auth/user_id").toString(); - - for (const auto &event : timeline.events()) { - TimelineItem *item = parseMessageEvent(event.toObject(), TimelineDirection::Bottom); - - if (item != nullptr) { - addTimelineItem(item, TimelineDirection::Bottom); - - if (localUser != event.toObject().value("sender").toString()) - message_count += 1; - } - } - - lastMessageDirection_ = TimelineDirection::Bottom; - - QApplication::processEvents(); - - if (isInitialSync) { - prev_batch_token_ = timeline.previousBatch(); - isInitialSync = false; - } - - // Exclude the top stretch. - if (!timeline.events().isEmpty() && scroll_layout_->count() > 1) - notifyForLastEvent(); - - if (isActiveWindow() && isVisible() && timeline.events().size() > 0) - readLastEvent(); - - return message_count; -} - -void -TimelineView::init() -{ - QSettings settings; - local_user_ = settings.value("auth/user_id").toString(); - - QIcon icon; - icon.addFile(":/icons/icons/ui/angle-arrow-down.png"); - scrollDownBtn_ = new FloatingButton(icon, this); - scrollDownBtn_->setBackgroundColor(QColor("#F5F5F5")); - scrollDownBtn_->setForegroundColor(QColor("black")); - scrollDownBtn_->hide(); - - connect(scrollDownBtn_, &QPushButton::clicked, this, [=]() { - const int max = scroll_area_->verticalScrollBar()->maximum(); - scroll_area_->verticalScrollBar()->setValue(max); - }); - top_layout_ = new QVBoxLayout(this); - top_layout_->setSpacing(0); - top_layout_->setMargin(0); - - scroll_area_ = new QScrollArea(this); - scroll_area_->setWidgetResizable(true); - scroll_area_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - - scrollbar_ = new ScrollBar(scroll_area_); - scroll_area_->setVerticalScrollBar(scrollbar_); - - scroll_widget_ = new QWidget(this); - - scroll_layout_ = new QVBoxLayout(scroll_widget_); - scroll_layout_->setContentsMargins(15, 0, 15, 15); - scroll_layout_->addStretch(1); - scroll_layout_->setSpacing(0); - scroll_layout_->setObjectName("timelinescrollarea"); - - scroll_area_->setWidget(scroll_widget_); - - top_layout_->addWidget(scroll_area_); - - setLayout(top_layout_); - - paginationTimer_ = new QTimer(this); - connect(paginationTimer_, &QTimer::timeout, this, &TimelineView::fetchHistory); - - connect(client_.data(), - &MatrixClient::messagesRetrieved, - this, - &TimelineView::addBackwardsEvents); - - connect(scroll_area_->verticalScrollBar(), - SIGNAL(valueChanged(int)), - this, - SLOT(sliderMoved(int))); - connect(scroll_area_->verticalScrollBar(), - SIGNAL(rangeChanged(int, int)), - this, - SLOT(sliderRangeChanged(int, int))); -} - -void -TimelineView::updateLastSender(const QString &user_id, TimelineDirection direction) -{ - if (direction == TimelineDirection::Bottom) - lastSender_ = user_id; - else - firstSender_ = user_id; -} - -bool -TimelineView::isSenderRendered(const QString &user_id, TimelineDirection direction) -{ - if (direction == TimelineDirection::Bottom) - return lastSender_ != user_id; - else - return firstSender_ != user_id; -} - -void -TimelineView::addTimelineItem(TimelineItem *item, TimelineDirection direction) -{ - if (direction == TimelineDirection::Bottom) - scroll_layout_->addWidget(item); - else - scroll_layout_->insertWidget(1, item); -} - -void -TimelineView::updatePendingMessage(int txn_id, QString event_id) -{ - if (pending_msgs_.head().txn_id == txn_id) { // We haven't received it yet - auto msg = pending_msgs_.dequeue(); - msg.event_id = event_id; - pending_sent_msgs_.append(msg); - } - sendNextPendingMessage(); -} - -void -TimelineView::addUserMessage(matrix::events::MessageEventType ty, const QString &body) -{ - QSettings settings; - auto user_id = settings.value("auth/user_id").toString(); - auto with_sender = lastSender_ != user_id; - - TimelineItem *view_item = new TimelineItem(ty, user_id, body, with_sender, scroll_widget_); - scroll_layout_->addWidget(view_item); - - lastMessageDirection_ = TimelineDirection::Bottom; - - QApplication::processEvents(); - - lastSender_ = user_id; - - int txn_id = client_->incrementTransactionId(); - PendingMessage message(ty, txn_id, body, "", "", view_item); - handleNewUserMessage(message); -} - -void -TimelineView::handleNewUserMessage(PendingMessage msg) -{ - pending_msgs_.enqueue(msg); - if (pending_msgs_.size() == 1 && pending_sent_msgs_.size() == 0) - sendNextPendingMessage(); -} - -void -TimelineView::sendNextPendingMessage() -{ - if (pending_msgs_.size() == 0) - return; - - PendingMessage &m = pending_msgs_.head(); - switch (m.ty) { - case matrix::events::MessageEventType::Image: - case matrix::events::MessageEventType::File: - client_->sendRoomMessage( - m.ty, m.txn_id, room_id_, QFileInfo(m.filename).fileName(), m.body); - break; - default: - client_->sendRoomMessage(m.ty, m.txn_id, room_id_, m.body); - break; - } -} - -void -TimelineView::notifyForLastEvent() -{ - auto lastItem = scroll_layout_->itemAt(scroll_layout_->count() - 1); - auto *lastTimelineItem = qobject_cast(lastItem->widget()); - - if (lastTimelineItem) - emit updateLastTimelineMessage(room_id_, lastTimelineItem->descriptionMessage()); - else - qWarning() << "Cast to TimelineView failed" << room_id_; -} - -bool -TimelineView::isPendingMessage(const QString &txnid, - const QString &sender, - const QString &local_userid) -{ - if (sender != local_userid) - return false; - - for (const auto &msg : pending_msgs_) { - if (QString::number(msg.txn_id) == txnid) - return true; - } - - for (const auto &msg : pending_sent_msgs_) { - if (QString::number(msg.txn_id) == txnid) - return true; - } - - return false; -} - -void -TimelineView::removePendingMessage(const QString &txnid) -{ - for (auto it = pending_sent_msgs_.begin(); it != pending_sent_msgs_.end(); ++it) { - if (QString::number(it->txn_id) == txnid) { - int index = std::distance(pending_sent_msgs_.begin(), it); - pending_sent_msgs_.removeAt(index); - return; - } - } - for (auto it = pending_msgs_.begin(); it != pending_msgs_.end(); ++it) { - if (QString::number(it->txn_id) == txnid) { - int index = std::distance(pending_msgs_.begin(), it); - pending_msgs_.removeAt(index); - return; - } - } -} - -void -TimelineView::handleFailedMessage(int txnid) -{ - Q_UNUSED(txnid); - // Note: We do this even if the message has already been echoed. - QTimer::singleShot(500, this, SLOT(sendNextPendingMessage())); -} - -void -TimelineView::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - -void -TimelineView::readLastEvent() const -{ - const auto eventId = getLastEventId(); - - if (!eventId.isEmpty()) - client_->readEvent(room_id_, eventId); -} - -QString -TimelineView::getLastEventId() const -{ - auto index = scroll_layout_->count(); - - // Search backwards for the first event that has a valid event id. - while (index > 0) { - --index; - - auto lastItem = scroll_layout_->itemAt(index); - auto *lastTimelineItem = qobject_cast(lastItem->widget()); - - if (lastTimelineItem && !lastTimelineItem->eventId().isEmpty()) - return lastTimelineItem->eventId(); - } - - return QString(""); -} - -void -TimelineView::showEvent(QShowEvent *event) -{ - readLastEvent(); - - QWidget::showEvent(event); -} - -bool -TimelineView::event(QEvent *event) -{ - if (event->type() == QEvent::WindowActivate) { - QTimer::singleShot(1000, this, [=]() { - emit clearUnreadMessageCount(room_id_); - readLastEvent(); - }); - } - - return QWidget::event(event); -} diff --git a/src/TimelineViewManager.cc b/src/TimelineViewManager.cc deleted file mode 100644 index daec481b..00000000 --- a/src/TimelineViewManager.cc +++ /dev/null @@ -1,300 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * 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 . - */ - -#include - -#include -#include -#include -#include - -#include "FileItem.h" -#include "ImageItem.h" -#include "MatrixClient.h" -#include "Sync.h" -#include "TimelineView.h" -#include "TimelineViewManager.h" - -TimelineViewManager::TimelineViewManager(QSharedPointer client, QWidget *parent) - : QStackedWidget(parent) - , client_(client) -{ - setStyleSheet("border: none;"); - - connect( - client_.data(), &MatrixClient::messageSent, this, &TimelineViewManager::messageSent); - - connect(client_.data(), - &MatrixClient::messageSendFailed, - this, - &TimelineViewManager::messageSendFailed); -} - -TimelineViewManager::~TimelineViewManager() {} - -void -TimelineViewManager::messageSent(const QString &event_id, const QString &roomid, int txn_id) -{ - // We save the latest valid transaction ID for later use. - QSettings settings; - settings.setValue("client/transaction_id", txn_id + 1); - - auto view = views_[roomid]; - view->updatePendingMessage(txn_id, event_id); -} - -void -TimelineViewManager::messageSendFailed(const QString &roomid, int txn_id) -{ - auto view = views_[roomid]; - view->handleFailedMessage(txn_id); -} - -void -TimelineViewManager::queueTextMessage(const QString &msg) -{ - auto room_id = active_room_; - auto view = views_[room_id]; - - view->addUserMessage(matrix::events::MessageEventType::Text, msg); -} - -void -TimelineViewManager::queueEmoteMessage(const QString &msg) -{ - auto room_id = active_room_; - auto view = views_[room_id]; - - view->addUserMessage(matrix::events::MessageEventType::Emote, msg); -} - -void -TimelineViewManager::queueImageMessage(const QString &roomid, - const QString &filename, - const QString &url) -{ - if (!views_.contains(roomid)) { - qDebug() << "Cannot send m.image message to a non-managed view"; - return; - } - - auto view = views_[roomid]; - - view->addUserMessage(url, filename); -} - -void -TimelineViewManager::queueFileMessage(const QString &roomid, - const QString &filename, - const QString &url) -{ - if (!views_.contains(roomid)) { - qDebug() << "Cannot send m.file message to a non-managed view"; - return; - } - - auto view = views_[roomid]; - - view->addUserMessage(url, filename); -} - -void -TimelineViewManager::clearAll() -{ - for (auto view : views_) - removeWidget(view.data()); - - views_.clear(); -} - -void -TimelineViewManager::initialize(const Rooms &rooms) -{ - for (auto it = rooms.join().constBegin(); it != rooms.join().constEnd(); ++it) { - addRoom(it.value(), it.key()); - } -} - -void -TimelineViewManager::initialize(const QList &rooms) -{ - for (const auto &roomid : rooms) { - addRoom(roomid); - } -} - -void -TimelineViewManager::addRoom(const JoinedRoom &room, const QString &room_id) -{ - // Create a history view with the room events. - TimelineView *view = new TimelineView(room.timeline(), client_, room_id); - views_.insert(room_id, QSharedPointer(view)); - - connect(view, - &TimelineView::updateLastTimelineMessage, - this, - &TimelineViewManager::updateRoomsLastMessage); - connect(view, - &TimelineView::clearUnreadMessageCount, - this, - &TimelineViewManager::clearRoomMessageCount); - - // Add the view in the widget stack. - addWidget(view); -} - -void -TimelineViewManager::addRoom(const QString &room_id) -{ - // Create a history view without any events. - TimelineView *view = new TimelineView(client_, room_id); - views_.insert(room_id, QSharedPointer(view)); - - connect(view, - &TimelineView::updateLastTimelineMessage, - this, - &TimelineViewManager::updateRoomsLastMessage); - connect(view, - &TimelineView::clearUnreadMessageCount, - this, - &TimelineViewManager::clearRoomMessageCount); - - // Add the view in the widget stack. - addWidget(view); -} - -void -TimelineViewManager::sync(const Rooms &rooms) -{ - for (auto it = rooms.join().constBegin(); it != rooms.join().constEnd(); ++it) { - auto roomid = it.key(); - - if (!views_.contains(roomid)) { - qDebug() << "Ignoring event from unknown room" << roomid; - continue; - } - - auto view = views_.value(roomid); - - int msgs_added = view->addEvents(it.value().timeline()); - - if (msgs_added > 0) { - // TODO: When the app window gets active the current - // unread count (if any) should be cleared. - auto isAppActive = QApplication::activeWindow() != nullptr; - - if (roomid != active_room_ || !isAppActive) - emit unreadMessages(roomid, msgs_added); - } - } -} - -void -TimelineViewManager::setHistoryView(const QString &room_id) -{ - if (!views_.contains(room_id)) { - qDebug() << "Room ID from RoomList is not present in ViewManager" << room_id; - return; - } - - active_room_ = room_id; - auto view = views_.value(room_id); - - setCurrentWidget(view.data()); - - view->fetchHistory(); - view->scrollDown(); -} - -QMap TimelineViewManager::DISPLAY_NAMES; - -QString -TimelineViewManager::chooseRandomColor() -{ - std::random_device random_device; - std::mt19937 engine{random_device()}; - std::uniform_real_distribution dist(0, 1); - - float hue = dist(engine); - float saturation = 0.9; - float value = 0.7; - - int hue_i = hue * 6; - - float f = hue * 6 - hue_i; - - float p = value * (1 - saturation); - float q = value * (1 - f * saturation); - float t = value * (1 - (1 - f) * saturation); - - float r = 0; - float g = 0; - float b = 0; - - if (hue_i == 0) { - r = value; - g = t; - b = p; - } else if (hue_i == 1) { - r = q; - g = value; - b = p; - } else if (hue_i == 2) { - r = p; - g = value; - b = t; - } else if (hue_i == 3) { - r = p; - g = q; - b = value; - } else if (hue_i == 4) { - r = t; - g = p; - b = value; - } else if (hue_i == 5) { - r = value; - g = p; - b = q; - } - - int ri = r * 256; - int gi = g * 256; - int bi = b * 256; - - QColor color(ri, gi, bi); - - return color.name(); -} - -QString -TimelineViewManager::displayName(const QString &userid) -{ - if (DISPLAY_NAMES.contains(userid)) - return DISPLAY_NAMES.value(userid); - - return userid; -} - -bool -TimelineViewManager::hasLoaded() const -{ - for (const auto &view : views_) - if (!view->hasLoaded()) - return false; - - return true; -} diff --git a/src/TopRoomBar.cc b/src/TopRoomBar.cc index d852ae32..3418e6f1 100644 --- a/src/TopRoomBar.cc +++ b/src/TopRoomBar.cc @@ -21,13 +21,14 @@ #include "Config.h" #include "FlatButton.h" #include "Label.h" -#include "LeaveRoomDialog.h" #include "MainWindow.h" #include "Menu.h" #include "OverlayModal.h" #include "RoomSettings.h" #include "TopRoomBar.h" +#include "dialogs/LeaveRoomDialog.h" + TopRoomBar::TopRoomBar(QWidget *parent) : QWidget(parent) , buttonSize_{32} diff --git a/src/UserInfoWidget.cc b/src/UserInfoWidget.cc index 3dc1bc89..98977bb3 100644 --- a/src/UserInfoWidget.cc +++ b/src/UserInfoWidget.cc @@ -20,11 +20,12 @@ #include "Avatar.h" #include "Config.h" #include "FlatButton.h" -#include "LogoutDialog.h" #include "MainWindow.h" #include "OverlayModal.h" #include "UserInfoWidget.h" +#include "dialogs/LogoutDialog.h" + UserInfoWidget::UserInfoWidget(QWidget *parent) : QWidget(parent) , display_name_("User") diff --git a/src/dialogs/ImageOverlayDialog.cc b/src/dialogs/ImageOverlayDialog.cc new file mode 100644 index 00000000..ad87d144 --- /dev/null +++ b/src/dialogs/ImageOverlayDialog.cc @@ -0,0 +1,129 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#include +#include +#include + +#include "dialogs/ImageOverlayDialog.h" + +ImageOverlayDialog::ImageOverlayDialog(QPixmap image, QWidget *parent) + : QWidget{parent} + , originalImage_{image} +{ + setMouseTracking(true); + setParent(0); + + setWindowFlags(windowFlags() | Qt::FramelessWindowHint); + + setAttribute(Qt::WA_NoSystemBackground, true); + setAttribute(Qt::WA_TranslucentBackground, true); + setAttribute(Qt::WA_DeleteOnClose, true); + setWindowState(Qt::WindowFullScreen); + + screen_ = QApplication::desktop()->availableGeometry(); + + move(QApplication::desktop()->mapToGlobal(screen_.topLeft())); + resize(screen_.size()); + + connect(this, SIGNAL(closing()), this, SLOT(close())); + + raise(); +} + +// TODO: Move this into Utils +void +ImageOverlayDialog::scaleImage(int max_width, int max_height) +{ + if (originalImage_.isNull()) + return; + + auto width_ratio = (double)max_width / (double)originalImage_.width(); + auto height_ratio = (double)max_height / (double)originalImage_.height(); + + auto min_aspect_ratio = std::min(width_ratio, height_ratio); + + int final_width = 0; + int final_height = 0; + + if (min_aspect_ratio > 1) { + final_width = originalImage_.width(); + final_height = originalImage_.height(); + } else { + final_width = originalImage_.width() * min_aspect_ratio; + final_height = originalImage_.height() * min_aspect_ratio; + } + + image_ = originalImage_.scaled( + final_width, final_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); +} + +void +ImageOverlayDialog::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + // Full screen overlay. + painter.fillRect(QRect(0, 0, screen_.width(), screen_.height()), QColor(55, 55, 55, 170)); + + // Left and Right margins + int outer_margin = screen_.width() * 0.12; + int buttonSize = 36; + int margin = outer_margin * 0.1; + + int max_width = screen_.width() - 2 * outer_margin; + int max_height = screen_.height(); + + scaleImage(max_width, max_height); + + int diff_x = max_width - image_.width(); + int diff_y = max_height - image_.height(); + + content_ = QRect(outer_margin + diff_x / 2, diff_y / 2, image_.width(), image_.height()); + close_button_ = + QRect(screen_.width() - margin - buttonSize, margin, buttonSize, buttonSize); + + // Draw main content_. + painter.drawPixmap(content_, image_); + + // Draw top right corner X. + QPen pen; + pen.setCapStyle(Qt::RoundCap); + pen.setWidthF(5); + pen.setColor("gray"); + + auto center = close_button_.center(); + + painter.setPen(pen); + painter.drawLine(center - QPointF(15, 15), center + QPointF(15, 15)); + painter.drawLine(center + QPointF(15, -15), center - QPointF(15, -15)); +} + +void +ImageOverlayDialog::mousePressEvent(QMouseEvent *event) +{ + if (event->button() != Qt::LeftButton) + return; + + if (close_button_.contains(event->pos())) + emit closing(); + else if (!content_.contains(event->pos())) + emit closing(); +} diff --git a/src/dialogs/JoinRoomDialog.cc b/src/dialogs/JoinRoomDialog.cc new file mode 100644 index 00000000..2cee7ef6 --- /dev/null +++ b/src/dialogs/JoinRoomDialog.cc @@ -0,0 +1,49 @@ +#include +#include + +#include "Config.h" +#include "FlatButton.h" +#include "Theme.h" + +#include "dialogs/JoinRoomDialog.h" + +JoinRoomDialog::JoinRoomDialog(QWidget *parent) + : QFrame(parent) +{ + setMaximumSize(400, 400); + + auto layout = new QVBoxLayout(this); + layout->setSpacing(30); + layout->setMargin(20); + + auto buttonLayout = new QHBoxLayout(); + buttonLayout->setSpacing(0); + buttonLayout->setMargin(0); + + confirmBtn_ = new FlatButton("JOIN", this); + confirmBtn_->setFontSize(conf::btn::fontSize); + + cancelBtn_ = new FlatButton(tr("CANCEL"), this); + cancelBtn_->setFontSize(conf::btn::fontSize); + + buttonLayout->addStretch(1); + buttonLayout->addWidget(confirmBtn_); + buttonLayout->addWidget(cancelBtn_); + + QFont font; + font.setPixelSize(conf::headerFontSize); + + auto label = new QLabel(tr("Room alias to join:"), this); + label->setFont(font); + + roomAliasEdit_ = new QLineEdit(this); + + layout->addWidget(label); + layout->addWidget(roomAliasEdit_); + layout->addLayout(buttonLayout); + + connect(confirmBtn_, &QPushButton::clicked, [=]() { + emit closing(true, roomAliasEdit_->text()); + }); + connect(cancelBtn_, &QPushButton::clicked, [=]() { emit closing(false, nullptr); }); +} diff --git a/src/dialogs/LeaveRoomDialog.cc b/src/dialogs/LeaveRoomDialog.cc new file mode 100644 index 00000000..b0d1679f --- /dev/null +++ b/src/dialogs/LeaveRoomDialog.cc @@ -0,0 +1,54 @@ +#include +#include +#include + +#include "Config.h" +#include "FlatButton.h" +#include "Theme.h" + +#include "dialogs/LeaveRoomDialog.h" + +LeaveRoomDialog::LeaveRoomDialog(QWidget *parent) + : QFrame(parent) +{ + setMaximumSize(400, 400); + + auto layout = new QVBoxLayout(this); + layout->setSpacing(30); + layout->setMargin(20); + + auto buttonLayout = new QHBoxLayout(); + buttonLayout->setSpacing(0); + buttonLayout->setMargin(0); + + confirmBtn_ = new FlatButton("LEAVE", this); + confirmBtn_->setFontSize(conf::btn::fontSize); + + cancelBtn_ = new FlatButton(tr("CANCEL"), this); + cancelBtn_->setFontSize(conf::btn::fontSize); + + buttonLayout->addStretch(1); + buttonLayout->addWidget(confirmBtn_); + buttonLayout->addWidget(cancelBtn_); + + QFont font; + font.setPixelSize(conf::headerFontSize); + + auto label = new QLabel(tr("Are you sure you want to leave?"), this); + label->setFont(font); + + layout->addWidget(label); + layout->addLayout(buttonLayout); + + connect(confirmBtn_, &QPushButton::clicked, [=]() { emit closing(true); }); + connect(cancelBtn_, &QPushButton::clicked, [=]() { emit closing(false); }); +} + +void +LeaveRoomDialog::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} diff --git a/src/dialogs/LogoutDialog.cc b/src/dialogs/LogoutDialog.cc new file mode 100644 index 00000000..73e09745 --- /dev/null +++ b/src/dialogs/LogoutDialog.cc @@ -0,0 +1,72 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#include +#include +#include +#include + +#include "Config.h" +#include "FlatButton.h" +#include "Theme.h" + +#include "dialogs/LogoutDialog.h" + +LogoutDialog::LogoutDialog(QWidget *parent) + : QFrame(parent) +{ + setMaximumSize(400, 400); + + auto layout = new QVBoxLayout(this); + layout->setSpacing(30); + layout->setMargin(20); + + auto buttonLayout = new QHBoxLayout(); + buttonLayout->setSpacing(0); + buttonLayout->setMargin(0); + + confirmBtn_ = new FlatButton("OK", this); + confirmBtn_->setFontSize(conf::btn::fontSize); + + cancelBtn_ = new FlatButton(tr("CANCEL"), this); + cancelBtn_->setFontSize(conf::btn::fontSize); + + buttonLayout->addStretch(1); + buttonLayout->addWidget(confirmBtn_); + buttonLayout->addWidget(cancelBtn_); + + QFont font; + font.setPixelSize(conf::headerFontSize); + + auto label = new QLabel(tr("Logout. Are you sure?"), this); + label->setFont(font); + + layout->addWidget(label); + layout->addLayout(buttonLayout); + + connect(confirmBtn_, &QPushButton::clicked, [=]() { emit closing(true); }); + connect(cancelBtn_, &QPushButton::clicked, [=]() { emit closing(false); }); +} + +void +LogoutDialog::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} diff --git a/src/emoji/EmojiCategory.cc b/src/emoji/EmojiCategory.cc new file mode 100644 index 00000000..42f09409 --- /dev/null +++ b/src/emoji/EmojiCategory.cc @@ -0,0 +1,90 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#include +#include +#include + +#include "Config.h" + +#include "emoji/EmojiCategory.h" + +EmojiCategory::EmojiCategory(QString category, QList emoji, QWidget *parent) + : QWidget(parent) +{ + mainLayout_ = new QVBoxLayout(this); + mainLayout_->setMargin(0); + mainLayout_->setSpacing(0); + + emojiListView_ = new QListView(); + itemModel_ = new QStandardItemModel(this); + + delegate_ = new EmojiItemDelegate(this); + data_ = new Emoji; + + emojiListView_->setItemDelegate(delegate_); + emojiListView_->setModel(itemModel_); + emojiListView_->setViewMode(QListView::IconMode); + emojiListView_->setFlow(QListView::LeftToRight); + emojiListView_->setResizeMode(QListView::Adjust); + emojiListView_->verticalScrollBar()->setEnabled(false); + emojiListView_->horizontalScrollBar()->setEnabled(false); + + const int cols = 7; + const int rows = emoji.size() / 7; + + // TODO: Be precise here. Take the parent into consideration. + emojiListView_->setFixedSize(cols * 50 + 20, rows * 50 + 20); + emojiListView_->setGridSize(QSize(50, 50)); + emojiListView_->setDragEnabled(false); + emojiListView_->setEditTriggers(QAbstractItemView::NoEditTriggers); + + for (const auto &e : emoji) { + data_->unicode = e.unicode; + + auto item = new QStandardItem; + item->setSizeHint(QSize(24, 24)); + + QVariant unicode(data_->unicode); + item->setData(unicode.toString(), Qt::UserRole); + + itemModel_->appendRow(item); + } + + QFont font("Open Sans SemiBold"); + font.setPixelSize(conf::fontSize); + + category_ = new QLabel(category, this); + category_->setFont(font); + category_->setStyleSheet("margin: 20px 0 20px 8px;"); + + mainLayout_->addWidget(category_); + mainLayout_->addWidget(emojiListView_); + + connect(emojiListView_, &QListView::clicked, this, &EmojiCategory::clickIndex); +} + +void +EmojiCategory::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + +EmojiCategory::~EmojiCategory() {} diff --git a/src/emoji/EmojiItemDelegate.cc b/src/emoji/EmojiItemDelegate.cc new file mode 100644 index 00000000..547d3b4c --- /dev/null +++ b/src/emoji/EmojiItemDelegate.cc @@ -0,0 +1,47 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#include +#include + +#include "emoji/EmojiItemDelegate.h" + +EmojiItemDelegate::EmojiItemDelegate(QObject *parent) + : QStyledItemDelegate(parent) +{ + data_ = new Emoji; +} + +EmojiItemDelegate::~EmojiItemDelegate() { delete data_; } + +void +EmojiItemDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + Q_UNUSED(index); + + QStyleOptionViewItem viewOption(option); + + auto emoji = index.data(Qt::UserRole).toString(); + + QFont font("Emoji One"); + font.setPixelSize(19); + + painter->setFont(font); + painter->drawText(viewOption.rect, Qt::AlignCenter, emoji); +} diff --git a/src/emoji/EmojiPanel.cc b/src/emoji/EmojiPanel.cc new file mode 100644 index 00000000..9df6f193 --- /dev/null +++ b/src/emoji/EmojiPanel.cc @@ -0,0 +1,245 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#include +#include +#include + +#include "DropShadow.h" +#include "FlatButton.h" + +#include "emoji/EmojiCategory.h" +#include "emoji/EmojiPanel.h" + +EmojiPanel::EmojiPanel(QWidget *parent) + : QWidget(parent) + , shadowMargin_{2} + , width_{370} + , height_{350} + , categoryIconSize_{20} +{ + setStyleSheet("QWidget {border: none;}" + "QScrollBar:vertical { width: 0px; margin: 0px; }" + "QScrollBar::handle:vertical { min-height: 30px; }"); + + setAttribute(Qt::WA_TranslucentBackground, true); + setAttribute(Qt::WA_ShowWithoutActivating, true); + setWindowFlags(Qt::FramelessWindowHint | Qt::ToolTip); + + auto mainWidget = new QWidget(this); + mainWidget->setMaximumSize(width_, height_); + + auto topLayout = new QVBoxLayout(this); + topLayout->addWidget(mainWidget); + topLayout->setMargin(shadowMargin_); + topLayout->setSpacing(0); + + auto contentLayout = new QVBoxLayout(mainWidget); + contentLayout->setMargin(0); + contentLayout->setSpacing(0); + + auto emojiCategories = new QFrame(mainWidget); + emojiCategories->setStyleSheet( + QString("background-color: %1") + .arg(palette().color(QPalette::Window).darker(110).name())); + + auto categoriesLayout = new QHBoxLayout(emojiCategories); + categoriesLayout->setSpacing(0); + categoriesLayout->setMargin(0); + + QIcon icon; + + auto peopleCategory = new FlatButton(emojiCategories); + icon.addFile(":/icons/icons/emoji-categories/people.png"); + peopleCategory->setIcon(icon); + peopleCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); + + auto natureCategory_ = new FlatButton(emojiCategories); + icon.addFile(":/icons/icons/emoji-categories/nature.png"); + natureCategory_->setIcon(icon); + natureCategory_->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); + + auto foodCategory_ = new FlatButton(emojiCategories); + icon.addFile(":/icons/icons/emoji-categories/foods.png"); + foodCategory_->setIcon(icon); + foodCategory_->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); + + auto activityCategory = new FlatButton(emojiCategories); + icon.addFile(":/icons/icons/emoji-categories/activity.png"); + activityCategory->setIcon(icon); + activityCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); + + auto travelCategory = new FlatButton(emojiCategories); + icon.addFile(":/icons/icons/emoji-categories/travel.png"); + travelCategory->setIcon(icon); + travelCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); + + auto objectsCategory = new FlatButton(emojiCategories); + icon.addFile(":/icons/icons/emoji-categories/objects.png"); + objectsCategory->setIcon(icon); + objectsCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); + + auto symbolsCategory = new FlatButton(emojiCategories); + icon.addFile(":/icons/icons/emoji-categories/symbols.png"); + symbolsCategory->setIcon(icon); + symbolsCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); + + auto flagsCategory = new FlatButton(emojiCategories); + icon.addFile(":/icons/icons/emoji-categories/flags.png"); + flagsCategory->setIcon(icon); + flagsCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); + + categoriesLayout->addWidget(peopleCategory); + categoriesLayout->addWidget(natureCategory_); + categoriesLayout->addWidget(foodCategory_); + categoriesLayout->addWidget(activityCategory); + categoriesLayout->addWidget(travelCategory); + categoriesLayout->addWidget(objectsCategory); + categoriesLayout->addWidget(symbolsCategory); + categoriesLayout->addWidget(flagsCategory); + + scrollArea_ = new QScrollArea(this); + scrollArea_->setWidgetResizable(true); + scrollArea_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + auto scrollWidget = new QWidget(this); + auto scrollLayout = new QVBoxLayout(scrollWidget); + + scrollLayout->setMargin(0); + scrollLayout->setSpacing(0); + scrollArea_->setWidget(scrollWidget); + + auto peopleEmoji = + new EmojiCategory(tr("Smileys & People"), emoji_provider_.people, scrollWidget); + scrollLayout->addWidget(peopleEmoji); + + auto natureEmoji = + new EmojiCategory(tr("Animals & Nature"), emoji_provider_.nature, scrollWidget); + scrollLayout->addWidget(natureEmoji); + + auto foodEmoji = new EmojiCategory(tr("Food & Drink"), emoji_provider_.food, scrollWidget); + scrollLayout->addWidget(foodEmoji); + + auto activityEmoji = + new EmojiCategory(tr("Activity"), emoji_provider_.activity, scrollWidget); + scrollLayout->addWidget(activityEmoji); + + auto travelEmoji = + new EmojiCategory(tr("Travel & Places"), emoji_provider_.travel, scrollWidget); + scrollLayout->addWidget(travelEmoji); + + auto objectsEmoji = new EmojiCategory(tr("Objects"), emoji_provider_.objects, scrollWidget); + scrollLayout->addWidget(objectsEmoji); + + auto symbolsEmoji = new EmojiCategory(tr("Symbols"), emoji_provider_.symbols, scrollWidget); + scrollLayout->addWidget(symbolsEmoji); + + auto flagsEmoji = new EmojiCategory(tr("Flags"), emoji_provider_.flags, scrollWidget); + scrollLayout->addWidget(flagsEmoji); + + contentLayout->addWidget(scrollArea_); + contentLayout->addWidget(emojiCategories); + + connect(peopleEmoji, &EmojiCategory::emojiSelected, this, &EmojiPanel::emojiSelected); + connect(peopleCategory, &QPushButton::clicked, [this, peopleEmoji]() { + this->showEmojiCategory(peopleEmoji); + }); + + connect(natureEmoji, &EmojiCategory::emojiSelected, this, &EmojiPanel::emojiSelected); + connect(natureCategory_, &QPushButton::clicked, [this, natureEmoji]() { + this->showEmojiCategory(natureEmoji); + }); + + connect(foodEmoji, &EmojiCategory::emojiSelected, this, &EmojiPanel::emojiSelected); + connect(foodCategory_, &QPushButton::clicked, [this, foodEmoji]() { + this->showEmojiCategory(foodEmoji); + }); + + connect(activityEmoji, &EmojiCategory::emojiSelected, this, &EmojiPanel::emojiSelected); + connect(activityCategory, &QPushButton::clicked, [this, activityEmoji]() { + this->showEmojiCategory(activityEmoji); + }); + + connect(travelEmoji, &EmojiCategory::emojiSelected, this, &EmojiPanel::emojiSelected); + connect(travelCategory, &QPushButton::clicked, [this, travelEmoji]() { + this->showEmojiCategory(travelEmoji); + }); + + connect(objectsEmoji, &EmojiCategory::emojiSelected, this, &EmojiPanel::emojiSelected); + connect(objectsCategory, &QPushButton::clicked, [this, objectsEmoji]() { + this->showEmojiCategory(objectsEmoji); + }); + + connect(symbolsEmoji, &EmojiCategory::emojiSelected, this, &EmojiPanel::emojiSelected); + connect(symbolsCategory, &QPushButton::clicked, [this, symbolsEmoji]() { + this->showEmojiCategory(symbolsEmoji); + }); + + connect(flagsEmoji, &EmojiCategory::emojiSelected, this, &EmojiPanel::emojiSelected); + connect(flagsCategory, &QPushButton::clicked, [this, flagsEmoji]() { + this->showEmojiCategory(flagsEmoji); + }); +} + +void +EmojiPanel::showEmojiCategory(const EmojiCategory *category) +{ + auto posToGo = category->mapToParent(QPoint()).y(); + auto current = scrollArea_->verticalScrollBar()->value(); + + if (current == posToGo) + return; + + // HACK + // If we want to go to a previous category and position the label at the top + // the 6*50 offset won't work because not all the categories have the same + // height. To ensure the category is at the top, we move to the top and go as + // normal to the next category. + if (current > posToGo) + this->scrollArea_->ensureVisible(0, 0, 0, 0); + + posToGo += 6 * 50; + this->scrollArea_->ensureVisible(0, posToGo, 0, 0); +} + +void +EmojiPanel::leaveEvent(QEvent *) +{ + hide(); +} + +void +EmojiPanel::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + + DropShadow::draw(p, + shadowMargin_, + 4.0, + QColor(120, 120, 120, 92), + QColor(255, 255, 255, 0), + 0.0, + 1.0, + 0.6, + width(), + height()); +} diff --git a/src/emoji/EmojiPickButton.cc b/src/emoji/EmojiPickButton.cc new file mode 100644 index 00000000..44955a8c --- /dev/null +++ b/src/emoji/EmojiPickButton.cc @@ -0,0 +1,65 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#include "emoji/EmojiPickButton.h" +#include "emoji/EmojiPanel.h" + +EmojiPickButton::EmojiPickButton(QWidget *parent) + : FlatButton(parent) + , panel_{nullptr} +{} + +void +EmojiPickButton::enterEvent(QEvent *e) +{ + Q_UNUSED(e); + + if (panel_.isNull()) { + panel_ = QSharedPointer(new EmojiPanel(this)); + connect( + panel_.data(), &EmojiPanel::emojiSelected, this, &EmojiPickButton::emojiSelected); + } + + QPoint pos(rect().x(), rect().y()); + pos = this->mapToGlobal(pos); + + auto panel_size = panel_->sizeHint(); + + auto x = pos.x() - panel_size.width() + horizontal_distance_; + auto y = pos.y() - panel_size.height() - vertical_distance_; + + panel_->move(x, y); + panel_->show(); +} + +void +EmojiPickButton::leaveEvent(QEvent *e) +{ + Q_UNUSED(e); + + if (panel_->underMouse()) + return; + + auto pos = QCursor::pos(); + auto panel_geometry = panel_->geometry(); + panel_geometry.adjust(0, 0, 0, vertical_distance_); + + if (panel_geometry.contains(pos)) + return; + + panel_->hide(); +} diff --git a/src/emoji/EmojiProvider.cc b/src/emoji/EmojiProvider.cc new file mode 100644 index 00000000..838c0340 --- /dev/null +++ b/src/emoji/EmojiProvider.cc @@ -0,0 +1,1465 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#include + +#include "emoji/EmojiProvider.h" + +const QList EmojiProvider::people = { + Emoji{QString::fromUtf8("\xf0\x9f\x98\x80"), ":grinning:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x81"), ":grin:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x82"), ":joy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa3"), ":rofl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x83"), ":smiley:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x84"), ":smile:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x85"), ":sweat_smile:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x86"), ":laughing:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x89"), ":wink:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x8a"), ":blush:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x8b"), ":yum:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x8e"), ":sunglasses:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x8d"), ":heart_eyes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x98"), ":kissing_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x97"), ":kissing:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x99"), ":kissing_smiling_eyes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x9a"), ":kissing_closed_eyes:"}, + Emoji{QString::fromUtf8("\xe2\x98\xba"), ":relaxed:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x82"), ":slight_smile:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x97"), ":hugging:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x94"), ":thinking:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x90"), ":neutral_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x91"), ":expressionless:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb6"), ":no_mouth:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x84"), ":rolling_eyes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x8f"), ":smirk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa3"), ":persevere:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa5"), ":disappointed_relieved:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xae"), ":open_mouth:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x90"), ":zipper_mouth:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xaf"), ":hushed:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xaa"), ":sleepy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xab"), ":tired_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb4"), ":sleeping:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x8c"), ":relieved:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x93"), ":nerd:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x9b"), ":stuck_out_tongue:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x9c"), ":stuck_out_tongue_winking_eye:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x9d"), ":stuck_out_tongue_closed_eyes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa4"), ":drooling_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x92"), ":unamused:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x93"), ":sweat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x94"), ":pensive:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x95"), ":confused:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x83"), ":upside_down:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x91"), ":money_mouth:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb2"), ":astonished:"}, + Emoji{QString::fromUtf8("\xe2\x98\xb9"), ":frowning2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x81"), ":slight_frown:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x96"), ":confounded:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x9e"), ":disappointed:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x9f"), ":worried:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa4"), ":triumph:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa2"), ":cry:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xad"), ":sob:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa6"), ":frowning:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa7"), ":anguished:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa8"), ":fearful:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa9"), ":weary:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xac"), ":grimacing:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb0"), ":cold_sweat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb1"), ":scream:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb3"), ":flushed:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb5"), ":dizzy_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa1"), ":rage:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa0"), ":angry:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x87"), ":innocent:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa0"), ":cowboy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa1"), ":clown:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa5"), ":lying_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb7"), ":mask:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x92"), ":thermometer_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x95"), ":head_bandage:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa2"), ":nauseated_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa7"), ":sneezing_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x88"), ":smiling_imp:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xbf"), ":imp:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb9"), ":japanese_ogre:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xba"), ":japanese_goblin:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x80"), ":skull:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xbb"), ":ghost:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xbd"), ":alien:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x96"), ":robot:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa9"), ":poop:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xba"), ":smiley_cat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb8"), ":smile_cat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb9"), ":joy_cat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xbb"), ":heart_eyes_cat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xbc"), ":smirk_cat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xbd"), ":kissing_cat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x80"), ":scream_cat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xbf"), ":crying_cat_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xbe"), ":pouting_cat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa6"), ":boy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa7"), ":girl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa8"), ":man:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa9"), ":woman:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb4"), ":older_man:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb5"), ":older_woman:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb6"), ":baby:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xbc"), ":angel:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xae"), ":cop:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xb5"), ":spy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x82"), ":guardsman:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb7"), ":construction_worker:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb3"), ":man_with_turban:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb1"), ":person_with_blond_hair:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x85"), ":santa:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb6"), ":mrs_claus:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb8"), ":princess:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb4"), ":prince:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb0"), ":bride_with_veil:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb5"), ":man_in_tuxedo:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb0"), ":pregnant_woman:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb2"), ":man_with_gua_pi_mao:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x8d"), ":person_frowning:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x8e"), ":person_with_pouting_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x85"), ":no_good:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x86"), ":ok_woman:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x81"), ":information_desk_person:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x8b"), ":raising_hand:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x87"), ":bow:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa6"), ":face_palm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb7"), ":shrug:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x86"), ":massage:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x87"), ":haircut:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb6"), ":walking:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x83"), ":runner:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x83"), ":dancer:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xba"), ":man_dancing:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xaf"), ":dancers:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xa3"), ":speaking_head:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa4"), ":bust_in_silhouette:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa5"), ":busts_in_silhouette:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xab"), ":couple:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xac"), ":two_men_holding_hands:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xad"), ":two_women_holding_hands:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x8f"), ":couplekiss:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x91"), ":couple_with_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xaa"), ":family:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xaa"), ":muscle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb3"), ":selfie:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x88"), ":point_left:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x89"), ":point_right:"}, + Emoji{QString::fromUtf8("\xe2\x98\x9d"), ":point_up:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x86"), ":point_up_2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\x95"), ":middle_finger:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x87"), ":point_down:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x8c"), ":v:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x9e"), ":fingers_crossed:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\x96"), ":vulcan:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x98"), ":metal:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x99"), ":call_me:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\x90"), ":hand_splayed:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x8b"), ":raised_hand:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x8c"), ":ok_hand:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x8d"), ":thumbsup:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x8e"), ":thumbsdown:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x8a"), ":fist:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x8a"), ":punch:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x9b"), ":left_facing_fist:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x9c"), ":right_facing_fist:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x9a"), ":raised_back_of_hand:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x8b"), ":wave:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x8f"), ":clap:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x8d"), ":writing_hand:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x90"), ":open_hands:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x8c"), ":raised_hands:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x8f"), ":pray:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x9d"), ":handshake:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x85"), ":nail_care:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x82"), ":ear:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x83"), ":nose:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa3"), ":footprints:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x80"), ":eyes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x81"), ":eye:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x85"), ":tongue:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x84"), ":lips:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x8b"), ":kiss:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa4"), ":zzz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x93"), ":eyeglasses:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xb6"), ":dark_sunglasses:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x94"), ":necktie:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x95"), ":shirt:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x96"), ":jeans:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x97"), ":dress:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x98"), ":kimono:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x99"), ":bikini:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x9a"), ":womans_clothes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x9b"), ":purse:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x9c"), ":handbag:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x9d"), ":pouch:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x92"), ":school_satchel:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x9e"), ":mans_shoe:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x9f"), ":athletic_shoe:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa0"), ":high_heel:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa1"), ":sandal:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa2"), ":boot:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x91"), ":crown:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x92"), ":womans_hat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa9"), ":tophat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x93"), ":mortar_board:"}, + Emoji{QString::fromUtf8("\xe2\x9b\x91"), ":helmet_with_cross:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x84"), ":lipstick:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x8d"), ":ring:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x82"), ":closed_umbrella:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xbc"), ":briefcase:"}, +}; + +const QList EmojiProvider::nature = { + Emoji{QString::fromUtf8("\xf0\x9f\x99\x88"), ":see_no_evil:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x89"), ":hear_no_evil:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x8a"), ":speak_no_evil:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa6"), ":sweat_drops:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa8"), ":dash:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb5"), ":monkey_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x92"), ":monkey:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x8d"), ":gorilla:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb6"), ":dog:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x95"), ":dog2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa9"), ":poodle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xba"), ":wolf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x8a"), ":fox:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb1"), ":cat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x88"), ":cat2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x81"), ":lion_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xaf"), ":tiger:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x85"), ":tiger2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x86"), ":leopard:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb4"), ":horse:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x8e"), ":racehorse:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x8c"), ":deer:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x84"), ":unicorn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xae"), ":cow:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x82"), ":ox:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x83"), ":water_buffalo:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x84"), ":cow2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb7"), ":pig:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x96"), ":pig2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x97"), ":boar:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xbd"), ":pig_nose:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x8f"), ":ram:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x91"), ":sheep:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x90"), ":goat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xaa"), ":dromedary_camel:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xab"), ":camel:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x98"), ":elephant:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x8f"), ":rhino:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xad"), ":mouse:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x81"), ":mouse2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x80"), ":rat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb9"), ":hamster:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb0"), ":rabbit:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x87"), ":rabbit2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xbf"), ":chipmunk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x87"), ":bat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xbb"), ":bear:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa8"), ":koala:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xbc"), ":panda_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xbe"), ":feet:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x83"), ":turkey:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x94"), ":chicken:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x93"), ":rooster:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa3"), ":hatching_chick:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa4"), ":baby_chick:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa5"), ":hatched_chick:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa6"), ":bird:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa7"), ":penguin:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x8a"), ":dove:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x85"), ":eagle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x86"), ":duck:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x89"), ":owl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb8"), ":frog:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x8a"), ":crocodile:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa2"), ":turtle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x8e"), ":lizard:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x8d"), ":snake:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb2"), ":dragon_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x89"), ":dragon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb3"), ":whale:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x8b"), ":whale2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xac"), ":dolphin:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x9f"), ":fish:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa0"), ":tropical_fish:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa1"), ":blowfish:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x88"), ":shark:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x99"), ":octopus:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x9a"), ":shell:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x80"), ":crab:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x90"), ":shrimp:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x91"), ":squid:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x8b"), ":butterfly:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x8c"), ":snail:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x9b"), ":bug:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x9c"), ":ant:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x9d"), ":bee:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x9e"), ":beetle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xb7"), ":spider:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xb8"), ":spider_web:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x82"), ":scorpion:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x90"), ":bouquet:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb8"), ":cherry_blossom:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb5"), ":rosette:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb9"), ":rose:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x80"), ":wilted_rose:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xba"), ":hibiscus:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xbb"), ":sunflower:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xbc"), ":blossom:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb7"), ":tulip:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb1"), ":seedling:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb2"), ":evergreen_tree:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb3"), ":deciduous_tree:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb4"), ":palm_tree:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb5"), ":cactus:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xbe"), ":ear_of_rice:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xbf"), ":herb:"}, + Emoji{QString::fromUtf8("\xe2\x98\x98"), ":shamrock:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x80"), ":four_leaf_clover:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x81"), ":maple_leaf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x82"), ":fallen_leaf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x83"), ":leaves:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x84"), ":mushroom:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb0"), ":chestnut:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x8d"), ":earth_africa:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x8e"), ":earth_americas:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x8f"), ":earth_asia:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x91"), ":new_moon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x92"), ":waxing_crescent_moon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x93"), ":first_quarter_moon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x94"), ":waxing_gibbous_moon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x95"), ":full_moon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x96"), ":waning_gibbous_moon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x97"), ":last_quarter_moon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x98"), ":waning_crescent_moon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x99"), ":crescent_moon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x9a"), ":new_moon_with_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x9b"), ":first_quarter_moon_with_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x9c"), ":last_quarter_moon_with_face:"}, + Emoji{QString::fromUtf8("\xe2\x98\x80"), ":sunny:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x9d"), ":full_moon_with_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x9e"), ":sun_with_face:"}, + Emoji{QString::fromUtf8("\xe2\xad\x90"), ":star:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x9f"), ":star2:"}, + Emoji{QString::fromUtf8("\xe2\x98\x81"), ":cloud:"}, + Emoji{QString::fromUtf8("\xe2\x9b\x85"), ":partly_sunny:"}, + Emoji{QString::fromUtf8("\xe2\x9b\x88"), ":thunder_cloud_rain:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa4"), ":white_sun_small_cloud:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa5"), ":white_sun_cloud:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa6"), ":white_sun_rain_cloud:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa7"), ":cloud_rain:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa8"), ":cloud_snow:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa9"), ":cloud_lightning:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xaa"), ":cloud_tornado:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xab"), ":fog:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xac"), ":wind_blowing_face:"}, + Emoji{QString::fromUtf8("\xe2\x98\x82"), ":umbrella2:"}, + Emoji{QString::fromUtf8("\xe2\x98\x94"), ":umbrella:"}, + Emoji{QString::fromUtf8("\xe2\x9a\xa1"), ":zap:"}, + Emoji{QString::fromUtf8("\xe2\x9d\x84"), ":snowflake:"}, + Emoji{QString::fromUtf8("\xe2\x98\x83"), ":snowman2:"}, + Emoji{QString::fromUtf8("\xe2\x9b\x84"), ":snowman:"}, + Emoji{QString::fromUtf8("\xe2\x98\x84"), ":comet:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa5"), ":fire:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa7"), ":droplet:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x8a"), ":ocean:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x83"), ":jack_o_lantern:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x84"), ":christmas_tree:"}, + Emoji{QString::fromUtf8("\xe2\x9c\xa8"), ":sparkles:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x8b"), ":tanabata_tree:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x8d"), ":bamboo:"}, +}; + +const QList EmojiProvider::food = { + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x87"), ":grapes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x88"), ":melon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x89"), ":watermelon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x8a"), ":tangerine:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x8b"), ":lemon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x8c"), ":banana:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x8d"), ":pineapple:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x8e"), ":apple:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x8f"), ":green_apple:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x90"), ":pear:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x91"), ":peach:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x92"), ":cherries:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x93"), ":strawberry:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x9d"), ":kiwi:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x85"), ":tomato:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x91"), ":avocado:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x86"), ":eggplant:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x94"), ":potato:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x95"), ":carrot:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xbd"), ":corn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb6"), ":hot_pepper:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x92"), ":cucumber:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x9c"), ":peanuts:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x9e"), ":bread:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x90"), ":croissant:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x96"), ":french_bread:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x9e"), ":pancakes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa7\x80"), ":cheese:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x96"), ":meat_on_bone:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x97"), ":poultry_leg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x93"), ":bacon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x94"), ":hamburger:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x9f"), ":fries:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x95"), ":pizza:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xad"), ":hotdog:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xae"), ":taco:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xaf"), ":burrito:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x99"), ":stuffed_flatbread:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x9a"), ":egg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb3"), ":cooking:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x98"), ":shallow_pan_of_food:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb2"), ":stew:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x97"), ":salad:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xbf"), ":popcorn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb1"), ":bento:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x98"), ":rice_cracker:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x99"), ":rice_ball:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x9a"), ":rice:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x9b"), ":curry:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x9c"), ":ramen:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x9d"), ":spaghetti:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa0"), ":sweet_potato:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa2"), ":oden:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa3"), ":sushi:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa4"), ":fried_shrimp:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa5"), ":fish_cake:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa1"), ":dango:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa6"), ":icecream:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa7"), ":shaved_ice:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa8"), ":ice_cream:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa9"), ":doughnut:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xaa"), ":cookie:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x82"), ":birthday:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb0"), ":cake:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xab"), ":chocolate_bar:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xac"), ":candy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xad"), ":lollipop:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xae"), ":custard:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xaf"), ":honey_pot:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xbc"), ":baby_bottle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x9b"), ":milk:"}, + Emoji{QString::fromUtf8("\xe2\x98\x95"), ":coffee:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb5"), ":tea:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb6"), ":sake:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xbe"), ":champagne:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb7"), ":wine_glass:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb8"), ":cocktail:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb9"), ":tropical_drink:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xba"), ":beer:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xbb"), ":beers:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x82"), ":champagne_glass:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x83"), ":tumbler_glass:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xbd"), ":fork_knife_plate:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb4"), ":fork_and_knife:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x84"), ":spoon:"}, +}; + +const QList EmojiProvider::activity = { + Emoji{QString::fromUtf8("\xf0\x9f\x91\xbe"), ":space_invader:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xb4"), ":levitate:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xba"), ":fencer:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x87"), ":horse_racing:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbb"), ":horse_racing_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbc"), ":horse_racing_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbd"), ":horse_racing_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbe"), ":horse_racing_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbf"), ":horse_racing_tone5:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb7"), ":skier:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x82"), ":snowboarder:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8c"), ":golfer:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x84"), ":surfer:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbb"), ":surfer_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbc"), ":surfer_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbd"), ":surfer_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbe"), ":surfer_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbf"), ":surfer_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa3"), ":rowboat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbb"), ":rowboat_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbc"), ":rowboat_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbd"), ":rowboat_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbe"), ":rowboat_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbf"), ":rowboat_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8a"), ":swimmer:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbb"), ":swimmer_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbc"), ":swimmer_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbd"), ":swimmer_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbe"), ":swimmer_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbf"), ":swimmer_tone5:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb9"), ":basketball_player:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbb"), ":basketball_player_tone1:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbc"), ":basketball_player_tone2:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbd"), ":basketball_player_tone3:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbe"), ":basketball_player_tone4:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbf"), ":basketball_player_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8b"), ":lifter:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbb"), ":lifter_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbc"), ":lifter_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbd"), ":lifter_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbe"), ":lifter_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbf"), ":lifter_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb4"), ":bicyclist:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbb"), ":bicyclist_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbc"), ":bicyclist_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbd"), ":bicyclist_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbe"), ":bicyclist_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbf"), ":bicyclist_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb5"), ":mountain_bicyclist:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbb"), ":mountain_bicyclist_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbc"), ":mountain_bicyclist_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbd"), ":mountain_bicyclist_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbe"), ":mountain_bicyclist_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbf"), ":mountain_bicyclist_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb8"), ":cartwheel:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbb"), ":cartwheel_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbc"), ":cartwheel_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbd"), ":cartwheel_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbe"), ":cartwheel_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbf"), ":cartwheel_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbc"), ":wrestlers:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbb"), ":wrestlers_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbc"), ":wrestlers_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbd"), ":wrestlers_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbe"), ":wrestlers_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbf"), ":wrestlers_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbd"), ":water_polo:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbb"), ":water_polo_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbc"), ":water_polo_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbd"), ":water_polo_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbe"), ":water_polo_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbf"), ":water_polo_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbe"), ":handball:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbb"), ":handball_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbc"), ":handball_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbd"), ":handball_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbe"), ":handball_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbf"), ":handball_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb9"), ":juggling:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbb"), ":juggling_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbc"), ":juggling_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbd"), ":juggling_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbe"), ":juggling_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbf"), ":juggling_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xaa"), ":circus_tent:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xad"), ":performing_arts:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa8"), ":art:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb0"), ":slot_machine:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x80"), ":bath:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbb"), ":bath_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbc"), ":bath_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbd"), ":bath_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbe"), ":bath_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbf"), ":bath_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x97"), ":reminder_ribbon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x9f"), ":tickets:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xab"), ":ticket:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x96"), ":military_medal:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x86"), ":trophy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x85"), ":medal:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x87"), ":first_place:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x88"), ":second_place:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x89"), ":third_place:"}, + Emoji{QString::fromUtf8("\xe2\x9a\xbd"), ":soccer:"}, + Emoji{QString::fromUtf8("\xe2\x9a\xbe"), ":baseball:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x80"), ":basketball:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x90"), ":volleyball:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x88"), ":football:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x89"), ":rugby_football:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xbe"), ":tennis:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb1"), ":8ball:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb3"), ":bowling:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8f"), ":cricket:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x91"), ":field_hockey:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x92"), ":hockey:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x93"), ":ping_pong:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb8"), ":badminton:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x8a"), ":boxing_glove:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x8b"), ":martial_arts_uniform:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x85"), ":goal:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xaf"), ":dart:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb3"), ":golf:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb8"), ":ice_skate:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa3"), ":fishing_pole_and_fish:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xbd"), ":running_shirt_with_sash:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xbf"), ":ski:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xae"), ":video_game:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb2"), ":game_die:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xbc"), ":musical_score:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa4"), ":microphone:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa7"), ":headphones:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb7"), ":saxophone:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb8"), ":guitar:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb9"), ":musical_keyboard:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xba"), ":trumpet:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xbb"), ":violin:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x81"), ":drum:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xac"), ":clapper:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb9"), ":bow_and_arrow:"}, +}; + +const QList EmojiProvider::travel = { + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8e"), ":race_car:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8d"), ":motorcycle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xbe"), ":japan:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x94"), ":mountain_snow:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb0"), ":mountain:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x8b"), ":volcano:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xbb"), ":mount_fuji:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x95"), ":camping:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x96"), ":beach:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x9c"), ":desert:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x9d"), ":island:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x9e"), ":park:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x9f"), ":stadium:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x9b"), ":classical_building:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x97"), ":construction_site:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x98"), ":homes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x99"), ":cityscape:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x9a"), ":house_abandoned:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa0"), ":house:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa1"), ":house_with_garden:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa2"), ":office:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa3"), ":post_office:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa4"), ":european_post_office:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa5"), ":hospital:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa6"), ":bank:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa8"), ":hotel:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa9"), ":love_hotel:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xaa"), ":convenience_store:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xab"), ":school:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xac"), ":department_store:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xad"), ":factory:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xaf"), ":japanese_castle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb0"), ":european_castle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x92"), ":wedding:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xbc"), ":tokyo_tower:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xbd"), ":statue_of_liberty:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xaa"), ":church:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x8c"), ":mosque:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x8d"), ":synagogue:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xa9"), ":shinto_shrine:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x8b"), ":kaaba:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb2"), ":fountain:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xba"), ":tent:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x81"), ":foggy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x83"), ":night_with_stars:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x84"), ":sunrise_over_mountains:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x85"), ":sunrise:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x86"), ":city_dusk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x87"), ":city_sunset:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x89"), ":bridge_at_night:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x8c"), ":milky_way:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa0"), ":carousel_horse:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa1"), ":ferris_wheel:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa2"), ":roller_coaster:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x82"), ":steam_locomotive:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x83"), ":railway_car:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x84"), ":bullettrain_side:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x85"), ":bullettrain_front:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x86"), ":train2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x87"), ":metro:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x88"), ":light_rail:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x89"), ":station:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x8a"), ":tram:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x9d"), ":monorail:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x9e"), ":mountain_railway:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x8b"), ":train:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x8c"), ":bus:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x8d"), ":oncoming_bus:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x8e"), ":trolleybus:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x90"), ":minibus:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x91"), ":ambulance:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x92"), ":fire_engine:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x93"), ":police_car:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x94"), ":oncoming_police_car:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x95"), ":taxi:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x96"), ":oncoming_taxi:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x97"), ":red_car:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x98"), ":oncoming_automobile:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x99"), ":blue_car:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x9a"), ":truck:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x9b"), ":articulated_lorry:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x9c"), ":tractor:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb2"), ":bike:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xb4"), ":scooter:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xb5"), ":motor_scooter:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x8f"), ":busstop:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa3"), ":motorway:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa4"), ":railway_track:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xbd"), ":fuelpump:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa8"), ":rotating_light:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa5"), ":traffic_light:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa6"), ":vertical_traffic_light:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa7"), ":construction:"}, + Emoji{QString::fromUtf8("\xe2\x9a\x93"), ":anchor:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb5"), ":sailboat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xb6"), ":canoe:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa4"), ":speedboat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xb3"), ":cruise_ship:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb4"), ":ferry:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa5"), ":motorboat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa2"), ":ship:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x88"), ":airplane:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa9"), ":airplane_small:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xab"), ":airplane_departure:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xac"), ":airplane_arriving:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xba"), ":seat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x81"), ":helicopter:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x9f"), ":suspension_railway:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa0"), ":mountain_cableway:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa1"), ":aerial_tramway:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x80"), ":rocket:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xb0"), ":satellite_orbital:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa0"), ":stars:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x88"), ":rainbow:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x86"), ":fireworks:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x87"), ":sparkler:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x91"), ":rice_scene:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x81"), ":checkered_flag:"}, +}; + +const QList EmojiProvider::objects = { + Emoji{QString::fromUtf8("\xe2\x98\xa0"), ":skull_crossbones:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x8c"), ":love_letter:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa3"), ":bomb:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xb3"), ":hole:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x8d"), ":shopping_bags:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xbf"), ":prayer_beads:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x8e"), ":gem:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xaa"), ":knife:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xba"), ":amphora:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xba"), ":map:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x88"), ":barber:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\xbc"), ":frame_photo:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x8e"), ":bellhop:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xaa"), ":door:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x8c"), ":sleeping_accommodation:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x8f"), ":bed:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x8b"), ":couch:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xbd"), ":toilet:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xbf"), ":shower:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x81"), ":bathtub:"}, + Emoji{QString::fromUtf8("\xe2\x8c\x9b"), ":hourglass:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xb3"), ":hourglass_flowing_sand:"}, + Emoji{QString::fromUtf8("\xe2\x8c\x9a"), ":watch:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xb0"), ":alarm_clock:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xb1"), ":stopwatch:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xb2"), ":timer:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xb0"), ":clock:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa1"), ":thermometer:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb1"), ":beach_umbrella:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x88"), ":balloon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x89"), ":tada:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x8a"), ":confetti_ball:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x8e"), ":dolls:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x8f"), ":flags:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x90"), ":wind_chime:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x80"), ":ribbon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x81"), ":gift:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xb9"), ":joystick:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xaf"), ":postal_horn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x99"), ":microphone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x9a"), ":level_slider:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x9b"), ":control_knobs:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xbb"), ":radio:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb1"), ":iphone:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb2"), ":calling:"}, + Emoji{QString::fromUtf8("\xe2\x98\x8e"), ":telephone:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x9e"), ":telephone_receiver:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x9f"), ":pager:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa0"), ":fax:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x8b"), ":battery:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x8c"), ":electric_plug:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xbb"), ":computer:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\xa5"), ":desktop:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\xa8"), ":printer:"}, + Emoji{QString::fromUtf8("\xe2\x8c\xa8"), ":keyboard:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\xb1"), ":mouse_three_button:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\xb2"), ":trackball:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xbd"), ":minidisc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xbe"), ":floppy_disk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xbf"), ":cd:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x80"), ":dvd:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa5"), ":movie_camera:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x9e"), ":film_frames:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xbd"), ":projector:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xba"), ":tv:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb7"), ":camera:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb8"), ":camera_with_flash:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb9"), ":video_camera:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xbc"), ":vhs:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x8d"), ":mag:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x8e"), ":mag_right:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xac"), ":microscope:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xad"), ":telescope:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa1"), ":satellite:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xaf"), ":candle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa1"), ":bulb:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa6"), ":flashlight:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xae"), ":izakaya_lantern:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x94"), ":notebook_with_decorative_cover:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x95"), ":closed_book:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x96"), ":book:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x97"), ":green_book:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x98"), ":blue_book:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x99"), ":orange_book:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x9a"), ":books:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x93"), ":notebook:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x92"), ":ledger:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x83"), ":page_with_curl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x9c"), ":scroll:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x84"), ":page_facing_up:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb0"), ":newspaper:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\x9e"), ":newspaper2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x91"), ":bookmark_tabs:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x96"), ":bookmark:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb7"), ":label:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb0"), ":moneybag:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb4"), ":yen:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb5"), ":dollar:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb6"), ":euro:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb7"), ":pound:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb8"), ":money_with_wings:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb3"), ":credit_card:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x89"), ":envelope:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa7"), ":e-mail:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa8"), ":incoming_envelope:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa9"), ":envelope_with_arrow:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa4"), ":outbox_tray:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa5"), ":inbox_tray:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa6"), ":package:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xab"), ":mailbox:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xaa"), ":mailbox_closed:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xac"), ":mailbox_with_mail:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xad"), ":mailbox_with_no_mail:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xae"), ":postbox:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xb3"), ":ballot_box:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x8f"), ":pencil2:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x92"), ":black_nib:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\x8b"), ":pen_fountain:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\x8a"), ":pen_ballpoint:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\x8c"), ":paintbrush:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\x8d"), ":crayon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x9d"), ":pencil:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x81"), ":file_folder:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x82"), ":open_file_folder:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\x82"), ":dividers:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x85"), ":date:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x86"), ":calendar:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\x92"), ":notepad_spiral:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\x93"), ":calendar_spiral:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x87"), ":card_index:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x88"), ":chart_with_upwards_trend:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x89"), ":chart_with_downwards_trend:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x8a"), ":bar_chart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x8b"), ":clipboard:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x8c"), ":pushpin:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x8d"), ":round_pushpin:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x8e"), ":paperclip:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\x87"), ":paperclips:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x8f"), ":straight_ruler:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x90"), ":triangular_ruler:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x82"), ":scissors:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\x83"), ":card_box:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\x84"), ":file_cabinet:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\x91"), ":wastebasket:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x92"), ":lock:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x93"), ":unlock:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x8f"), ":lock_with_ink_pen:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x90"), ":closed_lock_with_key:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x91"), ":key:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\x9d"), ":key2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa8"), ":hammer:"}, + Emoji{QString::fromUtf8("\xe2\x9b\x8f"), ":pick:"}, + Emoji{QString::fromUtf8("\xe2\x9a\x92"), ":hammer_pick:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa0"), ":tools:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xa1"), ":dagger:"}, + Emoji{QString::fromUtf8("\xe2\x9a\x94"), ":crossed_swords:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xab"), ":gun:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa1"), ":shield:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa7"), ":wrench:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa9"), ":nut_and_bolt:"}, + Emoji{QString::fromUtf8("\xe2\x9a\x99"), ":gear:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\x9c"), ":compression:"}, + Emoji{QString::fromUtf8("\xe2\x9a\x97"), ":alembic:"}, + Emoji{QString::fromUtf8("\xe2\x9a\x96"), ":scales:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x97"), ":link:"}, + Emoji{QString::fromUtf8("\xe2\x9b\x93"), ":chains:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x89"), ":syringe:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x8a"), ":pill:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xac"), ":smoking:"}, + Emoji{QString::fromUtf8("\xe2\x9a\xb0"), ":coffin:"}, + Emoji{QString::fromUtf8("\xe2\x9a\xb1"), ":urn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xbf"), ":moyai:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa2"), ":oil:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xae"), ":crystal_ball:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x92"), ":shopping_cart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa9"), ":triangular_flag_on_post:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x8c"), ":crossed_flags:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb4"), ":flag_black:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb3"), ":flag_white:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb3\xf0\x9f\x8c\x88"), ":rainbow_flag:"}, +}; + +const QList EmojiProvider::symbols = { + Emoji{QString::fromUtf8("\xf0\x9f\x91\x81\xf0\x9f\x97\xa8"), ":eye_in_speech_bubble:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x98"), ":cupid:"}, + Emoji{QString::fromUtf8("\xe2\x9d\xa4"), ":heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x93"), ":heartbeat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x94"), ":broken_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x95"), ":two_hearts:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x96"), ":sparkling_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x97"), ":heartpulse:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x99"), ":blue_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x9a"), ":green_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x9b"), ":yellow_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x9c"), ":purple_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\xa4"), ":black_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x9d"), ":gift_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x9e"), ":revolving_hearts:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x9f"), ":heart_decoration:"}, + Emoji{QString::fromUtf8("\xe2\x9d\xa3"), ":heart_exclamation:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa2"), ":anger:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa5"), ":boom:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xab"), ":dizzy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xac"), ":speech_balloon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xa8"), ":speech_left:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xaf"), ":anger_right:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xad"), ":thought_balloon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xae"), ":white_flower:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x90"), ":globe_with_meridians:"}, + Emoji{QString::fromUtf8("\xe2\x99\xa8"), ":hotsprings:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x91"), ":octagonal_sign:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x9b"), ":clock12:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xa7"), ":clock1230:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x90"), ":clock1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x9c"), ":clock130:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x91"), ":clock2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x9d"), ":clock230:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x92"), ":clock3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x9e"), ":clock330:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x93"), ":clock4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x9f"), ":clock430:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x94"), ":clock5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xa0"), ":clock530:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x95"), ":clock6:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xa1"), ":clock630:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x96"), ":clock7:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xa2"), ":clock730:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x97"), ":clock8:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xa3"), ":clock830:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x98"), ":clock9:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xa4"), ":clock930:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x99"), ":clock10:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xa5"), ":clock1030:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x9a"), ":clock11:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xa6"), ":clock1130:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x80"), ":cyclone:"}, + Emoji{QString::fromUtf8("\xe2\x99\xa0"), ":spades:"}, + Emoji{QString::fromUtf8("\xe2\x99\xa5"), ":hearts:"}, + Emoji{QString::fromUtf8("\xe2\x99\xa6"), ":diamonds:"}, + Emoji{QString::fromUtf8("\xe2\x99\xa3"), ":clubs:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x83\x8f"), ":black_joker:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x80\x84"), ":mahjong:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb4"), ":flower_playing_cards:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x87"), ":mute:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x88"), ":speaker:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x89"), ":sound:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x8a"), ":loud_sound:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa2"), ":loudspeaker:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa3"), ":mega:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x94"), ":bell:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x95"), ":no_bell:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb5"), ":musical_note:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb6"), ":notes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb9"), ":chart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb1"), ":currency_exchange:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb2"), ":heavy_dollar_sign:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa7"), ":atm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xae"), ":put_litter_in_its_place:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb0"), ":potable_water:"}, + Emoji{QString::fromUtf8("\xe2\x99\xbf"), ":wheelchair:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb9"), ":mens:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xba"), ":womens:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xbb"), ":restroom:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xbc"), ":baby_symbol:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xbe"), ":wc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x82"), ":passport_control:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x83"), ":customs:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x84"), ":baggage_claim:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x85"), ":left_luggage:"}, + Emoji{QString::fromUtf8("\xe2\x9a\xa0"), ":warning:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb8"), ":children_crossing:"}, + Emoji{QString::fromUtf8("\xe2\x9b\x94"), ":no_entry:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xab"), ":no_entry_sign:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb3"), ":no_bicycles:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xad"), ":no_smoking:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xaf"), ":do_not_litter:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb1"), ":non-potable_water:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb7"), ":no_pedestrians:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb5"), ":no_mobile_phones:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x9e"), ":underage:"}, + Emoji{QString::fromUtf8("\xe2\x98\xa2"), ":radioactive:"}, + Emoji{QString::fromUtf8("\xe2\x98\xa3"), ":biohazard:"}, + Emoji{QString::fromUtf8("\xe2\xac\x86"), ":arrow_up:"}, + Emoji{QString::fromUtf8("\xe2\x86\x97"), ":arrow_upper_right:"}, + Emoji{QString::fromUtf8("\xe2\x9e\xa1"), ":arrow_right:"}, + Emoji{QString::fromUtf8("\xe2\x86\x98"), ":arrow_lower_right:"}, + Emoji{QString::fromUtf8("\xe2\xac\x87"), ":arrow_down:"}, + Emoji{QString::fromUtf8("\xe2\x86\x99"), ":arrow_lower_left:"}, + Emoji{QString::fromUtf8("\xe2\xac\x85"), ":arrow_left:"}, + Emoji{QString::fromUtf8("\xe2\x86\x96"), ":arrow_upper_left:"}, + Emoji{QString::fromUtf8("\xe2\x86\x95"), ":arrow_up_down:"}, + Emoji{QString::fromUtf8("\xe2\x86\x94"), ":left_right_arrow:"}, + Emoji{QString::fromUtf8("\xe2\x86\xa9"), ":leftwards_arrow_with_hook:"}, + Emoji{QString::fromUtf8("\xe2\x86\xaa"), ":arrow_right_hook:"}, + Emoji{QString::fromUtf8("\xe2\xa4\xb4"), ":arrow_heading_up:"}, + Emoji{QString::fromUtf8("\xe2\xa4\xb5"), ":arrow_heading_down:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x83"), ":arrows_clockwise:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x84"), ":arrows_counterclockwise:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x99"), ":back:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x9a"), ":end:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x9b"), ":on:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x9c"), ":soon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x9d"), ":top:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x90"), ":place_of_worship:"}, + Emoji{QString::fromUtf8("\xe2\x9a\x9b"), ":atom:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x89"), ":om_symbol:"}, + Emoji{QString::fromUtf8("\xe2\x9c\xa1"), ":star_of_david:"}, + Emoji{QString::fromUtf8("\xe2\x98\xb8"), ":wheel_of_dharma:"}, + Emoji{QString::fromUtf8("\xe2\x98\xaf"), ":yin_yang:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x9d"), ":cross:"}, + Emoji{QString::fromUtf8("\xe2\x98\xa6"), ":orthodox_cross:"}, + Emoji{QString::fromUtf8("\xe2\x98\xaa"), ":star_and_crescent:"}, + Emoji{QString::fromUtf8("\xe2\x98\xae"), ":peace:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x8e"), ":menorah:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xaf"), ":six_pointed_star:"}, + Emoji{QString::fromUtf8("\xe2\x99\x88"), ":aries:"}, + Emoji{QString::fromUtf8("\xe2\x99\x89"), ":taurus:"}, + Emoji{QString::fromUtf8("\xe2\x99\x8a"), ":gemini:"}, + Emoji{QString::fromUtf8("\xe2\x99\x8b"), ":cancer:"}, + Emoji{QString::fromUtf8("\xe2\x99\x8c"), ":leo:"}, + Emoji{QString::fromUtf8("\xe2\x99\x8d"), ":virgo:"}, + Emoji{QString::fromUtf8("\xe2\x99\x8e"), ":libra:"}, + Emoji{QString::fromUtf8("\xe2\x99\x8f"), ":scorpius:"}, + Emoji{QString::fromUtf8("\xe2\x99\x90"), ":sagittarius:"}, + Emoji{QString::fromUtf8("\xe2\x99\x91"), ":capricorn:"}, + Emoji{QString::fromUtf8("\xe2\x99\x92"), ":aquarius:"}, + Emoji{QString::fromUtf8("\xe2\x99\x93"), ":pisces:"}, + Emoji{QString::fromUtf8("\xe2\x9b\x8e"), ":ophiuchus:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x80"), ":twisted_rightwards_arrows:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x81"), ":repeat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x82"), ":repeat_one:"}, + Emoji{QString::fromUtf8("\xe2\x96\xb6"), ":arrow_forward:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xa9"), ":fast_forward:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xad"), ":track_next:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xaf"), ":play_pause:"}, + Emoji{QString::fromUtf8("\xe2\x97\x80"), ":arrow_backward:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xaa"), ":rewind:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xae"), ":track_previous:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xbc"), ":arrow_up_small:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xab"), ":arrow_double_up:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xbd"), ":arrow_down_small:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xac"), ":arrow_double_down:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xb8"), ":pause_button:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xb9"), ":stop_button:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xba"), ":record_button:"}, + Emoji{QString::fromUtf8("\xe2\x8f\x8f"), ":eject:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa6"), ":cinema:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x85"), ":low_brightness:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x86"), ":high_brightness:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb6"), ":signal_strength:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb3"), ":vibration_mode:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb4"), ":mobile_phone_off:"}, + Emoji{QString::fromUtf8("\xe2\x99\xbb"), ":recycle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x9b"), ":name_badge:"}, + Emoji{QString::fromUtf8("\xe2\x9a\x9c"), ":fleur-de-lis:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb0"), ":beginner:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb1"), ":trident:"}, + Emoji{QString::fromUtf8("\xe2\xad\x95"), ":o:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x85"), ":white_check_mark:"}, + Emoji{QString::fromUtf8("\xe2\x98\x91"), ":ballot_box_with_check:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x94"), ":heavy_check_mark:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x96"), ":heavy_multiplication_x:"}, + Emoji{QString::fromUtf8("\xe2\x9d\x8c"), ":x:"}, + Emoji{QString::fromUtf8("\xe2\x9d\x8e"), ":negative_squared_cross_mark:"}, + Emoji{QString::fromUtf8("\xe2\x9e\x95"), ":heavy_plus_sign:"}, + Emoji{QString::fromUtf8("\xe2\x9e\x96"), ":heavy_minus_sign:"}, + Emoji{QString::fromUtf8("\xe2\x9e\x97"), ":heavy_division_sign:"}, + Emoji{QString::fromUtf8("\xe2\x9e\xb0"), ":curly_loop:"}, + Emoji{QString::fromUtf8("\xe2\x9e\xbf"), ":loop:"}, + Emoji{QString::fromUtf8("\xe3\x80\xbd"), ":part_alternation_mark:"}, + Emoji{QString::fromUtf8("\xe2\x9c\xb3"), ":eight_spoked_asterisk:"}, + Emoji{QString::fromUtf8("\xe2\x9c\xb4"), ":eight_pointed_black_star:"}, + Emoji{QString::fromUtf8("\xe2\x9d\x87"), ":sparkle:"}, + Emoji{QString::fromUtf8("\xe2\x80\xbc"), ":bangbang:"}, + Emoji{QString::fromUtf8("\xe2\x81\x89"), ":interrobang:"}, + Emoji{QString::fromUtf8("\xe2\x9d\x93"), ":question:"}, + Emoji{QString::fromUtf8("\xe2\x9d\x94"), ":grey_question:"}, + Emoji{QString::fromUtf8("\xe2\x9d\x95"), ":grey_exclamation:"}, + Emoji{QString::fromUtf8("\xe2\x9d\x97"), ":exclamation:"}, + Emoji{QString::fromUtf8("\xe3\x80\xb0"), ":wavy_dash:"}, + Emoji{QString::fromUtf8("\xc2\xa9"), ":copyright:"}, + Emoji{QString::fromUtf8("\xc2\xae"), ":registered:"}, + Emoji{QString::fromUtf8("\xe2\x84\xa2"), ":tm:"}, + Emoji{QString::fromUtf8("#\xe2\x83\xa3"), ":hash:"}, + Emoji{QString::fromUtf8("*\xe2\x83\xa3"), ":asterisk:"}, + Emoji{QString::fromUtf8("0\xe2\x83\xa3"), ":zero:"}, + Emoji{QString::fromUtf8("1\xe2\x83\xa3"), ":one:"}, + Emoji{QString::fromUtf8("2\xe2\x83\xa3"), ":two:"}, + Emoji{QString::fromUtf8("3\xe2\x83\xa3"), ":three:"}, + Emoji{QString::fromUtf8("4\xe2\x83\xa3"), ":four:"}, + Emoji{QString::fromUtf8("5\xe2\x83\xa3"), ":five:"}, + Emoji{QString::fromUtf8("6\xe2\x83\xa3"), ":six:"}, + Emoji{QString::fromUtf8("7\xe2\x83\xa3"), ":seven:"}, + Emoji{QString::fromUtf8("8\xe2\x83\xa3"), ":eight:"}, + Emoji{QString::fromUtf8("9\xe2\x83\xa3"), ":nine:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x9f"), ":keycap_ten:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xaf"), ":100:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa0"), ":capital_abcd:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa1"), ":abcd:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa2"), ":1234:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa3"), ":symbols:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa4"), ":abc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x85\xb0"), ":a:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x8e"), ":ab:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x85\xb1"), ":b:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x91"), ":cl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x92"), ":cool:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x93"), ":free:"}, + Emoji{QString::fromUtf8("\xe2\x84\xb9"), ":information_source:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x94"), ":id:"}, + Emoji{QString::fromUtf8("\xe2\x93\x82"), ":m:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x95"), ":new:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x96"), ":ng:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x85\xbe"), ":o2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x97"), ":ok:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x85\xbf"), ":parking:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x98"), ":sos:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x99"), ":up:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x9a"), ":vs:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\x81"), ":koko:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\x82"), ":sa:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xb7"), ":u6708:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xb6"), ":u6709:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xaf"), ":u6307:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x89\x90"), ":ideograph_advantage:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xb9"), ":u5272:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\x9a"), ":u7121:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xb2"), ":u7981:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x89\x91"), ":accept:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xb8"), ":u7533:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xb4"), ":u5408:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xb3"), ":u7a7a:"}, + Emoji{QString::fromUtf8("\xe3\x8a\x97"), ":congratulations:"}, + Emoji{QString::fromUtf8("\xe3\x8a\x99"), ":secret:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xba"), ":u55b6:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xb5"), ":u6e80:"}, + Emoji{QString::fromUtf8("\xe2\x96\xaa"), ":black_small_square:"}, + Emoji{QString::fromUtf8("\xe2\x96\xab"), ":white_small_square:"}, + Emoji{QString::fromUtf8("\xe2\x97\xbb"), ":white_medium_square:"}, + Emoji{QString::fromUtf8("\xe2\x97\xbc"), ":black_medium_square:"}, + Emoji{QString::fromUtf8("\xe2\x97\xbd"), ":white_medium_small_square:"}, + Emoji{QString::fromUtf8("\xe2\x97\xbe"), ":black_medium_small_square:"}, + Emoji{QString::fromUtf8("\xe2\xac\x9b"), ":black_large_square:"}, + Emoji{QString::fromUtf8("\xe2\xac\x9c"), ":white_large_square:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb6"), ":large_orange_diamond:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb7"), ":large_blue_diamond:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb8"), ":small_orange_diamond:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb9"), ":small_blue_diamond:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xba"), ":small_red_triangle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xbb"), ":small_red_triangle_down:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa0"), ":diamond_shape_with_a_dot_inside:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x98"), ":radio_button:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb2"), ":black_square_button:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb3"), ":white_square_button:"}, + Emoji{QString::fromUtf8("\xe2\x9a\xaa"), ":white_circle:"}, + Emoji{QString::fromUtf8("\xe2\x9a\xab"), ":black_circle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb4"), ":red_circle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb5"), ":blue_circle:"}, +}; + +const QList EmojiProvider::flags = { + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xa8"), ":flag_ac:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xa9"), ":flag_ad:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xaa"), ":flag_ae:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xab"), ":flag_af:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xac"), ":flag_ag:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xae"), ":flag_ai:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb1"), ":flag_al:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb2"), ":flag_am:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb4"), ":flag_ao:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb6"), ":flag_aq:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb7"), ":flag_ar:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb8"), ":flag_as:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb9"), ":flag_at:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xba"), ":flag_au:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xbc"), ":flag_aw:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xbd"), ":flag_ax:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xbf"), ":flag_az:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xa6"), ":flag_ba:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xa7"), ":flag_bb:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xa9"), ":flag_bd:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xaa"), ":flag_be:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xab"), ":flag_bf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xac"), ":flag_bg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xad"), ":flag_bh:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xae"), ":flag_bi:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xaf"), ":flag_bj:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb1"), ":flag_bl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb2"), ":flag_bm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb3"), ":flag_bn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb4"), ":flag_bo:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb6"), ":flag_bq:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb7"), ":flag_br:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb8"), ":flag_bs:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb9"), ":flag_bt:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbb"), ":flag_bv:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbc"), ":flag_bw:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbe"), ":flag_by:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbf"), ":flag_bz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xa6"), ":flag_ca:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xa8"), ":flag_cc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xa9"), ":flag_cd:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xab"), ":flag_cf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xac"), ":flag_cg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xad"), ":flag_ch:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xae"), ":flag_ci:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb0"), ":flag_ck:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb1"), ":flag_cl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb2"), ":flag_cm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb3"), ":flag_cn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb4"), ":flag_co:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb5"), ":flag_cp:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb7"), ":flag_cr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xba"), ":flag_cu:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbb"), ":flag_cv:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbc"), ":flag_cw:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbd"), ":flag_cx:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbe"), ":flag_cy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbf"), ":flag_cz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xaa"), ":flag_de:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xac"), ":flag_dg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xaf"), ":flag_dj:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xb0"), ":flag_dk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xb2"), ":flag_dm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xb4"), ":flag_do:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xbf"), ":flag_dz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xa6"), ":flag_ea:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xa8"), ":flag_ec:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xaa"), ":flag_ee:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xac"), ":flag_eg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xad"), ":flag_eh:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xb7"), ":flag_er:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xb8"), ":flag_es:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xb9"), ":flag_et:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xba"), ":flag_eu:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xae"), ":flag_fi:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xaf"), ":flag_fj:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb0"), ":flag_fk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb2"), ":flag_fm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb4"), ":flag_fo:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb7"), ":flag_fr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xa6"), ":flag_ga:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xa7"), ":flag_gb:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xa9"), ":flag_gd:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xaa"), ":flag_ge:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xab"), ":flag_gf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xac"), ":flag_gg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xad"), ":flag_gh:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xae"), ":flag_gi:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb1"), ":flag_gl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb2"), ":flag_gm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb3"), ":flag_gn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb5"), ":flag_gp:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb6"), ":flag_gq:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb7"), ":flag_gr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb8"), ":flag_gs:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb9"), ":flag_gt:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xba"), ":flag_gu:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xbc"), ":flag_gw:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xbe"), ":flag_gy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb0"), ":flag_hk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb2"), ":flag_hm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb3"), ":flag_hn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb7"), ":flag_hr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb9"), ":flag_ht:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xba"), ":flag_hu:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xa8"), ":flag_ic:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xa9"), ":flag_id:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xaa"), ":flag_ie:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb1"), ":flag_il:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb2"), ":flag_im:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb3"), ":flag_in:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb4"), ":flag_io:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb6"), ":flag_iq:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb7"), ":flag_ir:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb8"), ":flag_is:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb9"), ":flag_it:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xaa"), ":flag_je:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xb2"), ":flag_jm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xb4"), ":flag_jo:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xb5"), ":flag_jp:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xaa"), ":flag_ke:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xac"), ":flag_kg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xad"), ":flag_kh:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xae"), ":flag_ki:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb2"), ":flag_km:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb3"), ":flag_kn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb5"), ":flag_kp:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb7"), ":flag_kr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xbc"), ":flag_kw:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xbe"), ":flag_ky:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xbf"), ":flag_kz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xa6"), ":flag_la:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xa7"), ":flag_lb:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xa8"), ":flag_lc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xae"), ":flag_li:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb0"), ":flag_lk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb7"), ":flag_lr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb8"), ":flag_ls:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb9"), ":flag_lt:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xba"), ":flag_lu:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xbb"), ":flag_lv:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xbe"), ":flag_ly:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xa6"), ":flag_ma:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xa8"), ":flag_mc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xa9"), ":flag_md:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xaa"), ":flag_me:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xab"), ":flag_mf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xac"), ":flag_mg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xad"), ":flag_mh:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb0"), ":flag_mk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb1"), ":flag_ml:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb2"), ":flag_mm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb3"), ":flag_mn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb4"), ":flag_mo:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb5"), ":flag_mp:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb6"), ":flag_mq:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb7"), ":flag_mr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb8"), ":flag_ms:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb9"), ":flag_mt:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xba"), ":flag_mu:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbb"), ":flag_mv:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbc"), ":flag_mw:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbd"), ":flag_mx:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbe"), ":flag_my:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbf"), ":flag_mz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xa6"), ":flag_na:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xa8"), ":flag_nc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xaa"), ":flag_ne:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xab"), ":flag_nf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xac"), ":flag_ng:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xae"), ":flag_ni:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb1"), ":flag_nl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb4"), ":flag_no:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb5"), ":flag_np:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb7"), ":flag_nr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xba"), ":flag_nu:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xbf"), ":flag_nz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb4\xf0\x9f\x87\xb2"), ":flag_om:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xa6"), ":flag_pa:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xaa"), ":flag_pe:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xab"), ":flag_pf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xac"), ":flag_pg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xad"), ":flag_ph:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb0"), ":flag_pk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb1"), ":flag_pl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb2"), ":flag_pm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb3"), ":flag_pn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb7"), ":flag_pr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb8"), ":flag_ps:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb9"), ":flag_pt:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xbc"), ":flag_pw:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xbe"), ":flag_py:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb6\xf0\x9f\x87\xa6"), ":flag_qa:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xaa"), ":flag_re:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xb4"), ":flag_ro:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xb8"), ":flag_rs:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xba"), ":flag_ru:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xbc"), ":flag_rw:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa6"), ":flag_sa:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa7"), ":flag_sb:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa8"), ":flag_sc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa9"), ":flag_sd:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xaa"), ":flag_se:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xac"), ":flag_sg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xad"), ":flag_sh:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xae"), ":flag_si:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xaf"), ":flag_sj:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb0"), ":flag_sk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb1"), ":flag_sl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb2"), ":flag_sm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb3"), ":flag_sn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb4"), ":flag_so:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb7"), ":flag_sr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb8"), ":flag_ss:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb9"), ":flag_st:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbb"), ":flag_sv:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbd"), ":flag_sx:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbe"), ":flag_sy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbf"), ":flag_sz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xa6"), ":flag_ta:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xa8"), ":flag_tc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xa9"), ":flag_td:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xab"), ":flag_tf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xac"), ":flag_tg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xad"), ":flag_th:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xaf"), ":flag_tj:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb0"), ":flag_tk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb1"), ":flag_tl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb2"), ":flag_tm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb3"), ":flag_tn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb4"), ":flag_to:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb7"), ":flag_tr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb9"), ":flag_tt:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xbb"), ":flag_tv:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xbc"), ":flag_tw:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xbf"), ":flag_tz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xa6"), ":flag_ua:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xac"), ":flag_ug:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xb2"), ":flag_um:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xb8"), ":flag_us:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xbe"), ":flag_uy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xbf"), ":flag_uz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xa6"), ":flag_va:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xa8"), ":flag_vc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xaa"), ":flag_ve:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xac"), ":flag_vg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xae"), ":flag_vi:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xb3"), ":flag_vn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xba"), ":flag_vu:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbc\xf0\x9f\x87\xab"), ":flag_wf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbc\xf0\x9f\x87\xb8"), ":flag_ws:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbd\xf0\x9f\x87\xb0"), ":flag_xk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbe\xf0\x9f\x87\xaa"), ":flag_ye:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbe\xf0\x9f\x87\xb9"), ":flag_yt:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbf\xf0\x9f\x87\xa6"), ":flag_za:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbf\xf0\x9f\x87\xb2"), ":flag_zm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbf\xf0\x9f\x87\xbc"), ":flag_zw:"}, +}; diff --git a/src/timeline/TimelineItem.cc b/src/timeline/TimelineItem.cc new file mode 100644 index 00000000..f7dd0f6e --- /dev/null +++ b/src/timeline/TimelineItem.cc @@ -0,0 +1,507 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#include +#include +#include +#include + +#include "Avatar.h" +#include "Config.h" +#include "Sync.h" + +#include "timeline/TimelineItem.h" +#include "timeline/widgets/FileItem.h" +#include "timeline/widgets/ImageItem.h" + +static const QRegExp URL_REGEX("((?:https?|ftp)://\\S+)"); +static const QString URL_HTML = "\\1"; + +namespace events = matrix::events; +namespace msgs = matrix::events::messages; + +void +TimelineItem::init() +{ + userAvatar_ = nullptr; + timestamp_ = nullptr; + userName_ = nullptr; + body_ = nullptr; + + font_.setPixelSize(conf::fontSize); + + QFontMetrics fm(font_); + + topLayout_ = new QHBoxLayout(this); + sideLayout_ = new QVBoxLayout(); + mainLayout_ = new QVBoxLayout(); + headerLayout_ = new QHBoxLayout(); + + topLayout_->setContentsMargins(conf::timeline::msgMargin, conf::timeline::msgMargin, 0, 0); + topLayout_->setSpacing(0); + + topLayout_->addLayout(sideLayout_); + topLayout_->addLayout(mainLayout_, 1); + + sideLayout_->setMargin(0); + sideLayout_->setSpacing(0); + + mainLayout_->setContentsMargins(conf::timeline::headerLeftMargin, 0, 0, 0); + mainLayout_->setSpacing(0); + + headerLayout_->setMargin(0); + headerLayout_->setSpacing(conf::timeline::headerSpacing); +} + +/* + * For messages created locally. + */ +TimelineItem::TimelineItem(events::MessageEventType ty, + const QString &userid, + QString body, + bool withSender, + QWidget *parent) + : QWidget(parent) +{ + init(); + + auto displayName = TimelineViewManager::displayName(userid); + auto timestamp = QDateTime::currentDateTime(); + + if (ty == events::MessageEventType::Emote) { + body = QString("* %1 %2").arg(displayName).arg(body); + descriptionMsg_ = {"", userid, body, descriptiveTime(timestamp)}; + } else { + descriptionMsg_ = { + "You: ", userid, body, descriptiveTime(QDateTime::currentDateTime())}; + } + + body = body.toHtmlEscaped(); + body.replace(URL_REGEX, URL_HTML); + body.replace("\n", "
"); + generateTimestamp(timestamp); + + if (withSender) { + generateBody(displayName, body); + setupAvatarLayout(displayName); + mainLayout_->addLayout(headerLayout_); + + AvatarProvider::resolve(userid, this); + } else { + generateBody(body); + setupSimpleLayout(); + } + + mainLayout_->addWidget(body_); +} + +TimelineItem::TimelineItem(ImageItem *image, + const QString &userid, + bool withSender, + QWidget *parent) + : QWidget{parent} +{ + init(); + + setupLocalWidgetLayout(image, userid, "sent an image", withSender); +} + +TimelineItem::TimelineItem(FileItem *file, const QString &userid, bool withSender, QWidget *parent) + : QWidget{parent} +{ + init(); + + setupLocalWidgetLayout(file, userid, "sent a file", withSender); +} + +/* + * Used to display images. The avatar and the username are displayed. + */ +TimelineItem::TimelineItem(ImageItem *image, + const events::MessageEvent &event, + bool with_sender, + QWidget *parent) + : QWidget(parent) +{ + init(); + + event_id_ = event.eventId(); + + auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp()); + auto displayName = TimelineViewManager::displayName(event.sender()); + + QSettings settings; + descriptionMsg_ = {event.sender() == settings.value("auth/user_id") ? "You" : displayName, + event.sender(), + " sent an image", + descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))}; + + generateTimestamp(timestamp); + + auto imageLayout = new QHBoxLayout(); + imageLayout->setContentsMargins(0, 5, 0, 0); + imageLayout->addWidget(image); + imageLayout->addStretch(1); + + if (with_sender) { + generateBody(displayName, ""); + setupAvatarLayout(displayName); + + mainLayout_->addLayout(headerLayout_); + + AvatarProvider::resolve(event.sender(), this); + } else { + setupSimpleLayout(); + } + + mainLayout_->addLayout(imageLayout); +} + +TimelineItem::TimelineItem(FileItem *file, + const events::MessageEvent &event, + bool with_sender, + QWidget *parent) + : QWidget(parent) +{ + init(); + + event_id_ = event.eventId(); + + auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp()); + auto displayName = TimelineViewManager::displayName(event.sender()); + + QSettings settings; + descriptionMsg_ = {event.sender() == settings.value("auth/user_id") ? "You" : displayName, + event.sender(), + " sent a file", + descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))}; + + generateTimestamp(timestamp); + + auto fileLayout = new QHBoxLayout(); + fileLayout->setContentsMargins(0, 5, 0, 0); + fileLayout->addWidget(file); + fileLayout->addStretch(1); + + if (with_sender) { + generateBody(displayName, ""); + setupAvatarLayout(displayName); + + mainLayout_->addLayout(headerLayout_); + + AvatarProvider::resolve(event.sender(), this); + } else { + setupSimpleLayout(); + } + + mainLayout_->addLayout(fileLayout); +} + +/* + * Used to display remote notice messages. + */ +TimelineItem::TimelineItem(const events::MessageEvent &event, + bool with_sender, + QWidget *parent) + : QWidget(parent) +{ + init(); + + event_id_ = event.eventId(); + + descriptionMsg_ = {TimelineViewManager::displayName(event.sender()), + event.sender(), + " sent a notification", + descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))}; + + auto body = event.content().body().trimmed().toHtmlEscaped(); + auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp()); + + generateTimestamp(timestamp); + + body.replace(URL_REGEX, URL_HTML); + body.replace("\n", "
"); + body = "" + body + ""; + + if (with_sender) { + auto displayName = TimelineViewManager::displayName(event.sender()); + + generateBody(displayName, body); + setupAvatarLayout(displayName); + + mainLayout_->addLayout(headerLayout_); + + AvatarProvider::resolve(event.sender(), this); + } else { + generateBody(body); + setupSimpleLayout(); + } + + mainLayout_->addWidget(body_); +} + +/* + * Used to display remote emote messages. + */ +TimelineItem::TimelineItem(const events::MessageEvent &event, + bool with_sender, + QWidget *parent) + : QWidget(parent) +{ + init(); + + event_id_ = event.eventId(); + + auto body = event.content().body().trimmed(); + auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp()); + auto displayName = TimelineViewManager::displayName(event.sender()); + auto emoteMsg = QString("* %1 %2").arg(displayName).arg(body); + + descriptionMsg_ = {"", + event.sender(), + emoteMsg, + descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))}; + + generateTimestamp(timestamp); + emoteMsg = emoteMsg.toHtmlEscaped(); + emoteMsg.replace(URL_REGEX, URL_HTML); + emoteMsg.replace("\n", "
"); + + if (with_sender) { + generateBody(displayName, emoteMsg); + setupAvatarLayout(displayName); + mainLayout_->addLayout(headerLayout_); + + AvatarProvider::resolve(event.sender(), this); + } else { + generateBody(emoteMsg); + setupSimpleLayout(); + } + + mainLayout_->addWidget(body_); +} + +/* + * Used to display remote text messages. + */ +TimelineItem::TimelineItem(const events::MessageEvent &event, + bool with_sender, + QWidget *parent) + : QWidget(parent) +{ + init(); + + event_id_ = event.eventId(); + + auto body = event.content().body().trimmed(); + auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp()); + auto displayName = TimelineViewManager::displayName(event.sender()); + + QSettings settings; + descriptionMsg_ = {event.sender() == settings.value("auth/user_id") ? "You" : displayName, + event.sender(), + QString(": %1").arg(body), + descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))}; + + generateTimestamp(timestamp); + + body = body.toHtmlEscaped(); + body.replace(URL_REGEX, URL_HTML); + body.replace("\n", "
"); + + if (with_sender) { + generateBody(displayName, body); + setupAvatarLayout(displayName); + + mainLayout_->addLayout(headerLayout_); + + AvatarProvider::resolve(event.sender(), this); + } else { + generateBody(body); + setupSimpleLayout(); + } + + mainLayout_->addWidget(body_); +} + +// Only the body is displayed. +void +TimelineItem::generateBody(const QString &body) +{ + QString content(" %1 "); + + body_ = new QLabel(this); + body_->setFont(font_); + body_->setWordWrap(true); + body_->setText(content.arg(replaceEmoji(body))); + body_->setMargin(0); + + body_->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction); + body_->setOpenExternalLinks(true); +} + +// The username/timestamp is displayed along with the message body. +void +TimelineItem::generateBody(const QString &userid, const QString &body) +{ + auto sender = userid; + + if (userid.startsWith("@")) { + // TODO: Fix this by using a UserId type. + if (userid.split(":")[0].split("@").size() > 1) + sender = userid.split(":")[0].split("@")[1]; + } + + QFont usernameFont = font_; + usernameFont.setBold(true); + + userName_ = new QLabel(this); + userName_->setFont(usernameFont); + userName_->setText(sender); + + if (body.isEmpty()) + return; + + body_ = new QLabel(this); + body_->setFont(font_); + body_->setWordWrap(true); + body_->setText(QString(" %1 ").arg(replaceEmoji(body))); + body_->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction); + body_->setOpenExternalLinks(true); + body_->setMargin(0); +} + +void +TimelineItem::generateTimestamp(const QDateTime &time) +{ + QFont timestampFont; + timestampFont.setPixelSize(conf::timeline::fonts::timestamp); + + QFontMetrics fm(timestampFont); + int topMargin = QFontMetrics(font_).ascent() - fm.ascent(); + + timestamp_ = new QLabel(this); + timestamp_->setFont(timestampFont); + timestamp_->setText(time.toString("HH:mm")); + timestamp_->setContentsMargins(0, topMargin, 0, 0); + timestamp_->setStyleSheet( + QString("font-size: %1px;").arg(conf::timeline::fonts::timestamp)); +} + +QString +TimelineItem::replaceEmoji(const QString &body) +{ + QString fmtBody = ""; + + QVector utf32_string = body.toUcs4(); + + for (auto &code : utf32_string) { + // TODO: Be more precise here. + if (code > 9000) + fmtBody += QString("") + .arg(conf::emojiSize) + + QString::fromUcs4(&code, 1) + ""; + else + fmtBody += QString::fromUcs4(&code, 1); + } + + return fmtBody; +} + +void +TimelineItem::setupAvatarLayout(const QString &userName) +{ + topLayout_->setContentsMargins(conf::timeline::msgMargin, conf::timeline::msgMargin, 0, 0); + + userAvatar_ = new Avatar(this); + userAvatar_->setLetter(QChar(userName[0]).toUpper()); + userAvatar_->setSize(conf::timeline::avatarSize); + + // TODO: The provided user name should be a UserId class + if (userName[0] == '@' && userName.size() > 1) + userAvatar_->setLetter(QChar(userName[1]).toUpper()); + + sideLayout_->addWidget(userAvatar_); + sideLayout_->addStretch(1); + + headerLayout_->addWidget(userName_); + headerLayout_->addWidget(timestamp_, 1); +} + +void +TimelineItem::setupSimpleLayout() +{ + sideLayout_->addWidget(timestamp_); + + // Keep only the time in plain text. + QTextEdit htmlText(timestamp_->text()); + QString plainText = htmlText.toPlainText(); + + timestamp_->adjustSize(); + + // Align the end of the avatar bubble with the end of the timestamp for + // messages with and without avatar. Otherwise their bodies would not be + // aligned. + int tsWidth = timestamp_->fontMetrics().width(plainText); + int offset = std::max(0, conf::timeline::avatarSize - tsWidth); + + int defaultFontHeight = QFontMetrics(font_).ascent(); + + timestamp_->setAlignment(Qt::AlignTop); + timestamp_->setContentsMargins( + offset + 1, defaultFontHeight - timestamp_->fontMetrics().ascent(), 0, 0); + topLayout_->setContentsMargins( + conf::timeline::msgMargin, conf::timeline::msgMargin / 3, 0, 0); +} + +void +TimelineItem::setUserAvatar(const QImage &avatar) +{ + if (userAvatar_ == nullptr) + return; + + userAvatar_->setImage(avatar); +} + +QString +TimelineItem::descriptiveTime(const QDateTime &then) +{ + auto now = QDateTime::currentDateTime(); + + auto days = then.daysTo(now); + + if (days == 0) + return then.toString("HH:mm"); + else if (days < 2) + return QString("Yesterday"); + else if (days < 365) + return then.toString("dd/MM"); + + return then.toString("dd/MM/yy"); +} + +TimelineItem::~TimelineItem() {} + +void +TimelineItem::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} diff --git a/src/timeline/TimelineView.cc b/src/timeline/TimelineView.cc new file mode 100644 index 00000000..8ccff85a --- /dev/null +++ b/src/timeline/TimelineView.cc @@ -0,0 +1,561 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#include +#include +#include + +#include "FloatingButton.h" +#include "RoomMessages.h" +#include "ScrollBar.h" +#include "Sync.h" + +#include "timeline/TimelineView.h" +#include "timeline/widgets/FileItem.h" +#include "timeline/widgets/ImageItem.h" + +namespace events = matrix::events; +namespace msgs = matrix::events::messages; + +static bool +isRedactedEvent(const QJsonObject &event) +{ + if (event.contains("redacted_because")) + return true; + + if (event.contains("unsigned") && + event.value("unsigned").toObject().contains("redacted_because")) + return true; + + return false; +} + +TimelineView::TimelineView(const Timeline &timeline, + QSharedPointer client, + const QString &room_id, + QWidget *parent) + : QWidget(parent) + , room_id_{room_id} + , client_{client} +{ + init(); + addEvents(timeline); +} + +TimelineView::TimelineView(QSharedPointer client, + const QString &room_id, + QWidget *parent) + : QWidget(parent) + , room_id_{room_id} + , client_{client} +{ + init(); +} + +void +TimelineView::sliderRangeChanged(int min, int max) +{ + Q_UNUSED(min); + + if (!scroll_area_->verticalScrollBar()->isVisible()) { + scroll_area_->verticalScrollBar()->setValue(max); + return; + } + + // If the scrollbar is close to the bottom and a new message + // is added we move the scrollbar. + if (max - scroll_area_->verticalScrollBar()->value() < SCROLL_BAR_GAP) { + scroll_area_->verticalScrollBar()->setValue(max); + return; + } + + int currentHeight = scroll_widget_->size().height(); + int diff = currentHeight - oldHeight_; + int newPosition = oldPosition_ + diff; + + // Keep the scroll bar to the bottom if it hasn't been activated yet. + if (oldPosition_ == 0 && !scroll_area_->verticalScrollBar()->isVisible()) + newPosition = max; + + if (lastMessageDirection_ == TimelineDirection::Top) + scroll_area_->verticalScrollBar()->setValue(newPosition); +} + +void +TimelineView::fetchHistory() +{ + bool hasEnoughMessages = scroll_area_->verticalScrollBar()->isVisible(); + + if (!hasEnoughMessages && !isTimelineFinished) { + isPaginationInProgress_ = true; + client_->messages(room_id_, prev_batch_token_); + paginationTimer_->start(500); + return; + } + + paginationTimer_->stop(); +} + +void +TimelineView::scrollDown() +{ + int current = scroll_area_->verticalScrollBar()->value(); + int max = scroll_area_->verticalScrollBar()->maximum(); + + // The first time we enter the room move the scroll bar to the bottom. + if (!isInitialized) { + scroll_area_->verticalScrollBar()->setValue(max); + isInitialized = true; + return; + } + + // If the gap is small enough move the scroll bar down. e.g when a new + // message appears. + if (max - current < SCROLL_BAR_GAP) + scroll_area_->verticalScrollBar()->setValue(max); +} + +void +TimelineView::sliderMoved(int position) +{ + if (!scroll_area_->verticalScrollBar()->isVisible()) + return; + + const int maxScroll = scroll_area_->verticalScrollBar()->maximum(); + const int currentScroll = scroll_area_->verticalScrollBar()->value(); + + if (maxScroll - currentScroll > SCROLL_BAR_GAP) { + scrollDownBtn_->show(); + scrollDownBtn_->raise(); + } else { + scrollDownBtn_->hide(); + } + + // The scrollbar is high enough so we can start retrieving old events. + if (position < SCROLL_BAR_GAP) { + if (isTimelineFinished) + return; + + // Prevent user from moving up when there is pagination in + // progress. + // TODO: Keep a map of the event ids to filter out duplicates. + if (isPaginationInProgress_) + return; + + isPaginationInProgress_ = true; + + // FIXME: Maybe move this to TimelineViewManager to remove the + // extra calls? + client_->messages(room_id_, prev_batch_token_); + } +} + +void +TimelineView::addBackwardsEvents(const QString &room_id, const RoomMessages &msgs) +{ + if (room_id_ != room_id) + return; + + if (msgs.chunk().count() == 0) { + isTimelineFinished = true; + return; + } + + isTimelineFinished = false; + QList items; + + // Reset the sender of the first message in the timeline + // cause we're about to insert a new one. + firstSender_.clear(); + + // Parse in reverse order to determine where we should not show sender's + // name. + auto ii = msgs.chunk().size(); + while (ii != 0) { + --ii; + + TimelineItem *item = + parseMessageEvent(msgs.chunk().at(ii).toObject(), TimelineDirection::Top); + + if (item != nullptr) + items.push_back(item); + } + + // Reverse again to render them. + std::reverse(items.begin(), items.end()); + + oldPosition_ = scroll_area_->verticalScrollBar()->value(); + oldHeight_ = scroll_widget_->size().height(); + + for (const auto &item : items) + addTimelineItem(item, TimelineDirection::Top); + + lastMessageDirection_ = TimelineDirection::Top; + + QApplication::processEvents(); + + prev_batch_token_ = msgs.end(); + isPaginationInProgress_ = false; + + // Exclude the top stretch. + if (!msgs.chunk().isEmpty() && scroll_layout_->count() > 1) + notifyForLastEvent(); + + // If this batch is the first being rendered (i.e the first and the last + // events originate from this batch), set the last sender. + if (lastSender_.isEmpty() && !items.isEmpty()) + lastSender_ = items.constFirst()->descriptionMessage().userid; +} + +TimelineItem * +TimelineView::parseMessageEvent(const QJsonObject &event, TimelineDirection direction) +{ + events::EventType ty = events::extractEventType(event); + + if (ty == events::EventType::RoomMessage) { + events::MessageEventType msg_type = events::extractMessageEventType(event); + + using Emote = events::MessageEvent; + using File = events::MessageEvent; + using Image = events::MessageEvent; + using Notice = events::MessageEvent; + using Text = events::MessageEvent; + + if (msg_type == events::MessageEventType::Text) { + return processMessageEvent(event, direction); + } else if (msg_type == events::MessageEventType::Notice) { + return processMessageEvent(event, direction); + } else if (msg_type == events::MessageEventType::Image) { + return processMessageEvent(event, direction); + } else if (msg_type == events::MessageEventType::Emote) { + return processMessageEvent(event, direction); + } else if (msg_type == events::MessageEventType::File) { + return processMessageEvent(event, direction); + } else if (msg_type == events::MessageEventType::Unknown) { + // TODO Handle redacted messages. + // Silenced for now. + if (!isRedactedEvent(event)) + qWarning() << "Unknown message type" << event; + + return nullptr; + } + } + + return nullptr; +} + +int +TimelineView::addEvents(const Timeline &timeline) +{ + int message_count = 0; + + QSettings settings; + QString localUser = settings.value("auth/user_id").toString(); + + for (const auto &event : timeline.events()) { + TimelineItem *item = parseMessageEvent(event.toObject(), TimelineDirection::Bottom); + + if (item != nullptr) { + addTimelineItem(item, TimelineDirection::Bottom); + + if (localUser != event.toObject().value("sender").toString()) + message_count += 1; + } + } + + lastMessageDirection_ = TimelineDirection::Bottom; + + QApplication::processEvents(); + + if (isInitialSync) { + prev_batch_token_ = timeline.previousBatch(); + isInitialSync = false; + } + + // Exclude the top stretch. + if (!timeline.events().isEmpty() && scroll_layout_->count() > 1) + notifyForLastEvent(); + + if (isActiveWindow() && isVisible() && timeline.events().size() > 0) + readLastEvent(); + + return message_count; +} + +void +TimelineView::init() +{ + QSettings settings; + local_user_ = settings.value("auth/user_id").toString(); + + QIcon icon; + icon.addFile(":/icons/icons/ui/angle-arrow-down.png"); + scrollDownBtn_ = new FloatingButton(icon, this); + scrollDownBtn_->setBackgroundColor(QColor("#F5F5F5")); + scrollDownBtn_->setForegroundColor(QColor("black")); + scrollDownBtn_->hide(); + + connect(scrollDownBtn_, &QPushButton::clicked, this, [=]() { + const int max = scroll_area_->verticalScrollBar()->maximum(); + scroll_area_->verticalScrollBar()->setValue(max); + }); + top_layout_ = new QVBoxLayout(this); + top_layout_->setSpacing(0); + top_layout_->setMargin(0); + + scroll_area_ = new QScrollArea(this); + scroll_area_->setWidgetResizable(true); + scroll_area_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + scrollbar_ = new ScrollBar(scroll_area_); + scroll_area_->setVerticalScrollBar(scrollbar_); + + scroll_widget_ = new QWidget(this); + + scroll_layout_ = new QVBoxLayout(scroll_widget_); + scroll_layout_->setContentsMargins(15, 0, 15, 15); + scroll_layout_->addStretch(1); + scroll_layout_->setSpacing(0); + scroll_layout_->setObjectName("timelinescrollarea"); + + scroll_area_->setWidget(scroll_widget_); + + top_layout_->addWidget(scroll_area_); + + setLayout(top_layout_); + + paginationTimer_ = new QTimer(this); + connect(paginationTimer_, &QTimer::timeout, this, &TimelineView::fetchHistory); + + connect(client_.data(), + &MatrixClient::messagesRetrieved, + this, + &TimelineView::addBackwardsEvents); + + connect(scroll_area_->verticalScrollBar(), + SIGNAL(valueChanged(int)), + this, + SLOT(sliderMoved(int))); + connect(scroll_area_->verticalScrollBar(), + SIGNAL(rangeChanged(int, int)), + this, + SLOT(sliderRangeChanged(int, int))); +} + +void +TimelineView::updateLastSender(const QString &user_id, TimelineDirection direction) +{ + if (direction == TimelineDirection::Bottom) + lastSender_ = user_id; + else + firstSender_ = user_id; +} + +bool +TimelineView::isSenderRendered(const QString &user_id, TimelineDirection direction) +{ + if (direction == TimelineDirection::Bottom) + return lastSender_ != user_id; + else + return firstSender_ != user_id; +} + +void +TimelineView::addTimelineItem(TimelineItem *item, TimelineDirection direction) +{ + if (direction == TimelineDirection::Bottom) + scroll_layout_->addWidget(item); + else + scroll_layout_->insertWidget(1, item); +} + +void +TimelineView::updatePendingMessage(int txn_id, QString event_id) +{ + if (pending_msgs_.head().txn_id == txn_id) { // We haven't received it yet + auto msg = pending_msgs_.dequeue(); + msg.event_id = event_id; + pending_sent_msgs_.append(msg); + } + sendNextPendingMessage(); +} + +void +TimelineView::addUserMessage(matrix::events::MessageEventType ty, const QString &body) +{ + QSettings settings; + auto user_id = settings.value("auth/user_id").toString(); + auto with_sender = lastSender_ != user_id; + + TimelineItem *view_item = new TimelineItem(ty, user_id, body, with_sender, scroll_widget_); + scroll_layout_->addWidget(view_item); + + lastMessageDirection_ = TimelineDirection::Bottom; + + QApplication::processEvents(); + + lastSender_ = user_id; + + int txn_id = client_->incrementTransactionId(); + PendingMessage message(ty, txn_id, body, "", "", view_item); + handleNewUserMessage(message); +} + +void +TimelineView::handleNewUserMessage(PendingMessage msg) +{ + pending_msgs_.enqueue(msg); + if (pending_msgs_.size() == 1 && pending_sent_msgs_.size() == 0) + sendNextPendingMessage(); +} + +void +TimelineView::sendNextPendingMessage() +{ + if (pending_msgs_.size() == 0) + return; + + PendingMessage &m = pending_msgs_.head(); + switch (m.ty) { + case matrix::events::MessageEventType::Image: + case matrix::events::MessageEventType::File: + client_->sendRoomMessage( + m.ty, m.txn_id, room_id_, QFileInfo(m.filename).fileName(), m.body); + break; + default: + client_->sendRoomMessage(m.ty, m.txn_id, room_id_, m.body); + break; + } +} + +void +TimelineView::notifyForLastEvent() +{ + auto lastItem = scroll_layout_->itemAt(scroll_layout_->count() - 1); + auto *lastTimelineItem = qobject_cast(lastItem->widget()); + + if (lastTimelineItem) + emit updateLastTimelineMessage(room_id_, lastTimelineItem->descriptionMessage()); + else + qWarning() << "Cast to TimelineView failed" << room_id_; +} + +bool +TimelineView::isPendingMessage(const QString &txnid, + const QString &sender, + const QString &local_userid) +{ + if (sender != local_userid) + return false; + + for (const auto &msg : pending_msgs_) { + if (QString::number(msg.txn_id) == txnid) + return true; + } + + for (const auto &msg : pending_sent_msgs_) { + if (QString::number(msg.txn_id) == txnid) + return true; + } + + return false; +} + +void +TimelineView::removePendingMessage(const QString &txnid) +{ + for (auto it = pending_sent_msgs_.begin(); it != pending_sent_msgs_.end(); ++it) { + if (QString::number(it->txn_id) == txnid) { + int index = std::distance(pending_sent_msgs_.begin(), it); + pending_sent_msgs_.removeAt(index); + return; + } + } + for (auto it = pending_msgs_.begin(); it != pending_msgs_.end(); ++it) { + if (QString::number(it->txn_id) == txnid) { + int index = std::distance(pending_msgs_.begin(), it); + pending_msgs_.removeAt(index); + return; + } + } +} + +void +TimelineView::handleFailedMessage(int txnid) +{ + Q_UNUSED(txnid); + // Note: We do this even if the message has already been echoed. + QTimer::singleShot(500, this, SLOT(sendNextPendingMessage())); +} + +void +TimelineView::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + +void +TimelineView::readLastEvent() const +{ + const auto eventId = getLastEventId(); + + if (!eventId.isEmpty()) + client_->readEvent(room_id_, eventId); +} + +QString +TimelineView::getLastEventId() const +{ + auto index = scroll_layout_->count(); + + // Search backwards for the first event that has a valid event id. + while (index > 0) { + --index; + + auto lastItem = scroll_layout_->itemAt(index); + auto *lastTimelineItem = qobject_cast(lastItem->widget()); + + if (lastTimelineItem && !lastTimelineItem->eventId().isEmpty()) + return lastTimelineItem->eventId(); + } + + return QString(""); +} + +void +TimelineView::showEvent(QShowEvent *event) +{ + readLastEvent(); + + QWidget::showEvent(event); +} + +bool +TimelineView::event(QEvent *event) +{ + if (event->type() == QEvent::WindowActivate) { + QTimer::singleShot(1000, this, [=]() { + emit clearUnreadMessageCount(room_id_); + readLastEvent(); + }); + } + + return QWidget::event(event); +} diff --git a/src/timeline/TimelineViewManager.cc b/src/timeline/TimelineViewManager.cc new file mode 100644 index 00000000..39f07639 --- /dev/null +++ b/src/timeline/TimelineViewManager.cc @@ -0,0 +1,301 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#include + +#include +#include +#include +#include + +#include "MatrixClient.h" +#include "Sync.h" + +#include "timeline/TimelineView.h" +#include "timeline/TimelineViewManager.h" +#include "timeline/widgets/FileItem.h" +#include "timeline/widgets/ImageItem.h" + +TimelineViewManager::TimelineViewManager(QSharedPointer client, QWidget *parent) + : QStackedWidget(parent) + , client_(client) +{ + setStyleSheet("border: none;"); + + connect( + client_.data(), &MatrixClient::messageSent, this, &TimelineViewManager::messageSent); + + connect(client_.data(), + &MatrixClient::messageSendFailed, + this, + &TimelineViewManager::messageSendFailed); +} + +TimelineViewManager::~TimelineViewManager() {} + +void +TimelineViewManager::messageSent(const QString &event_id, const QString &roomid, int txn_id) +{ + // We save the latest valid transaction ID for later use. + QSettings settings; + settings.setValue("client/transaction_id", txn_id + 1); + + auto view = views_[roomid]; + view->updatePendingMessage(txn_id, event_id); +} + +void +TimelineViewManager::messageSendFailed(const QString &roomid, int txn_id) +{ + auto view = views_[roomid]; + view->handleFailedMessage(txn_id); +} + +void +TimelineViewManager::queueTextMessage(const QString &msg) +{ + auto room_id = active_room_; + auto view = views_[room_id]; + + view->addUserMessage(matrix::events::MessageEventType::Text, msg); +} + +void +TimelineViewManager::queueEmoteMessage(const QString &msg) +{ + auto room_id = active_room_; + auto view = views_[room_id]; + + view->addUserMessage(matrix::events::MessageEventType::Emote, msg); +} + +void +TimelineViewManager::queueImageMessage(const QString &roomid, + const QString &filename, + const QString &url) +{ + if (!views_.contains(roomid)) { + qDebug() << "Cannot send m.image message to a non-managed view"; + return; + } + + auto view = views_[roomid]; + + view->addUserMessage(url, filename); +} + +void +TimelineViewManager::queueFileMessage(const QString &roomid, + const QString &filename, + const QString &url) +{ + if (!views_.contains(roomid)) { + qDebug() << "Cannot send m.file message to a non-managed view"; + return; + } + + auto view = views_[roomid]; + + view->addUserMessage(url, filename); +} + +void +TimelineViewManager::clearAll() +{ + for (auto view : views_) + removeWidget(view.data()); + + views_.clear(); +} + +void +TimelineViewManager::initialize(const Rooms &rooms) +{ + for (auto it = rooms.join().constBegin(); it != rooms.join().constEnd(); ++it) { + addRoom(it.value(), it.key()); + } +} + +void +TimelineViewManager::initialize(const QList &rooms) +{ + for (const auto &roomid : rooms) { + addRoom(roomid); + } +} + +void +TimelineViewManager::addRoom(const JoinedRoom &room, const QString &room_id) +{ + // Create a history view with the room events. + TimelineView *view = new TimelineView(room.timeline(), client_, room_id); + views_.insert(room_id, QSharedPointer(view)); + + connect(view, + &TimelineView::updateLastTimelineMessage, + this, + &TimelineViewManager::updateRoomsLastMessage); + connect(view, + &TimelineView::clearUnreadMessageCount, + this, + &TimelineViewManager::clearRoomMessageCount); + + // Add the view in the widget stack. + addWidget(view); +} + +void +TimelineViewManager::addRoom(const QString &room_id) +{ + // Create a history view without any events. + TimelineView *view = new TimelineView(client_, room_id); + views_.insert(room_id, QSharedPointer(view)); + + connect(view, + &TimelineView::updateLastTimelineMessage, + this, + &TimelineViewManager::updateRoomsLastMessage); + connect(view, + &TimelineView::clearUnreadMessageCount, + this, + &TimelineViewManager::clearRoomMessageCount); + + // Add the view in the widget stack. + addWidget(view); +} + +void +TimelineViewManager::sync(const Rooms &rooms) +{ + for (auto it = rooms.join().constBegin(); it != rooms.join().constEnd(); ++it) { + auto roomid = it.key(); + + if (!views_.contains(roomid)) { + qDebug() << "Ignoring event from unknown room" << roomid; + continue; + } + + auto view = views_.value(roomid); + + int msgs_added = view->addEvents(it.value().timeline()); + + if (msgs_added > 0) { + // TODO: When the app window gets active the current + // unread count (if any) should be cleared. + auto isAppActive = QApplication::activeWindow() != nullptr; + + if (roomid != active_room_ || !isAppActive) + emit unreadMessages(roomid, msgs_added); + } + } +} + +void +TimelineViewManager::setHistoryView(const QString &room_id) +{ + if (!views_.contains(room_id)) { + qDebug() << "Room ID from RoomList is not present in ViewManager" << room_id; + return; + } + + active_room_ = room_id; + auto view = views_.value(room_id); + + setCurrentWidget(view.data()); + + view->fetchHistory(); + view->scrollDown(); +} + +QMap TimelineViewManager::DISPLAY_NAMES; + +QString +TimelineViewManager::chooseRandomColor() +{ + std::random_device random_device; + std::mt19937 engine{random_device()}; + std::uniform_real_distribution dist(0, 1); + + float hue = dist(engine); + float saturation = 0.9; + float value = 0.7; + + int hue_i = hue * 6; + + float f = hue * 6 - hue_i; + + float p = value * (1 - saturation); + float q = value * (1 - f * saturation); + float t = value * (1 - (1 - f) * saturation); + + float r = 0; + float g = 0; + float b = 0; + + if (hue_i == 0) { + r = value; + g = t; + b = p; + } else if (hue_i == 1) { + r = q; + g = value; + b = p; + } else if (hue_i == 2) { + r = p; + g = value; + b = t; + } else if (hue_i == 3) { + r = p; + g = q; + b = value; + } else if (hue_i == 4) { + r = t; + g = p; + b = value; + } else if (hue_i == 5) { + r = value; + g = p; + b = q; + } + + int ri = r * 256; + int gi = g * 256; + int bi = b * 256; + + QColor color(ri, gi, bi); + + return color.name(); +} + +QString +TimelineViewManager::displayName(const QString &userid) +{ + if (DISPLAY_NAMES.contains(userid)) + return DISPLAY_NAMES.value(userid); + + return userid; +} + +bool +TimelineViewManager::hasLoaded() const +{ + for (const auto &view : views_) + if (!view->hasLoaded()) + return false; + + return true; +} diff --git a/src/timeline/widgets/FileItem.cc b/src/timeline/widgets/FileItem.cc new file mode 100644 index 00000000..8d0100c7 --- /dev/null +++ b/src/timeline/widgets/FileItem.cc @@ -0,0 +1,208 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "timeline/widgets/FileItem.h" + +namespace events = matrix::events; +namespace msgs = matrix::events::messages; + +void +FileItem::init() +{ + setMouseTracking(true); + setCursor(Qt::PointingHandCursor); + setAttribute(Qt::WA_Hover, true); + + icon_.addFile(":/icons/icons/ui/arrow-pointing-down.png"); + + QList url_parts = url_.toString().split("mxc://"); + if (url_parts.size() != 2) { + qDebug() << "Invalid format for image" << url_.toString(); + return; + } + + QString media_params = url_parts[1]; + url_ = QString("%1/_matrix/media/r0/download/%2") + .arg(client_.data()->getHomeServer().toString(), media_params); + + connect(client_.data(), &MatrixClient::fileDownloaded, this, &FileItem::fileDownloaded); +} + +FileItem::FileItem(QSharedPointer client, + const events::MessageEvent &event, + QWidget *parent) + : QWidget(parent) + , url_{event.msgContent().url()} + , text_{event.content().body()} + , event_{event} + , client_{client} +{ + readableFileSize_ = calculateFileSize(event.msgContent().info().size); + + init(); +} + +FileItem::FileItem(QSharedPointer client, + const QString &url, + const QString &filename, + QWidget *parent) + : QWidget(parent) + , url_{url} + , text_{QFileInfo(filename).fileName()} + , client_{client} +{ + readableFileSize_ = calculateFileSize(QFileInfo(filename).size()); + + init(); +} + +QString +FileItem::calculateFileSize(int nbytes) const +{ + if (nbytes < 1024) + return QString("%1 B").arg(nbytes); + + if (nbytes < 1024 * 1024) + return QString("%1 KB").arg(nbytes / 1024); + + return QString("%1 MB").arg(nbytes / 1024 / 1024); +} + +void +FileItem::openUrl() +{ + if (url_.toString().isEmpty()) + return; + + if (!QDesktopServices::openUrl(url_)) + qWarning() << "Could not open url" << url_.toString(); +} + +QSize +FileItem::sizeHint() const +{ + return QSize(MaxWidth, Height); +} + +void +FileItem::mousePressEvent(QMouseEvent *event) +{ + if (event->button() != Qt::LeftButton) + return; + + auto point = event->pos(); + + // Click on the download icon. + if (QRect(HorizontalPadding, VerticalPadding / 2, IconDiameter, IconDiameter) + .contains(point)) { + filenameToSave_ = QFileDialog::getSaveFileName(this, tr("Save File"), text_); + + if (filenameToSave_.isEmpty()) + return; + + client_->downloadFile(event_.eventId(), url_); + } else { + openUrl(); + } +} + +void +FileItem::fileDownloaded(const QString &event_id, const QByteArray &data) +{ + if (event_id != event_.eventId()) + return; + + try { + QFile file(filenameToSave_); + + if (!file.open(QIODevice::WriteOnly)) + return; + + file.write(data); + file.close(); + } catch (const std::exception &ex) { + qDebug() << "Error while saving file to:" << ex.what(); + } +} + +void +FileItem::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + QFont font("Open Sans"); + font.setPixelSize(12); + font.setWeight(80); + + QFontMetrics fm(font); + + int computedWidth = std::min( + fm.width(text_) + 2 * IconRadius + VerticalPadding * 2 + TextPadding, (double)MaxWidth); + + QPainterPath path; + path.addRoundedRect(QRectF(0, 0, computedWidth, Height), 10, 10); + + painter.setPen(Qt::NoPen); + painter.fillPath(path, backgroundColor_); + painter.drawPath(path); + + QPainterPath circle; + circle.addEllipse(QPoint(IconXCenter, IconYCenter), IconRadius, IconRadius); + + painter.setPen(Qt::NoPen); + painter.fillPath(circle, iconColor_); + painter.drawPath(circle); + + icon_.paint(&painter, + QRect(IconXCenter - DownloadIconRadius / 2, + IconYCenter - DownloadIconRadius / 2, + DownloadIconRadius, + DownloadIconRadius), + Qt::AlignCenter, + QIcon::Normal); + + const int textStartX = HorizontalPadding + 2 * IconRadius + TextPadding; + const int textStartY = VerticalPadding + fm.ascent() / 2; + + // Draw the filename. + QString elidedText = + fm.elidedText(text_, + Qt::ElideRight, + computedWidth - HorizontalPadding * 2 - TextPadding - 2 * IconRadius); + + painter.setFont(font); + painter.setPen(QPen(textColor_)); + painter.drawText(QPoint(textStartX, textStartY), elidedText); + + // Draw the filesize. + font.setWeight(50); + painter.setFont(font); + painter.setPen(QPen(textColor_)); + painter.drawText(QPoint(textStartX, textStartY + 1.5 * fm.ascent()), readableFileSize_); +} diff --git a/src/timeline/widgets/ImageItem.cc b/src/timeline/widgets/ImageItem.cc new file mode 100644 index 00000000..106fc79b --- /dev/null +++ b/src/timeline/widgets/ImageItem.cc @@ -0,0 +1,223 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +#include "dialogs/ImageOverlayDialog.h" +#include "timeline/widgets/ImageItem.h" + +namespace events = matrix::events; +namespace msgs = matrix::events::messages; + +ImageItem::ImageItem(QSharedPointer client, + const events::MessageEvent &event, + QWidget *parent) + : QWidget(parent) + , event_{event} + , client_{client} +{ + setMouseTracking(true); + setCursor(Qt::PointingHandCursor); + setAttribute(Qt::WA_Hover, true); + + url_ = event.msgContent().url(); + text_ = event.content().body(); + + QList url_parts = url_.toString().split("mxc://"); + + if (url_parts.size() != 2) { + qDebug() << "Invalid format for image" << url_.toString(); + return; + } + + QString media_params = url_parts[1]; + url_ = QString("%1/_matrix/media/r0/download/%2") + .arg(client_.data()->getHomeServer().toString(), media_params); + + client_.data()->downloadImage(event.eventId(), url_); + + connect(client_.data(), + SIGNAL(imageDownloaded(const QString &, const QPixmap &)), + this, + SLOT(imageDownloaded(const QString &, const QPixmap &))); +} + +ImageItem::ImageItem(QSharedPointer client, + const QString &url, + const QString &filename, + QWidget *parent) + : QWidget(parent) + , url_{url} + , text_{QFileInfo(filename).fileName()} + , client_{client} +{ + setMouseTracking(true); + setCursor(Qt::PointingHandCursor); + setAttribute(Qt::WA_Hover, true); + + QList url_parts = url_.toString().split("mxc://"); + + if (url_parts.size() != 2) { + qDebug() << "Invalid format for image" << url_.toString(); + return; + } + + QString media_params = url_parts[1]; + url_ = QString("%1/_matrix/media/r0/download/%2") + .arg(client_.data()->getHomeServer().toString(), media_params); + + setImage(QPixmap(filename)); +} + +void +ImageItem::imageDownloaded(const QString &event_id, const QPixmap &img) +{ + if (event_id != event_.eventId()) + return; + + setImage(img); +} + +void +ImageItem::openUrl() +{ + if (url_.toString().isEmpty()) + return; + + if (!QDesktopServices::openUrl(url_)) + qWarning() << "Could not open url" << url_.toString(); +} + +void +ImageItem::scaleImage() +{ + if (image_.isNull()) + return; + + auto width_ratio = (double)max_width_ / (double)image_.width(); + auto height_ratio = (double)max_height_ / (double)image_.height(); + + auto min_aspect_ratio = std::min(width_ratio, height_ratio); + + if (min_aspect_ratio > 1) { + width_ = image_.width(); + height_ = image_.height(); + } else { + width_ = image_.width() * min_aspect_ratio; + height_ = image_.height() * min_aspect_ratio; + } + + setFixedSize(width_, height_); + scaled_image_ = + image_.scaled(width_, height_, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); +} + +QSize +ImageItem::sizeHint() const +{ + if (image_.isNull()) + return QSize(max_width_, bottom_height_); + + return QSize(width_, height_); +} + +void +ImageItem::setImage(const QPixmap &image) +{ + image_ = image; + scaleImage(); + update(); +} + +void +ImageItem::mousePressEvent(QMouseEvent *event) +{ + if (event->button() != Qt::LeftButton) + return; + + if (image_.isNull()) { + openUrl(); + return; + } + + auto point = event->pos(); + + // Click on the text box. + if (QRect(0, height_ - bottom_height_, width_, bottom_height_).contains(point)) { + openUrl(); + } else { + auto image_dialog = new ImageOverlayDialog(image_, this); + image_dialog->show(); + } +} + +void +ImageItem::resizeEvent(QResizeEvent *event) +{ + Q_UNUSED(event); + + scaleImage(); +} + +void +ImageItem::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + QFont font("Open Sans"); + font.setPixelSize(12); + + QFontMetrics metrics(font); + int fontHeight = metrics.height(); + + if (image_.isNull()) { + int height = fontHeight + 10; + + QString elidedText = metrics.elidedText(text_, Qt::ElideRight, max_width_ - 10); + + setFixedSize(metrics.width(elidedText), fontHeight + 10); + + painter.setFont(font); + painter.setPen(QPen(QColor(66, 133, 244))); + painter.drawText(QPoint(0, height / 2 + 2), elidedText); + + return; + } + + painter.fillRect(QRect(0, 0, width_, height_), scaled_image_); + + if (underMouse()) { + // Bottom text section + painter.fillRect(QRect(0, height_ - bottom_height_, width_, bottom_height_), + QBrush(QColor(33, 33, 33, 128))); + + QString elidedText = metrics.elidedText(text_, Qt::ElideRight, width_ - 10); + + font.setWeight(80); + painter.setFont(font); + painter.setPen(QPen(QColor("white"))); + painter.drawText(QPoint(5, height_ - fontHeight / 2), elidedText); + } +} -- cgit 1.5.1