summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThibault Meunier <thibault.meunier@isode.com>2016-07-18 09:03:55 (GMT)
committerKevin Smith <kevin.smith@isode.com>2017-02-20 15:58:04 (GMT)
commit9fe83ff62fb48662df7920afc0be71d04dafe3e4 (patch)
tree4805c1e1c0f41145bb4b97dd5e1314294e4c71b8 /Swift/QtUI
parent407a266b8de87c8cd35b3ab4d0fede20786d4944 (diff)
downloadswift-9fe83ff62fb48662df7920afc0be71d04dafe3e4.zip
swift-9fe83ff62fb48662df7920afc0be71d04dafe3e4.tar.bz2
New Unicode Emojis Dialog
The new selector behaves like the old one. However, selection of an emoji results in the corresponding UTF-8 sequence to be inserted into the input widget instead of a ASCII emoticon. The code is based on the Emojione library which is MIT licensed. Emojione provides a mapping from shortnames to relevant Unicode codepoint, as well as mappings from textual emoticons (e.g. ). This commit does not modify the existing emoticon parser and so does not include any ability to enter emojis via text entry. The part of the Emojione library required to generate the mappings in C++ is included in this patch, specifically the emoji.json file. It is used to generate a corresponding .cpp file. Mapping code can be generated as follows: * cd BuildTools/EmojisGenerator/ * (optional) update emoji.json from https://github.com/Ranks/emojione/blob/master/emoji.json) - Version used with this commit: ba845a7 * npm install * node generate.js Test-information: General * Click the emoji button opens the selector * Change tab * Click an emoji and check it appears correctly * Click outside emoji dialog hides it Emojis * Emojis are well printed on macOS with Qt 5.7.1 * Emojis are black/white on Windows 10 with Qt 5.7.1 * Emojis have the right tooltip (when mouse is hover) * Check emojis are rendered appropriately by the receiving client Tabs * Tabs have the right tooltip * Click an emoji adds to recent tab * Emojis in the Recent tab are ordered by last click date (with a maximum of 50 "recent" emojis) * Recent emojis are saved in the QtSetting under "recentEmojis" Change-Id: Ibd07b8713d6272da6a8a4c9c35ddf866473f662b
Diffstat (limited to 'Swift/QtUI')
-rw-r--r--Swift/QtUI/QtChatWindow.cpp51
-rw-r--r--Swift/QtUI/QtChatWindow.h19
-rw-r--r--Swift/QtUI/QtChatWindowFactory.cpp10
-rw-r--r--Swift/QtUI/QtChatWindowFactory.h14
-rw-r--r--Swift/QtUI/QtEmojiCell.cpp41
-rw-r--r--Swift/QtUI/QtEmojiCell.h30
-rw-r--r--Swift/QtUI/QtEmojisGrid.cpp72
-rw-r--r--Swift/QtUI/QtEmojisGrid.h31
-rw-r--r--Swift/QtUI/QtEmojisScroll.cpp34
-rw-r--r--Swift/QtUI/QtEmojisScroll.h18
-rw-r--r--Swift/QtUI/QtEmojisSelector.cpp75
-rw-r--r--Swift/QtUI/QtEmojisSelector.h40
-rw-r--r--Swift/QtUI/QtEmoticonCell.cpp36
-rw-r--r--Swift/QtUI/QtEmoticonCell.h40
-rw-r--r--Swift/QtUI/QtEmoticonsGrid.cpp63
-rw-r--r--Swift/QtUI/QtEmoticonsGrid.h46
-rw-r--r--Swift/QtUI/QtRecentEmojisGrid.cpp59
-rw-r--r--Swift/QtUI/QtRecentEmojisGrid.h43
-rw-r--r--Swift/QtUI/QtSwift.cpp7
-rw-r--r--Swift/QtUI/SConscript7
20 files changed, 509 insertions, 227 deletions
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index d799e19..345139c 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -1,96 +1,97 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swift/QtUI/QtChatWindow.h>
#include <memory>
#include <boost/cstdint.hpp>
#include <boost/lexical_cast.hpp>
#include <QApplication>
#include <QBoxLayout>
#include <QCloseEvent>
#include <QComboBox>
#include <QCursor>
#include <QDebug>
#include <QFileDialog>
#include <QFileInfo>
+#include <QFontMetrics>
#include <QInputDialog>
#include <QLabel>
#include <QLineEdit>
#include <QMenu>
#include <QMessageBox>
#include <QMimeData>
#include <QPoint>
#include <QPushButton>
#include <QSize>
#include <QSplitter>
#include <QString>
#include <QTextDocument>
#include <QTextEdit>
#include <QTime>
#include <QToolButton>
#include <QUrl>
#include <Swiften/Base/Log.h>
#include <Swift/Controllers/Roster/ContactRosterItem.h>
#include <Swift/Controllers/Roster/Roster.h>
#include <Swift/Controllers/Roster/RosterItem.h>
#include <Swift/Controllers/Settings/SettingsProvider.h>
#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <SwifTools/EmojiMapper.h>
#include <SwifTools/TabComplete.h>
#include <Swift/QtUI/QtAddBookmarkWindow.h>
#include <Swift/QtUI/QtEditBookmarkWindow.h>
-#include <Swift/QtUI/QtEmoticonsGrid.h>
+#include <Swift/QtUI/QtEmojisSelector.h>
#include <Swift/QtUI/QtPlainChatView.h>
#include <Swift/QtUI/QtScaledAvatarCache.h>
#include <Swift/QtUI/QtSettingsProvider.h>
#include <Swift/QtUI/QtTextEdit.h>
#include <Swift/QtUI/QtUISettingConstants.h>
#include <Swift/QtUI/QtUtilities.h>
#include <Swift/QtUI/QtWebKitChatView.h>
#include <Swift/QtUI/Roster/QtOccupantListWidget.h>
namespace Swift {
-QtChatWindow::QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, const std::map<std::string, std::string>& emoticons) : QtTabbable(), id_(Q2PSTRING(contact)), contact_(contact), nextAlertId_(0), eventStream_(eventStream), blockingState_(BlockingUnsupported), isMUC_(false), supportsImpromptuChat_(false), roomBookmarkState_(RoomNotBookmarked) {
- settings_ = settings;
+QtChatWindow::QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QtSettingsProvider* qtOnlySettings) : QtTabbable(), id_(Q2PSTRING(contact)), contact_(contact), nextAlertId_(0), eventStream_(eventStream), settings_(settings), qtOnlySettings_(qtOnlySettings), blockingState_(BlockingUnsupported), isMUC_(false), supportsImpromptuChat_(false), roomBookmarkState_(RoomNotBookmarked) {
unreadCount_ = 0;
isOnline_ = true;
completer_ = nullptr;
affiliationEditor_ = nullptr;
theme_ = theme;
isCorrection_ = false;
labelModel_ = nullptr;
correctionEnabled_ = Maybe;
fileTransferEnabled_ = Maybe;
updateTitleWithUnreadCount();
assert(settings);
setAcceptDrops(true);
alertStyleSheet_ = ".QWidget, QTextEdit { background: rgb(255, 255, 153); color: black }";
QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, this);
layout->setContentsMargins(0,0,0,0);
layout->setSpacing(2);
alertLayout_ = new QVBoxLayout();
layout->addLayout(alertLayout_);
subjectLayout_ = new QBoxLayout(QBoxLayout::LeftToRight);
subject_ = new QLineEdit(this);
subjectLayout_->addWidget(subject_);
setSubject("");
subject_->setReadOnly(true);
QPushButton* actionButton_ = new QPushButton(this);
actionButton_->setIcon(QIcon(":/icons/actions.png"));
@@ -118,71 +119,67 @@ QtChatWindow::QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventSt
midBar_ = new QWidget(this);
//layout->addWidget(midBar);
midBar_->setAutoFillBackground(true);
QHBoxLayout *midBarLayout = new QHBoxLayout(midBar_);
midBarLayout->setContentsMargins(0,0,0,0);
midBarLayout->setSpacing(2);
//midBarLayout->addStretch();
labelsWidget_ = new QComboBox(this);
labelsWidget_->setFocusPolicy(Qt::NoFocus);
labelsWidget_->hide();
labelsWidget_->setSizeAdjustPolicy(QComboBox::AdjustToContents);
midBarLayout->addWidget(labelsWidget_,0);
connect(labelsWidget_, SIGNAL(currentIndexChanged(int)), this, SLOT(handleCurrentLabelChanged(int)));
defaultLabelsPalette_ = labelsWidget_->palette();
QHBoxLayout* inputBarLayout = new QHBoxLayout();
inputBarLayout->setContentsMargins(0,0,0,0);
inputBarLayout->setSpacing(2);
input_ = new QtTextEdit(settings_, this);
input_->setAcceptRichText(false);
inputBarLayout->addWidget(midBar_);
inputBarLayout->addWidget(input_);
correctingLabel_ = new QLabel(tr("Correcting"), this);
inputBarLayout->addWidget(correctingLabel_);
correctingLabel_->hide();
connect(input_, SIGNAL(receivedFocus()), this, SLOT(handleTextInputReceivedFocus()));
connect(input_, SIGNAL(lostFocus()), this, SLOT(handleTextInputLostFocus()));
- QPushButton* emoticonsButton_ = new QPushButton(this);
- emoticonsButton_->setIcon(QIcon(":/emoticons/smile.png"));
- connect(emoticonsButton_, SIGNAL(clicked()), this, SLOT(handleEmoticonsButtonClicked()));
-
- emoticonsMenu_ = new QMenu(this);
- QtEmoticonsGrid* emoticonsGrid = new QtEmoticonsGrid(emoticons, emoticonsMenu_);
- connect(emoticonsGrid, SIGNAL(emoticonClicked(QString)), this, SLOT(handleEmoticonClicked(QString)));
+ QPushButton* emojisButton_ = new QPushButton(this);
+ emojisButton_->setText("\xF0\x9F\x98\x83");
+ connect(emojisButton_, SIGNAL(clicked()), this, SLOT(handleEmojisButtonClicked()));
// using an extra layout to work around Qt margin glitches on OS X
QHBoxLayout* actionLayout = new QHBoxLayout();
- actionLayout->addWidget(emoticonsButton_);
+ actionLayout->addWidget(emojisButton_);
actionLayout->addWidget(actionButton_);
inputBarLayout->addLayout(actionLayout);
layout->addLayout(inputBarLayout);
inputClearing_ = false;
contactIsTyping_ = false;
tabCompletion_ = false;
connect(input_, SIGNAL(unhandledKeyPressEvent(QKeyEvent*)), this, SLOT(handleKeyPressEvent(QKeyEvent*)));
connect(input_, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
connect(input_, SIGNAL(textChanged()), this, SLOT(handleInputChanged()));
connect(input_, SIGNAL(cursorPositionChanged()), this, SLOT(handleCursorPositionChanged()));
setFocusProxy(input_);
logRosterSplitter_->setFocusProxy(input_);
midBar_->setFocusProxy(input_);
messageLog_->setFocusProxy(input_);
connect(messageLog_, SIGNAL(gotFocus()), input_, SLOT(setFocus()));
resize(400,300);
connect(messageLog_, SIGNAL(fontResized(int)), this, SIGNAL(fontResized(int)));
connect(messageLog_, SIGNAL(logCleared()), this, SLOT(handleLogCleared()));
treeWidget_->onSomethingSelectedChanged.connect(boost::bind(&QtChatWindow::handleOccupantSelectionChanged, this, _1));
treeWidget_->onOccupantActionSelected.connect(boost::bind(boost::ref(onOccupantActionSelected), _1, _2));
settings_->onSettingChanged.connect(boost::bind(&QtChatWindow::handleSettingChanged, this, _1));
messageLog_->showEmoticons(settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS));
setMinimumSize(100, 100);
}
@@ -636,69 +633,85 @@ void QtChatWindow::dropEvent(QDropEvent *event) {
}
}
else if (event->mimeData()->hasFormat("application/vnd.swift.contact-jid-list")) {
std::vector<JID> invites = jidListFromQByteArray(event->mimeData()->data("application/vnd.swift.contact-jid-list"));
onInviteToChat(invites);
}
}
std::vector<JID> QtChatWindow::jidListFromQByteArray(const QByteArray& dataBytes) {
QDataStream dataStream(dataBytes);
std::vector<JID> invites;
while (!dataStream.atEnd()) {
QString jidString;
dataStream >> jidString;
invites.push_back(Q2PSTRING(jidString));
}
return invites;
}
void QtChatWindow::setAvailableOccupantActions(const std::vector<OccupantAction>& actions) {
treeWidget_->setAvailableOccupantActions(actions);
}
void QtChatWindow::setSubject(const std::string& subject) {
//subject_->setVisible(!subject.empty());
subject_->setText(P2QSTRING(subject));
subject_->setToolTip(P2QSTRING(subject));
subject_->setCursorPosition(0);
}
-void QtChatWindow::handleEmoticonsButtonClicked() {
- emoticonsMenu_->adjustSize();
- QSize menuSize = emoticonsMenu_->size();
- emoticonsMenu_->exec(QPoint(QCursor::pos().x() - menuSize.width(), QCursor::pos().y() - menuSize.height()));
+void QtChatWindow::handleEmojisButtonClicked() {
+ // Create QtEmojisSelector and QMenu
+ emojisGrid_ = new QtEmojisSelector(qtOnlySettings_->getQSettings());
+ auto emojisLayout = new QVBoxLayout();
+ emojisLayout->setContentsMargins(style()->pixelMetric(QStyle::PM_MenuHMargin),style()->pixelMetric(QStyle::PM_MenuVMargin),
+ style()->pixelMetric(QStyle::PM_MenuHMargin),style()->pixelMetric(QStyle::PM_MenuVMargin));
+ emojisLayout->addWidget(emojisGrid_);
+ emojisMenu_ = std::unique_ptr<QMenu>(new QMenu());
+ emojisMenu_->setLayout(emojisLayout);
+ emojisMenu_->adjustSize();
+
+ connect(emojisGrid_, SIGNAL(emojiClicked(QString)), this, SLOT(handleEmojiClicked(QString)));
+
+ QSize menuSize = emojisMenu_->size();
+ emojisMenu_->exec(QPoint(QCursor::pos().x() - menuSize.width(), QCursor::pos().y() - menuSize.height()));
}
-void QtChatWindow::handleEmoticonClicked(QString emoticonAsText) {
- input_->textCursor().insertText(emoticonAsText);
- input_->setFocus();
+void QtChatWindow::handleEmojiClicked(QString emoji) {
+ if (isVisible()) {
+ input_->textCursor().insertText(emoji);
+ input_->setFocus();
+ // The next line also deletes the emojisGrid_, as it was added to the
+ // layout of the emojisMenu_.
+ emojisMenu_.reset();
+ }
}
void QtChatWindow::handleTextInputReceivedFocus() {
lastLineTracker_.setHasFocus(true);
input_->setFocus();
onAllMessagesRead();
}
void QtChatWindow::handleTextInputLostFocus() {
lastLineTracker_.setHasFocus(false);
}
void QtChatWindow::handleActionButtonClicked() {
QMenu contextMenu;
QAction* changeSubject = nullptr;
QAction* configure = nullptr;
QAction* affiliations = nullptr;
QAction* destroy = nullptr;
QAction* invite = nullptr;
QAction* block = nullptr;
QAction* unblock = nullptr;
if (availableRoomActions_.empty()) {
if (blockingState_ == IsBlocked) {
unblock = contextMenu.addAction(tr("Unblock"));
unblock->setEnabled(isOnline_);
}
else if (!isMUC_ && blockingState_ == IsUnblocked) {
block = contextMenu.addAction(tr("Block"));
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index 08ad7ad..4c64c84 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -1,235 +1,240 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
#include <map>
#include <QMap>
#include <QMenu>
#include <QPointer>
#include <QString>
#include <QTextCursor>
+#include <QVBoxLayout>
#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
+#include <SwifTools/EmojiMapper.h>
#include <SwifTools/LastLineTracker.h>
#include <Swift/QtUI/ChatSnippet.h>
#include <Swift/QtUI/QtAffiliationEditor.h>
+#include <Swift/QtUI/QtEmojisSelector.h>
#include <Swift/QtUI/QtMUCConfigurationWindow.h>
#include <Swift/QtUI/QtSwiftUtil.h>
#include <Swift/QtUI/QtTabbable.h>
class QTextEdit;
class QLineEdit;
class QComboBox;
class QLabel;
class QSplitter;
class QPushButton;
namespace Swift {
class QtChatView;
class QtOccupantListWidget;
class QtChatTheme;
class TreeWidget;
class QtTextEdit;
class UIEventStream;
class QtChatWindowJSBridge;
class SettingsProvider;
- class QtEmoticonsGrid;
+ class QtSettingsProvider;
class LabelModel : public QAbstractListModel {
Q_OBJECT
public:
LabelModel(QObject* parent = nullptr) : QAbstractListModel(parent) {}
virtual int rowCount(const QModelIndex& /*index*/) const {
return static_cast<int>(availableLabels_.size());
}
virtual QVariant data(const QModelIndex& index, int role) const {
if (!index.isValid()) {
return QVariant();
}
std::shared_ptr<SecurityLabel> label = availableLabels_[index.row()].getLabel();
if (label && role == Qt::TextColorRole) {
return P2QSTRING(label->getForegroundColor());
}
if (label && role == Qt::TextColorRole) {
return P2QSTRING(label->getBackgroundColor());
}
if (role == Qt::DisplayRole) {
std::string selector = availableLabels_[index.row()].getSelector();
std::string displayMarking = label ? label->getDisplayMarking() : "";
QString labelName = selector.empty() ? displayMarking.c_str() : selector.c_str();
return labelName;
}
return QVariant();
}
std::vector<SecurityLabelsCatalog::Item> availableLabels_;
};
class QtChatWindow : public QtTabbable, public ChatWindow {
Q_OBJECT
public:
- QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, const std::map<std::string, std::string>& emoticons);
+ QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QtSettingsProvider* qtOnlySettings);
virtual ~QtChatWindow();
std::string addMessage(const ChatMessage& message, const std::string &senderName, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time);
std::string addAction(const ChatMessage& message, const std::string &senderName, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time);
std::string addSystemMessage(const ChatMessage& message, Direction direction);
void addPresenceMessage(const ChatMessage& message, Direction direction);
void addErrorMessage(const ChatMessage& message);
void replaceMessage(const ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time);
void replaceWithAction(const ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time);
// File transfer related stuff
std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes, const std::string& description);
void setFileTransferProgress(std::string id, const int percentageDone);
void setFileTransferStatus(std::string id, const FileTransferState state, const std::string& msg);
std::string addWhiteboardRequest(bool senderIsSelf);
void setWhiteboardSessionStatus(std::string id, const ChatWindow::WhiteboardSessionState state);
void show();
bool isVisible() const;
void activate();
void setUnreadMessageCount(int count);
void convertToMUC(MUCType mucType);
// TreeWidget *getTreeWidget();
void setAvailableSecurityLabels(const std::vector<SecurityLabelsCatalog::Item>& labels);
void setSecurityLabelsEnabled(bool enabled);
void setSecurityLabelsError();
SecurityLabelsCatalog::Item getSelectedSecurityLabel();
void setName(const std::string& name);
void setOnline(bool online);
QtTabbable::AlertType getWidgetAlertState();
void setContactChatState(ChatState::ChatStateType state);
void setRosterModel(Roster* roster);
void setTabComplete(TabComplete* completer);
int getCount();
virtual void replaceSystemMessage(const ChatMessage& message, const std::string& id, const TimestampBehaviour timestampBehaviour);
void replaceLastMessage(const ChatMessage& message, const TimestampBehaviour timestampBehaviour);
void setAckState(const std::string& id, AckState state);
// message receipts
void setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state);
void flash();
QByteArray getSplitterState();
virtual void setAvailableOccupantActions(const std::vector<OccupantAction>& actions);
void setSubject(const std::string& subject);
void showRoomConfigurationForm(Form::ref);
void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct = true, bool isImpromptu = false, bool isContinuation = false);
void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&);
void setAvailableRoomActions(const std::vector<RoomAction>& actions);
void setBlockingState(BlockingState state);
virtual void setCanInitiateImpromptuChats(bool supportsImpromptu);
virtual void showBookmarkWindow(const MUCBookmark& bookmark);
virtual void setBookmarkState(RoomBookmarkState bookmarkState);
virtual std::string getID() const;
virtual void setEmphasiseFocus(bool emphasise);
public slots:
void handleChangeSplitterState(QByteArray state);
+ void handleEmojiClicked(QString emoji);
void handleFontResized(int fontSizeSteps);
AlertID addAlert(const std::string& alertText);
void removeAlert(const AlertID id);
void setCorrectionEnabled(Tristate enabled);
void setFileTransferEnabled(Tristate enabled);
signals:
void geometryChanged();
void splitterMoved();
void fontResized(int);
protected slots:
void closeEvent(QCloseEvent* event);
void resizeEvent(QResizeEvent* event);
void moveEvent(QMoveEvent* event);
void dragEnterEvent(QDragEnterEvent *event);
void dropEvent(QDropEvent *event);
protected:
void showEvent(QShowEvent* event);
private slots:
void handleLogCleared();
void returnPressed();
void handleInputChanged();
void handleCursorPositionChanged();
void handleKeyPressEvent(QKeyEvent* event);
void handleSplitterMoved(int pos, int index);
void handleAlertButtonClicked();
void handleActionButtonClicked();
void handleAffiliationEditorAccepted();
void handleCurrentLabelChanged(int);
- void handleEmoticonsButtonClicked();
- void handleEmoticonClicked(QString emoticonAsText);
+ void handleEmojisButtonClicked();
void handleTextInputReceivedFocus();
void handleTextInputLostFocus();
private:
void updateTitleWithUnreadCount();
void tabComplete();
void beginCorrection();
void cancelCorrection();
void handleSettingChanged(const std::string& setting);
void handleOccupantSelectionChanged(RosterItem* item);
void handleAppendedToLog();
static std::vector<JID> jidListFromQByteArray(const QByteArray& dataBytes);
private:
int unreadCount_;
bool contactIsTyping_;
LastLineTracker lastLineTracker_;
std::string id_;
QString contact_;
QString lastSentMessage_;
QTextCursor tabCompleteCursor_;
QtChatView* messageLog_;
QtChatTheme* theme_;
QtTextEdit* input_;
QWidget* midBar_;
QBoxLayout* subjectLayout_;
QComboBox* labelsWidget_;
QtOccupantListWidget* treeWidget_;
QLabel* correctingLabel_;
boost::optional<AlertID> correctingAlert_;
QVBoxLayout* alertLayout_;
std::map<AlertID, QWidget*> alertWidgets_;
AlertID nextAlertId_;
TabComplete* completer_;
QLineEdit* subject_;
bool isCorrection_;
bool inputClearing_;
bool tabCompletion_;
UIEventStream* eventStream_;
bool isOnline_;
QSplitter *logRosterSplitter_;
Tristate correctionEnabled_;
Tristate fileTransferEnabled_;
QString alertStyleSheet_;
QPointer<QtMUCConfigurationWindow> mucConfigurationWindow_;
QPointer<QtAffiliationEditor> affiliationEditor_;
- SettingsProvider* settings_;
+ SettingsProvider* settings_ = nullptr;
+ QtSettingsProvider* qtOnlySettings_ = nullptr;
std::vector<ChatWindow::RoomAction> availableRoomActions_;
QPalette defaultLabelsPalette_;
LabelModel* labelModel_;
BlockingState blockingState_;
bool impromptu_;
bool isMUC_;
bool supportsImpromptuChat_;
RoomBookmarkState roomBookmarkState_;
- QMenu* emoticonsMenu_;
+ std::unique_ptr<QMenu> emojisMenu_;
+ QPointer<QtEmojisSelector> emojisGrid_;
};
}
diff --git a/Swift/QtUI/QtChatWindowFactory.cpp b/Swift/QtUI/QtChatWindowFactory.cpp
index 9c59e9a..5a4a501 100644
--- a/Swift/QtUI/QtChatWindowFactory.cpp
+++ b/Swift/QtUI/QtChatWindowFactory.cpp
@@ -1,86 +1,88 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swift/QtUI/QtChatWindowFactory.h>
#include <QDesktopWidget>
-#include <qdebug.h>
+#include <SwifTools/EmojiMapper.h>
#include <Swift/QtUI/QtChatTabs.h>
#include <Swift/QtUI/QtChatTabsBase.h>
#include <Swift/QtUI/QtChatTheme.h>
#include <Swift/QtUI/QtChatWindow.h>
+#include <Swift/QtUI/QtEmojisSelector.h>
#include <Swift/QtUI/QtSingleWindow.h>
#include <Swift/QtUI/QtSwiftUtil.h>
namespace Swift {
static const QString SPLITTER_STATE = "mucSplitterState";
static const QString CHAT_TABS_GEOMETRY = "chatTabsGeometry";
-QtChatWindowFactory::QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabsBase* tabs, const QString& themePath, const std::map<std::string, std::string>& emoticons) : themePath_(themePath), emoticons_(emoticons) {
+QtChatWindowFactory::QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabsBase* tabs, const QString& themePath) : themePath_(themePath) {
qtOnlySettings_ = qtSettings;
settings_ = settings;
tabs_ = tabs;
theme_ = nullptr;
QtChatTabs* fullTabs = dynamic_cast<QtChatTabs*>(tabs_);
if (splitter) {
assert(fullTabs && "Netbook mode and no-tabs interface is not supported!");
splitter->addWidget(fullTabs);
} else if (fullTabs) {
QVariant chatTabsGeometryVariant = qtOnlySettings_->getQSettings()->value(CHAT_TABS_GEOMETRY);
if (chatTabsGeometryVariant.isValid()) {
fullTabs->restoreGeometry(chatTabsGeometryVariant.toByteArray());
}
connect(fullTabs, SIGNAL(geometryChanged()), this, SLOT(handleWindowGeometryChanged()));
}
}
QtChatWindowFactory::~QtChatWindowFactory() {
delete theme_;
}
ChatWindow* QtChatWindowFactory::createChatWindow(const JID &contact,UIEventStream* eventStream) {
if (!theme_) {
theme_ = new QtChatTheme(themePath_);
if (theme_->getIncomingContent().isEmpty()) {
delete theme_;
theme_ = new QtChatTheme(":/themes/Default/"); /* Use the inbuilt theme */
}
}
- QtChatWindow* chatWindow = new QtChatWindow(P2QSTRING(contact.toString()), theme_, eventStream, settings_, emoticons_);
+ QtChatWindow* chatWindow = new QtChatWindow(P2QSTRING(contact.toString()), theme_, eventStream, settings_, qtOnlySettings_);
+
connect(chatWindow, SIGNAL(splitterMoved()), this, SLOT(handleSplitterMoved()));
connect(this, SIGNAL(changeSplitterState(QByteArray)), chatWindow, SLOT(handleChangeSplitterState(QByteArray)));
QVariant splitterState = qtOnlySettings_->getQSettings()->value(SPLITTER_STATE);
if(splitterState.isValid()) {
chatWindow->handleChangeSplitterState(splitterState.toByteArray());
}
if (tabs_) {
tabs_->addTab(chatWindow);
} else {
QVariant chatGeometryVariant = qtOnlySettings_->getQSettings()->value(CHAT_TABS_GEOMETRY);
if (chatGeometryVariant.isValid()) {
chatWindow->restoreGeometry(chatGeometryVariant.toByteArray());
}
connect(chatWindow, SIGNAL(geometryChanged()), this, SLOT(handleWindowGeometryChanged()));
}
return chatWindow;
}
void QtChatWindowFactory::handleWindowGeometryChanged() {
qtOnlySettings_->getQSettings()->setValue(CHAT_TABS_GEOMETRY, qobject_cast<QWidget*>(sender())->saveGeometry());
}
void QtChatWindowFactory::handleSplitterMoved() {
QByteArray splitterState = qobject_cast<QtChatWindow*>(sender())->getSplitterState();
qtOnlySettings_->getQSettings()->setValue(SPLITTER_STATE, QVariant(splitterState));
emit changeSplitterState(splitterState);
}
diff --git a/Swift/QtUI/QtChatWindowFactory.h b/Swift/QtUI/QtChatWindowFactory.h
index 29bc5bc..a217215 100644
--- a/Swift/QtUI/QtChatWindowFactory.h
+++ b/Swift/QtUI/QtChatWindowFactory.h
@@ -1,47 +1,47 @@
/*
- * Copyright (c) 2010-2015 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
-#include <map>
-#include <string>
-
+#include <QMenu>
#include <QObject>
#include <QSplitter>
+#include <QVBoxLayout>
#include <Swiften/JID/JID.h>
#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
+#include <SwifTools/EmojiMapper.h>
+
+#include <Swift/QtUI/QtEmojisSelector.h>
#include <Swift/QtUI/QtSettingsProvider.h>
namespace Swift {
class QtChatTabsBase;
class QtChatTheme;
class UIEventStream;
class QtUIPreferences;
class QtSingleWindow;
class QtChatWindowFactory : public QObject, public ChatWindowFactory {
Q_OBJECT
public:
- QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabsBase* tabs, const QString& themePath, const std::map<std::string, std::string>& emoticons);
+ QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabsBase* tabs, const QString& themePath);
~QtChatWindowFactory();
ChatWindow* createChatWindow(const JID &contact, UIEventStream* eventStream);
signals:
void changeSplitterState(QByteArray);
private slots:
void handleWindowGeometryChanged();
void handleSplitterMoved();
private:
QString themePath_;
SettingsProvider* settings_;
QtSettingsProvider* qtOnlySettings_;
QtChatTabsBase* tabs_;
QtChatTheme* theme_;
- std::map<std::string, std::string> emoticons_;
};
}
-
diff --git a/Swift/QtUI/QtEmojiCell.cpp b/Swift/QtUI/QtEmojiCell.cpp
new file mode 100644
index 0000000..4932ece
--- /dev/null
+++ b/Swift/QtUI/QtEmojiCell.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/QtEmojiCell.h>
+
+#include <QFont>
+#include <QFontMetrics>
+#include <QPushButton>
+#include <QString>
+
+#include <SwifTools/EmojiMapper.h>
+
+namespace Swift {
+ QtEmojiCell::QtEmojiCell(QString shortname, QString text, QWidget* parent) : QPushButton(parent) {
+ setText(text);
+ QFont font = this->font();
+ font.setPointSize(22);
+ font.setBold(true);
+ setFont(font);
+
+ setFixedWidth(fontMetrics().width("\xF0\x9F\x98\x83")+5);
+
+ setFlat(true);
+ setToolTip(shortname);
+ connect(this, SIGNAL(clicked()), this, SLOT(handleEmojiClicked()));
+ }
+
+ QtEmojiCell::QtEmojiCell(const QtEmojiCell& b) : QtEmojiCell(b.toolTip(), b.text()) {
+ }
+
+ QtEmojiCell::~QtEmojiCell() {
+ }
+
+ void QtEmojiCell::handleEmojiClicked () {
+ emit emojiClicked(text());
+ }
+
+}
diff --git a/Swift/QtUI/QtEmojiCell.h b/Swift/QtUI/QtEmojiCell.h
new file mode 100644
index 0000000..62f989b
--- /dev/null
+++ b/Swift/QtUI/QtEmojiCell.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <QPushButton>
+#include <QString>
+
+#include <SwifTools/EmojiMapper.h>
+
+class QWidget;
+class QMouseEvent;
+
+namespace Swift {
+ class QtEmojiCell : public QPushButton {
+ Q_OBJECT
+ public:
+ QtEmojiCell(const QtEmojiCell& b);
+ QtEmojiCell(QString shortname, QString text, QWidget* parent = nullptr);
+ ~QtEmojiCell();
+ signals:
+ void emojiClicked(QString emojiAsText);
+
+ private slots:
+ void handleEmojiClicked();
+ };
+}
diff --git a/Swift/QtUI/QtEmojisGrid.cpp b/Swift/QtUI/QtEmojisGrid.cpp
new file mode 100644
index 0000000..eadc64f
--- /dev/null
+++ b/Swift/QtUI/QtEmojisGrid.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/QtEmojisGrid.h>
+
+#include <string>
+
+#include <QString>
+#include <QVector>
+
+#include <SwifTools/EmojiMapper.h>
+
+#include <Swift/QtUI/QtEmojiCell.h>
+#include <Swift/QtUI/QtRecentEmojisGrid.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+QtEmojisGrid::QtEmojisGrid() {
+
+}
+
+QtEmojisGrid::QtEmojisGrid(QString categoryName) {
+ auto category = EmojiMapper::categoryNameToEmojis(Q2PSTRING(categoryName));
+
+ QVector<QString> categoryEmojis;
+
+ for (const auto& emoji : category) {
+ categoryEmojis.push_back(P2QSTRING(emoji));
+ }
+
+ setEmojis(categoryEmojis);
+ }
+
+ void QtEmojisGrid::setEmojis(const QVector<QString>& emojis) {
+ clearEmojis();
+
+ int iEmoji = 0;
+ for (const auto& unicodeEmoji : emojis) {
+ QString shortname = QString::fromStdString(EmojiMapper::unicodeToShortname(Q2PSTRING(unicodeEmoji)));
+ QtEmojiCell* emoji = new QtEmojiCell(shortname, unicodeEmoji);
+ this->addWidget(emoji, iEmoji/6, iEmoji%6);
+ connect(emoji, SIGNAL(emojiClicked(QString)), this, SIGNAL(onEmojiSelected(QString)));
+ iEmoji++;
+ }
+ for (int index = 0; index < columnCount(); index++) {
+ auto layoutItem = itemAtPosition(0, index);
+ if (layoutItem) {
+ auto cellWidget = layoutItem->widget();
+ if (cellWidget) {
+ setColumnMinimumWidth(index, cellWidget->width());
+ }
+ }
+ }
+ setSpacing(5);
+ }
+
+ void QtEmojisGrid::clearEmojis() {
+ QLayoutItem* child = nullptr;
+ while ((child = this->takeAt(0)) != 0) {
+ if (child->widget()) {
+ child->widget()->hide();
+ removeWidget(child->widget());
+ }
+ else {
+ removeItem(child);
+ }
+ }
+ }
+}
diff --git a/Swift/QtUI/QtEmojisGrid.h b/Swift/QtUI/QtEmojisGrid.h
new file mode 100644
index 0000000..e4bc664
--- /dev/null
+++ b/Swift/QtUI/QtEmojisGrid.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <QGridLayout>
+#include <QString>
+#include <QVector>
+
+#include <SwifTools/EmojiMapper.h>
+
+namespace Swift {
+ class QtEmojisGrid : public QGridLayout {
+ Q_OBJECT
+ public:
+ explicit QtEmojisGrid();
+ explicit QtEmojisGrid(QString categoryName);
+
+ protected:
+ void setEmojis(const QVector<QString>& emojis);
+
+ private:
+ void clearEmojis();
+
+ signals:
+ void onEmojiSelected(QString emoji);
+ };
+}
diff --git a/Swift/QtUI/QtEmojisScroll.cpp b/Swift/QtUI/QtEmojisScroll.cpp
new file mode 100644
index 0000000..3e9969b
--- /dev/null
+++ b/Swift/QtUI/QtEmojisScroll.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/QtEmojisScroll.h>
+
+#include <QLayout>
+#include <QScrollArea>
+#include <QStyle>
+
+#include <Swift/QtUI/QtEmojisGrid.h>
+#include <Swift/QtUI/QtRecentEmojisGrid.h>
+
+namespace Swift {
+ QtEmojisScroll::QtEmojisScroll(QGridLayout* emojiLayout, QWidget *parent) : QWidget(parent) {
+ auto selector = new QWidget();
+ auto scrollArea = new QScrollArea();
+ scrollArea->setWidgetResizable(true);
+ scrollArea->setWidget(selector);
+ // Set minimum width to fit GridLayout (no horizontal ScrollBar)
+ const int margin = style()->pixelMetric(QStyle::PM_MenuHMargin) * 2;
+ scrollArea->setMinimumWidth((emojiLayout->columnCount()+1)*(emojiLayout->columnMinimumWidth(0)+emojiLayout->spacing())+margin);
+ // Set height according to width (better ratio)
+ const double ratio = 16.0/9.0; //ratio width/height
+ scrollArea->setMinimumHeight(scrollArea->minimumWidth()/ratio);
+ selector->setLayout(emojiLayout);
+
+ this->setLayout(new QVBoxLayout);
+ this->layout()->addWidget(scrollArea);
+ this->layout()->setContentsMargins(0,0,0,0);
+ }
+}
diff --git a/Swift/QtUI/QtEmojisScroll.h b/Swift/QtUI/QtEmojisScroll.h
new file mode 100644
index 0000000..8ee9257
--- /dev/null
+++ b/Swift/QtUI/QtEmojisScroll.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <QGridLayout>
+#include <QWidget>
+
+namespace Swift {
+ class QtEmojisScroll : public QWidget {
+ Q_OBJECT
+ public:
+ QtEmojisScroll(QGridLayout* emojiLayout, QWidget *parent = 0);
+ };
+}
diff --git a/Swift/QtUI/QtEmojisSelector.cpp b/Swift/QtUI/QtEmojisSelector.cpp
new file mode 100644
index 0000000..8b21380
--- /dev/null
+++ b/Swift/QtUI/QtEmojisSelector.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/QtEmojisSelector.h>
+
+#include <QScrollArea>
+#include <QSettings>
+#include <QString>
+#include <QWidget>
+
+#include <SwifTools/EmojiMapper.h>
+
+#include <Swift/QtUI/QtEmojisGrid.h>
+#include <Swift/QtUI/QtEmojisScroll.h>
+#include <Swift/QtUI/QtRecentEmojisGrid.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+ QtEmojisSelector::QtEmojisSelector(QSettings* settings, QWidget* parent) : QTabWidget(parent), settings_(settings) {
+ recentEmojisGrid_ = addRecentTab();
+ connect(recentEmojisGrid_, SIGNAL(onEmojiSelected(QString)), this, SLOT(emojiClickedSlot(QString)));
+
+ for (const auto& category : EmojiMapper::getCategories()) {
+ if (category != "modifier") {
+ QtEmojisGrid* grid = addTab(P2QSTRING(category));
+ connect(grid, SIGNAL(onEmojiSelected(QString)), this, SLOT(emojiClickedSlot(QString)));
+ }
+ }
+
+ loadSettings();
+ }
+
+ QtEmojisSelector::~QtEmojisSelector() {
+ writeSettings();
+ }
+
+ QtRecentEmojisGrid* QtEmojisSelector::addRecentTab() {
+ QtRecentEmojisGrid* recent = new QtRecentEmojisGrid(settings_);
+ QtEmojisScroll* scroll = new QtEmojisScroll(recent);
+ QTabWidget::addTab(scroll, QString::fromStdString(EmojiMapper::categoryToFlagshipUnicodeEmoji("recent")));
+
+ setTabToolTip(count()-1, tr("Recent"));
+
+ return recent;
+ }
+
+ QtEmojisGrid* QtEmojisSelector::addTab(QString categoryName) {
+ QtEmojisGrid* grid = new QtEmojisGrid(categoryName);
+ QtEmojisScroll* scroll = new QtEmojisScroll(grid);
+ QTabWidget::addTab(scroll, QString::fromStdString(EmojiMapper::categoryToFlagshipUnicodeEmoji(Q2PSTRING(categoryName))));
+ setTabToolTip(count()-1, categoryName.replace(0, 1, categoryName[0].toUpper()));
+
+ return grid;
+ }
+
+ void QtEmojisSelector::loadSettings() {
+ if (settings_->contains("currentEmojiTab")) {
+ setCurrentIndex(settings_->value("currentEmojiTab").toInt());
+ } else {
+ setCurrentIndex(1); //index of people category
+ }
+ }
+
+ void QtEmojisSelector::writeSettings() {
+ settings_->setValue("currentEmojiTab", currentIndex());
+ }
+
+ void QtEmojisSelector::emojiClickedSlot(QString emoji) {
+ recentEmojisGrid_->handleEmojiClicked(emoji);
+ emit emojiClicked(emoji);
+ }
+}
diff --git a/Swift/QtUI/QtEmojisSelector.h b/Swift/QtUI/QtEmojisSelector.h
new file mode 100644
index 0000000..1065aa0
--- /dev/null
+++ b/Swift/QtUI/QtEmojisSelector.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <QSettings>
+#include <QTabWidget>
+
+#include <Swift/QtUI/QtEmojisGrid.h>
+#include <Swift/QtUI/QtRecentEmojisGrid.h>
+
+namespace Swift {
+
+class QtEmojisSelector : public QTabWidget {
+ Q_OBJECT
+ public:
+ QtEmojisSelector(QSettings* settings, QWidget * parent = 0);
+ ~QtEmojisSelector();
+
+ public slots:
+ void emojiClickedSlot(QString emoji);
+
+ signals:
+ void emojiClicked(QString emoji);
+
+ private:
+ QtRecentEmojisGrid* addRecentTab();
+ QtEmojisGrid* addTab(QString categoryName);
+ void loadSettings();
+ void writeSettings();
+
+ private:
+ QSettings* settings_ = nullptr;
+ QtRecentEmojisGrid* recentEmojisGrid_ = nullptr;
+};
+
+}
diff --git a/Swift/QtUI/QtEmoticonCell.cpp b/Swift/QtUI/QtEmoticonCell.cpp
deleted file mode 100644
index fe580aa..0000000
--- a/Swift/QtUI/QtEmoticonCell.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2015 Daniel Baczynski
- * Licensed under the Simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-/*
- * Copyright (c) 2016 Isode Limited.
- * All rights reserved.
- * See the COPYING file for more information.
- */
-
-#include <Swift/QtUI/QtEmoticonCell.h>
-
-namespace Swift {
-
-QtEmoticonCell::QtEmoticonCell(const QString emoticonAsText, QString filePath, QWidget* parent) : QLabel(parent), emoticonAsText_(emoticonAsText) {
- if (filePath.startsWith("qrc:/")) {
- filePath.remove(0, 3);
- }
- setPixmap(QPixmap(filePath));
- setToolTip(emoticonAsText_);
-}
-
-QtEmoticonCell::~QtEmoticonCell() {
-
-}
-
-void QtEmoticonCell::mousePressEvent (QMouseEvent* event) {
- emit emoticonClicked(emoticonAsText_);
- QLabel::mousePressEvent(event);
-}
-
-}
-
-
diff --git a/Swift/QtUI/QtEmoticonCell.h b/Swift/QtUI/QtEmoticonCell.h
deleted file mode 100644
index 669d1ed..0000000
--- a/Swift/QtUI/QtEmoticonCell.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2015 Daniel Baczynski
- * Licensed under the Simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-/*
- * Copyright (c) 2016 Isode Limited.
- * All rights reserved.
- * See the COPYING file for more information.
- */
-
-#pragma once
-
-#include <map>
-#include <string>
-
-#include <QLabel>
-#include <QPixmap>
-#include <QString>
-
-class QWidget;
-class QMouseEvent;
-
-namespace Swift {
-
- class QtEmoticonCell : public QLabel {
- Q_OBJECT
- public:
- QtEmoticonCell(const QString emoticonAsText, QString filePath, QWidget* parent = nullptr);
- ~QtEmoticonCell();
- virtual void mousePressEvent(QMouseEvent* event);
-
- signals:
- void emoticonClicked(QString emoticonAsText);
-
- private:
- QString emoticonAsText_;
- };
-}
diff --git a/Swift/QtUI/QtEmoticonsGrid.cpp b/Swift/QtUI/QtEmoticonsGrid.cpp
deleted file mode 100644
index 4c8c024..0000000
--- a/Swift/QtUI/QtEmoticonsGrid.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2015 Daniel Baczynski
- * Licensed under the Simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-/*
- * Copyright (c) 2016 Isode Limited.
- * All rights reserved.
- * See the COPYING file for more information.
- */
-
-#include <Swift/QtUI/QtEmoticonsGrid.h>
-
-#include <set>
-
-#include <boost/range/adaptor/reversed.hpp>
-
-#include <QPushButton>
-
-#include <Swift/QtUI/QtSwiftUtil.h>
-
-namespace Swift {
-
-QtEmoticonsGrid::QtEmoticonsGrid(const std::map<std::string, std::string>& emoticons, QWidget* parent) : QGridLayout(parent) {
- makeUniqueEmoticonsMap(emoticons);
-
- // Create grid: 3 columns, [uniqueEmoticons_.size()/3] rows
- int row = 0;
- int column = 0;
-
- for (auto&& emoticon : uniqueEmoticons_) {
- QtEmoticonCell* newCell = new QtEmoticonCell(P2QSTRING(emoticon.first), P2QSTRING(emoticon.second));
- addWidget(newCell, row, column);
- connect(newCell, SIGNAL(emoticonClicked(QString)), this, SLOT(emoticonClickedSlot(QString)));
-
- column++;
- if (column >= 3) {
- column = 0;
- row++;
- }
- }
-}
-
-QtEmoticonsGrid::~QtEmoticonsGrid() {
-
-}
-
-void QtEmoticonsGrid::makeUniqueEmoticonsMap(const std::map<std::string, std::string>& emoticons) {
- std::set<std::string> paths;
- for (auto&& emoticon : boost::adaptors::reverse(emoticons)) {
- if (paths.find(emoticon.second) == paths.end()) {
- uniqueEmoticons_.insert(emoticon);
- paths.insert(emoticon.second);
- }
- }
-}
-
-void QtEmoticonsGrid::emoticonClickedSlot(QString emoticonAsText) {
- emit emoticonClicked(emoticonAsText);
-}
-
-}
diff --git a/Swift/QtUI/QtEmoticonsGrid.h b/Swift/QtUI/QtEmoticonsGrid.h
deleted file mode 100644
index b3b8ac3..0000000
--- a/Swift/QtUI/QtEmoticonsGrid.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2015 Daniel Baczynski
- * Licensed under the Simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-/*
- * Copyright (c) 2016 Isode Limited.
- * All rights reserved.
- * See the COPYING file for more information.
- */
-
-#pragma once
-
-#include <map>
-#include <string>
-
-#include <QGridLayout>
-#include <QLabel>
-#include <QSize>
-#include <QString>
-
-#include <Swift/QtUI/QtEmoticonCell.h>
-
-class QWidget;
-
-namespace Swift {
-
- class QtEmoticonsGrid : public QGridLayout {
- Q_OBJECT
- public:
- explicit QtEmoticonsGrid(const std::map<std::string, std::string>& emoticons, QWidget* parent = nullptr);
- virtual ~QtEmoticonsGrid();
-
- signals:
- void emoticonClicked(QString emoticonAsText);
-
- public slots:
- void emoticonClickedSlot(QString emoticonAsText);
-
- private:
- void makeUniqueEmoticonsMap(const std::map<std::string, std::string>& emoticons);
-
- std::map<std::string, std::string> uniqueEmoticons_;
- };
-}
diff --git a/Swift/QtUI/QtRecentEmojisGrid.cpp b/Swift/QtUI/QtRecentEmojisGrid.cpp
new file mode 100644
index 0000000..54cca76
--- /dev/null
+++ b/Swift/QtUI/QtRecentEmojisGrid.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/QtUI/QtRecentEmojisGrid.h>
+
+#include <algorithm>
+
+#include <QSettings>
+#include <QVector>
+
+#include <SwifTools/EmojiMapper.h>
+
+#include <Swift/QtUI/QtEmojiCell.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+ const int QtRecentEmojisGrid::MAX_RECENTS = 50;
+
+ QtRecentEmojisGrid::QtRecentEmojisGrid(QSettings* settings) : QtEmojisGrid(), settings_(settings) {
+ loadSettings();
+ connect(this, SIGNAL(onEmojiSelected(QString)), this, SLOT(handleEmojiClicked(QString)));
+ refresh();
+ }
+
+ QtRecentEmojisGrid::~QtRecentEmojisGrid() {
+ writeSettings();
+ }
+
+ void QtRecentEmojisGrid::handleEmojiClicked(QString emoji) {
+ recents_.erase(std::remove(recents_.begin(), recents_.end(), emoji), recents_.end());
+
+ if (recents_.size() > MAX_RECENTS) {
+ recents_.resize(MAX_RECENTS - 1);
+ }
+
+ recents_.push_front(emoji);
+ refresh();
+ }
+
+ void QtRecentEmojisGrid::refresh() {
+ QtEmojisGrid::setEmojis(recents_);
+ }
+
+ void QtRecentEmojisGrid::loadSettings() {
+ QByteArray readData = settings_->value("recentEmojis").toByteArray();
+ QDataStream readStream(&readData, QIODevice::ReadOnly);
+ readStream >> recents_;
+ }
+
+ void QtRecentEmojisGrid::writeSettings() {
+ QByteArray data;
+ QDataStream stream(&data, QIODevice::WriteOnly);
+ stream << recents_;
+ settings_->setValue("recentEmojis", data);
+ }
+}
diff --git a/Swift/QtUI/QtRecentEmojisGrid.h b/Swift/QtUI/QtRecentEmojisGrid.h
new file mode 100644
index 0000000..6201b26
--- /dev/null
+++ b/Swift/QtUI/QtRecentEmojisGrid.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <QGridLayout>
+#include <QSettings>
+#include <QSize>
+#include <QString>
+#include <QVector>
+
+#include <SwifTools/EmojiMapper.h>
+
+#include <Swift/QtUI/QtEmojisGrid.h>
+
+namespace Swift {
+ class QtRecentEmojisGrid : public QtEmojisGrid {
+ Q_OBJECT
+ public:
+ explicit QtRecentEmojisGrid(QSettings* settings);
+ ~QtRecentEmojisGrid();
+
+ public slots:
+ void handleEmojiClicked(QString emoji);
+
+ private:
+ void refresh();
+ void loadSettings();
+ void writeSettings();
+
+ private:
+ static const int MAX_RECENTS;
+
+ private:
+ QVector<QString> recents_;
+ QSettings* settings_;
+ };
+}
diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp
index 3f6ce8e..ee21c4f 100644
--- a/Swift/QtUI/QtSwift.cpp
+++ b/Swift/QtUI/QtSwift.cpp
@@ -1,61 +1,62 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swift/QtUI/QtSwift.h>
#include <map>
#include <string>
#include <boost/bind.hpp>
#include <QApplication>
#include <QFile>
#include <QFontDatabase>
#include <QMap>
#include <QMessageBox>
#include <qdebug.h>
#include <Swiften/Base/Log.h>
#include <Swiften/Base/Path.h>
#include <Swiften/Base/Paths.h>
#include <Swiften/Base/Platform.h>
#include <Swiften/Client/Client.h>
#include <Swiften/Elements/Presence.h>
#include <Swiften/TLS/TLSContextFactory.h>
#include <SwifTools/Application/PlatformApplicationPathProvider.h>
#include <SwifTools/AutoUpdater/AutoUpdater.h>
#include <SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h>
+#include <SwifTools/EmojiMapper.h>
#include <Swift/Controllers/ApplicationInfo.h>
#include <Swift/Controllers/BuildVersion.h>
#include <Swift/Controllers/MainController.h>
#include <Swift/Controllers/SettingConstants.h>
#include <Swift/Controllers/Settings/SettingsProviderHierachy.h>
#include <Swift/Controllers/Settings/XMLSettingsProvider.h>
#include <Swift/Controllers/StatusCache.h>
#include <Swift/Controllers/Storages/CertificateFileStorageFactory.h>
#include <Swift/Controllers/Storages/FileStoragesFactory.h>
#include <Swift/QtUI/QtChatTabs.h>
#include <Swift/QtUI/QtChatTabsBase.h>
#include <Swift/QtUI/QtChatTabsShortcutOnlySubstitute.h>
#include <Swift/QtUI/QtChatWindowFactory.h>
#include <Swift/QtUI/QtLoginWindow.h>
#include <Swift/QtUI/QtSingleWindow.h>
#include <Swift/QtUI/QtSoundPlayer.h>
#include <Swift/QtUI/QtSwiftUtil.h>
#include <Swift/QtUI/QtSystemTray.h>
#include <Swift/QtUI/QtUIFactory.h>
#include <Swift/QtUI/QtUISettingConstants.h>
#include <Swift/QtUI/SwiftUpdateFeeds.h>
#if defined(SWIFTEN_PLATFORM_WINDOWS)
#include <Swift/QtUI/WindowsNotifier.h>
#elif defined(HAVE_GROWL)
#include <SwifTools/Notifier/GrowlNotifier.h>
#elif defined(SWIFTEN_PLATFORM_LINUX)
#include <Swift/QtUI/FreeDesktopNotifier.h>
@@ -201,61 +202,61 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
"themes/Default/Lato2OFL/Lato-Italic.ttf",
"themes/Default/Lato2OFL/Lato-Light.ttf",
"themes/Default/Lato2OFL/Lato-LightItalic.ttf",
"themes/Default/Lato2OFL/Lato-Medium.ttf",
"themes/Default/Lato2OFL/Lato-MediumItalic.ttf",
"themes/Default/Lato2OFL/Lato-Regular.ttf",
"themes/Default/Lato2OFL/Lato-Semibold.ttf",
"themes/Default/Lato2OFL/Lato-SemiboldItalic.ttf",
"themes/Default/Lato2OFL/Lato-Thin.ttf",
"themes/Default/Lato2OFL/Lato-ThinItalic.ttf"
};
for (auto&& fontName : fontNames) {
std::string fontPath = std::string(":/") + fontName;
int error = QFontDatabase::addApplicationFont(P2QSTRING(fontPath));
assert((error != -1) && "Failed to load font.");
}
bool enableAdHocCommandOnJID = options.count("enable-jid-adhocs") > 0;
tabs_ = nullptr;
if (options.count("no-tabs") && !splitter_) {
tabs_ = new QtChatTabsShortcutOnlySubstitute();
}
else {
tabs_ = new QtChatTabs(splitter_ != nullptr, settingsHierachy_, true);
}
bool startMinimized = options.count("start-minimized") > 0;
applicationPathProvider_ = new PlatformApplicationPathProvider(SWIFT_APPLICATION_NAME);
storagesFactory_ = new FileStoragesFactory(applicationPathProvider_->getDataDir(), networkFactories_.getCryptoProvider());
certificateStorageFactory_ = new CertificateFileStorageFactory(applicationPathProvider_->getDataDir(), tlsFactories_.getCertificateFactory(), networkFactories_.getCryptoProvider());
- chatWindowFactory_ = new QtChatWindowFactory(splitter_, settingsHierachy_, qtSettings_, tabs_, ":/themes/Default/", emoticons);
+ chatWindowFactory_ = new QtChatWindowFactory(splitter_, settingsHierachy_, qtSettings_, tabs_, ":/themes/Default/");
soundPlayer_ = new QtSoundPlayer(applicationPathProvider_);
// Ugly, because the dock depends on the tray, but the temporary
// multi-account hack creates one tray per account.
QtSystemTray* systemTray = new QtSystemTray();
systemTrays_.push_back(systemTray);
#if defined(HAVE_GROWL)
notifier_ = new GrowlNotifier(SWIFT_APPLICATION_NAME);
#elif defined(SWIFTEN_PLATFORM_WINDOWS)
notifier_ = new WindowsNotifier(SWIFT_APPLICATION_NAME, applicationPathProvider_->getResourcePath("/images/logo-icon-32.png"), systemTray->getQSystemTrayIcon());
#elif defined(SWIFTEN_PLATFORM_LINUX)
notifier_ = new FreeDesktopNotifier(SWIFT_APPLICATION_NAME);
#elif defined(SWIFTEN_PLATFORM_MACOSX)
notifier_ = new NotificationCenterNotifier();
#else
notifier_ = new NullNotifier();
#endif
#if defined(SWIFTEN_PLATFORM_MACOSX)
dock_ = new MacOSXDock(&cocoaApplication_);
#else
dock_ = new NullDock();
#endif
#if defined(SWIFTEN_PLATFORM_MACOSX)
uriHandler_ = new QtURIHandler();
#elif defined(SWIFTEN_PLATFORM_WIN32)
uriHandler_ = new NullURIHandler();
#else
@@ -297,57 +298,57 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
PlatformAutoUpdaterFactory autoUpdaterFactory;
if (autoUpdaterFactory.isSupported() && settingsHierachy_->getSetting(QtUISettingConstants::ENABLE_SOFTWARE_UPDATES)
&& !settingsHierachy_->getSetting(QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL).empty()) {
autoUpdater_ = autoUpdaterFactory.createAutoUpdater(updateChannelToFeed(settingsHierachy_->getSetting(QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL)));
autoUpdater_->checkForUpdates();
autoUpdater_->onSuggestRestartToUserToUpdate.connect(boost::bind(&QtSwift::handleRecommendRestartToInstallUpdate, this));
settingsHierachy_->onSettingChanged.connect([&](const std::string& path) {
if (path == QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL.getKey()) {
autoUpdater_->setAppcastFeed(updateChannelToFeed(settingsHierachy_->getSetting(QtUISettingConstants::SOFTWARE_UPDATE_CHANNEL)));
autoUpdater_->checkForUpdates();
}
});
}
}
QtSwift::~QtSwift() {
delete autoUpdater_;
for (auto* factory : uiFactories_) {
delete factory;
}
for (auto* controller : mainControllers_) {
delete controller;
}
delete notifier_;
for (auto* tray : systemTrays_) {
delete tray;
}
delete tabs_;
+ delete chatWindowFactory_;
delete splitter_;
delete settingsHierachy_;
delete qtSettings_;
delete xmlSettings_;
delete statusCache_;
delete uriHandler_;
delete dock_;
delete soundPlayer_;
- delete chatWindowFactory_;
delete certificateStorageFactory_;
delete storagesFactory_;
delete applicationPathProvider_;
}
void QtSwift::handleAboutToQuit() {
#if defined(SWIFTEN_PLATFORM_MACOSX)
// This is required so Sparkle knows about the application shutting down
// and can update the application in background.
CocoaUIHelpers::sendCocoaApplicationWillTerminateNotification();
#endif
}
void QtSwift::handleRecommendRestartToInstallUpdate() {
notifier_->showMessage(Notifier::SystemMessage, Q2PSTRING(tr("Swift Update Available")), Q2PSTRING(tr("Restart Swift to update to the new Swift version.")), "", [](){});
}
}
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 88cb1ac..99759f4 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -109,62 +109,65 @@ sources = [
"QtNameWidget.cpp",
"QtSettingsProvider.cpp",
"QtStatusWidget.cpp",
"QtScaledAvatarCache.cpp",
"QtSwift.cpp",
"QtURIHandler.cpp",
"QtChatWindow.cpp",
"QtChatView.cpp",
"QtWebKitChatView.cpp",
"QtPlainChatView.cpp",
"QtChatTheme.cpp",
"QtChatTabs.cpp",
"QtChatTabsBase.cpp",
"QtChatTabsShortcutOnlySubstitute.cpp",
"QtSoundPlayer.cpp",
"QtSystemTray.cpp",
"QtCachedImageScaler.cpp",
"QtTabbable.cpp",
"QtTabWidget.cpp",
"QtTextEdit.cpp",
"QtXMLConsoleWidget.cpp",
"QtHistoryWindow.cpp",
"QtFileTransferListWidget.cpp",
"QtFileTransferListItemModel.cpp",
"QtAdHocCommandWindow.cpp",
"QtAdHocCommandWithJIDWindow.cpp",
"QtUtilities.cpp",
"QtBookmarkDetailWindow.cpp",
"QtAddBookmarkWindow.cpp",
"QtEditBookmarkWindow.cpp",
- "QtEmoticonsGrid.cpp",
- "QtEmoticonCell.cpp",
+ "QtEmojisGrid.cpp",
+ "QtEmojiCell.cpp",
+ "QtEmojisScroll.cpp",
+ "QtEmojisSelector.cpp",
+ "QtRecentEmojisGrid.cpp",
"QtContactEditWindow.cpp",
"QtContactEditWidget.cpp",
"QtSingleWindow.cpp",
"QtHighlightEditor.cpp",
"QtColorToolButton.cpp",
"QtClosableLineEdit.cpp",
"ChatSnippet.cpp",
"MessageSnippet.cpp",
"SystemMessageSnippet.cpp",
"QtElidingLabel.cpp",
"QtFormWidget.cpp",
"QtFormResultItemModel.cpp",
"QtLineEdit.cpp",
"QtJoinMUCWindow.cpp",
"QtConnectionSettingsWindow.cpp",
"Roster/RosterModel.cpp",
"Roster/QtTreeWidget.cpp",
"Roster/RosterDelegate.cpp",
"Roster/GroupItemDelegate.cpp",
"Roster/DelegateCommons.cpp",
"Roster/QtFilterWidget.cpp",
"Roster/QtRosterWidget.cpp",
"Roster/QtOccupantListWidget.cpp",
"Roster/RosterTooltip.cpp",
"EventViewer/EventModel.cpp",
"EventViewer/EventDelegate.cpp",
"EventViewer/TwoLineDelegate.cpp",
"EventViewer/QtEventWindow.cpp",
"EventViewer/QtEvent.cpp",
"ChatList/QtChatListWindow.cpp",