summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
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",