diff options
Diffstat (limited to 'Swift/QtUI')
-rw-r--r-- | Swift/QtUI/QtChatWindow.cpp | 51 | ||||
-rw-r--r-- | Swift/QtUI/QtChatWindow.h | 19 | ||||
-rw-r--r-- | Swift/QtUI/QtChatWindowFactory.cpp | 10 | ||||
-rw-r--r-- | Swift/QtUI/QtChatWindowFactory.h | 14 | ||||
-rw-r--r-- | Swift/QtUI/QtEmojiCell.cpp | 41 | ||||
-rw-r--r-- | Swift/QtUI/QtEmojiCell.h | 30 | ||||
-rw-r--r-- | Swift/QtUI/QtEmojisGrid.cpp | 72 | ||||
-rw-r--r-- | Swift/QtUI/QtEmojisGrid.h | 31 | ||||
-rw-r--r-- | Swift/QtUI/QtEmojisScroll.cpp | 34 | ||||
-rw-r--r-- | Swift/QtUI/QtEmojisScroll.h | 18 | ||||
-rw-r--r-- | Swift/QtUI/QtEmojisSelector.cpp | 75 | ||||
-rw-r--r-- | Swift/QtUI/QtEmojisSelector.h | 40 | ||||
-rw-r--r-- | Swift/QtUI/QtEmoticonCell.cpp | 36 | ||||
-rw-r--r-- | Swift/QtUI/QtEmoticonCell.h | 40 | ||||
-rw-r--r-- | Swift/QtUI/QtEmoticonsGrid.cpp | 63 | ||||
-rw-r--r-- | Swift/QtUI/QtEmoticonsGrid.h | 46 | ||||
-rw-r--r-- | Swift/QtUI/QtRecentEmojisGrid.cpp | 59 | ||||
-rw-r--r-- | Swift/QtUI/QtRecentEmojisGrid.h | 43 | ||||
-rw-r--r-- | Swift/QtUI/QtSwift.cpp | 7 | ||||
-rw-r--r-- | Swift/QtUI/SConscript | 7 |
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", |