diff options
-rw-r--r-- | Swift/QtUI/QtChatWindow.cpp | 12 | ||||
-rw-r--r-- | Swift/QtUI/QtChatWindow.h | 4 | ||||
-rw-r--r-- | Swift/QtUI/QtChatWindowFactory.cpp | 4 | ||||
-rw-r--r-- | Swift/QtUI/QtChatWindowFactory.h | 6 | ||||
-rw-r--r-- | Swift/QtUI/QtEmojiCell.cpp | 15 | ||||
-rw-r--r-- | Swift/QtUI/QtEmojiCell.h | 7 | ||||
-rw-r--r-- | Swift/QtUI/QtEmojisGrid.cpp | 6 | ||||
-rw-r--r-- | Swift/QtUI/QtEmojisGrid.h | 1 | ||||
-rw-r--r-- | Swift/QtUI/QtEmojisScroll.cpp | 6 | ||||
-rw-r--r-- | Swift/QtUI/QtEmojisSelector.cpp | 24 | ||||
-rw-r--r-- | Swift/QtUI/QtEmojisSelector.h | 8 | ||||
-rw-r--r-- | Swift/QtUI/QtEmoticonsGrid.cpp | 40 | ||||
-rw-r--r-- | Swift/QtUI/QtEmoticonsGrid.h | 23 | ||||
-rw-r--r-- | Swift/QtUI/QtSwift.cpp | 2 | ||||
-rw-r--r-- | Swift/QtUI/SConscript | 1 |
15 files changed, 147 insertions, 12 deletions
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp index 345139c..f57b83f 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.cpp @@ -1,97 +1,100 @@ /* * Copyright (c) 2010-2017 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swift/QtUI/QtChatWindow.h> +#include <map> #include <memory> +#include <string> #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 <Swiften/Base/Platform.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/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, QtSettingsProvider* qtOnlySettings) : QtTabbable(), id_(Q2PSTRING(contact)), contact_(contact), nextAlertId_(0), eventStream_(eventStream), settings_(settings), qtOnlySettings_(qtOnlySettings), blockingState_(BlockingUnsupported), isMUC_(false), supportsImpromptuChat_(false), roomBookmarkState_(RoomNotBookmarked) { +QtChatWindow::QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QtSettingsProvider* qtOnlySettings, const std::map<std::string, std::string>& emoticonsMap) : QtTabbable(), id_(Q2PSTRING(contact)), contact_(contact), nextAlertId_(0), eventStream_(eventStream), settings_(settings), qtOnlySettings_(qtOnlySettings), blockingState_(BlockingUnsupported), isMUC_(false), supportsImpromptuChat_(false), roomBookmarkState_(RoomNotBookmarked), emoticonsMap_(emoticonsMap) { unreadCount_ = 0; isOnline_ = true; completer_ = nullptr; 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")); @@ -120,61 +123,66 @@ 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* emojisButton_ = new QPushButton(this); + +#ifdef SWIFTEN_PLATFORM_MACOSX emojisButton_->setText("\xF0\x9F\x98\x83"); +#else + emojisButton_->setIcon(QIcon(":/emoticons/smile.png")); +#endif connect(emojisButton_, SIGNAL(clicked()), this, SLOT(handleEmojisButtonClicked())); // using an extra layout to work around Qt margin glitches on OS X QHBoxLayout* actionLayout = new QHBoxLayout(); 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)); @@ -635,61 +643,61 @@ 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::handleEmojisButtonClicked() { // Create QtEmojisSelector and QMenu - emojisGrid_ = new QtEmojisSelector(qtOnlySettings_->getQSettings()); + emojisGrid_ = new QtEmojisSelector(qtOnlySettings_->getQSettings(), emoticonsMap_); auto emojisLayout = new QVBoxLayout(); emojisLayout->setContentsMargins(style()->pixelMetric(QStyle::PM_MenuHMargin),style()->pixelMetric(QStyle::PM_MenuVMargin), style()->pixelMetric(QStyle::PM_MenuHMargin),style()->pixelMetric(QStyle::PM_MenuVMargin)); 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::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(); } diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h index 4c64c84..fca4aec 100644 --- a/Swift/QtUI/QtChatWindow.h +++ b/Swift/QtUI/QtChatWindow.h @@ -1,39 +1,40 @@ /* * Copyright (c) 2010-2017 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once #include <map> +#include <string> #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; @@ -54,61 +55,61 @@ namespace Swift { 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, QtSettingsProvider* qtOnlySettings); + QtChatWindow(const QString& contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QtSettingsProvider* qtOnlySettings, const std::map<std::string, std::string>& emoticonsMap); virtual ~QtChatWindow(); std::string addMessage(const ChatMessage& message, const std::string &senderName, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time); std::string addAction(const ChatMessage& message, const std::string &senderName, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time); 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); @@ -209,32 +210,33 @@ namespace Swift { 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_ = nullptr; QtSettingsProvider* qtOnlySettings_ = nullptr; std::vector<ChatWindow::RoomAction> availableRoomActions_; QPalette defaultLabelsPalette_; LabelModel* labelModel_; BlockingState blockingState_; bool impromptu_; bool isMUC_; bool supportsImpromptuChat_; RoomBookmarkState roomBookmarkState_; std::unique_ptr<QMenu> emojisMenu_; QPointer<QtEmojisSelector> emojisGrid_; + std::map<std::string, std::string> emoticonsMap_; }; } diff --git a/Swift/QtUI/QtChatWindowFactory.cpp b/Swift/QtUI/QtChatWindowFactory.cpp index 5a4a501..49cfe4d 100644 --- a/Swift/QtUI/QtChatWindowFactory.cpp +++ b/Swift/QtUI/QtChatWindowFactory.cpp @@ -1,87 +1,87 @@ /* * Copyright (c) 2010-2017 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swift/QtUI/QtChatWindowFactory.h> #include <QDesktopWidget> #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) : themePath_(themePath) { +QtChatWindowFactory::QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabsBase* tabs, const QString& themePath, const std::map<std::string, std::string>& emoticonsMap) : themePath_(themePath), emoticonsMap_(emoticonsMap) { qtOnlySettings_ = qtSettings; settings_ = settings; tabs_ = tabs; 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_, qtOnlySettings_); + QtChatWindow* chatWindow = new QtChatWindow(P2QSTRING(contact.toString()), theme_, eventStream, settings_, qtOnlySettings_, emoticonsMap_); connect(chatWindow, SIGNAL(splitterMoved()), this, SLOT(handleSplitterMoved())); connect(this, SIGNAL(changeSplitterState(QByteArray)), chatWindow, SLOT(handleChangeSplitterState(QByteArray))); 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 a217215..2bb6a90 100644 --- a/Swift/QtUI/QtChatWindowFactory.h +++ b/Swift/QtUI/QtChatWindowFactory.h @@ -1,47 +1,51 @@ /* * 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); + QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabsBase* tabs, const QString& themePath, const std::map<std::string, std::string>& emoticonsMap); ~QtChatWindowFactory(); ChatWindow* createChatWindow(const JID &contact, UIEventStream* eventStream); signals: 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> emoticonsMap_; }; } diff --git a/Swift/QtUI/QtEmojiCell.cpp b/Swift/QtUI/QtEmojiCell.cpp index 3f2c652..865f1f6 100644 --- a/Swift/QtUI/QtEmojiCell.cpp +++ b/Swift/QtUI/QtEmojiCell.cpp @@ -3,41 +3,54 @@ * 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); const auto boundingRect = fontMetrics().boundingRect("\xF0\x9F\x98\x83"); setFixedWidth(qMax(boundingRect.width(), boundingRect.height())); setFixedHeight(qMax(boundingRect.width(), boundingRect.height())); setFlat(true); setToolTip(shortname); connect(this, SIGNAL(clicked()), this, SLOT(handleEmojiClicked())); } + QtEmojiCell::QtEmojiCell(QIcon icon, QString text, QWidget* parent) : QPushButton(parent), text_(text) { + setIcon(icon); + setFlat(true); + connect(this, SIGNAL(clicked()), this, SLOT(handleEmojiClicked())); + setFixedSize(icon.availableSizes()[0] * 1.5); + } + QtEmojiCell::QtEmojiCell(const QtEmojiCell& b) : QtEmojiCell(b.toolTip(), b.text()) { + } QtEmojiCell::~QtEmojiCell() { } void QtEmojiCell::handleEmojiClicked () { - emit emojiClicked(text()); + if (text_.isEmpty()) { + emit emojiClicked(text()); + } + else { + emit emojiClicked(text_); + } } } diff --git a/Swift/QtUI/QtEmojiCell.h b/Swift/QtUI/QtEmojiCell.h index 62f989b..250b10d 100644 --- a/Swift/QtUI/QtEmojiCell.h +++ b/Swift/QtUI/QtEmojiCell.h @@ -1,30 +1,35 @@ /* - * Copyright (c) 2016 Isode Limited. + * Copyright (c) 2016-2017 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(QIcon icon, QString text, QWidget* parent = nullptr); ~QtEmojiCell(); + signals: void emojiClicked(QString emojiAsText); private slots: void handleEmojiClicked(); + + private: + QString text_; }; } diff --git a/Swift/QtUI/QtEmojisGrid.cpp b/Swift/QtUI/QtEmojisGrid.cpp index 6064a66..981dd67 100644 --- a/Swift/QtUI/QtEmojisGrid.cpp +++ b/Swift/QtUI/QtEmojisGrid.cpp @@ -20,43 +20,49 @@ namespace Swift { static const int emojiCellSpacing = 2; QtEmojisGrid::QtEmojisGrid() : FlowLayout(0, emojiCellSpacing, emojiCellSpacing) { } QtEmojisGrid::QtEmojisGrid(QString categoryName) : FlowLayout(0, emojiCellSpacing, emojiCellSpacing) { 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(); for (const auto& unicodeEmoji : emojis) { QString shortname = QString::fromStdString(EmojiMapper::unicodeToShortname(Q2PSTRING(unicodeEmoji))); auto emoji = new QtEmojiCell(shortname, unicodeEmoji); connect(emoji, SIGNAL(emojiClicked(QString)), this, SIGNAL(onEmojiSelected(QString))); addItem(new QWidgetItem(emoji)); } } + void QtEmojisGrid::addEmoticon(QIcon icon, QString text) { + auto emoji = new QtEmojiCell(icon, text); + connect(emoji, SIGNAL(emojiClicked(QString)), this, SIGNAL(onEmojiSelected(QString))); + addItem(new QWidgetItem(emoji)); + } + void QtEmojisGrid::clearEmojis() { QLayoutItem* child = nullptr; while ((child = this->takeAt(0)) != nullptr) { if (child->widget()) { child->widget()->hide(); removeWidget(child->widget()); } else { removeItem(child); } } } } diff --git a/Swift/QtUI/QtEmojisGrid.h b/Swift/QtUI/QtEmojisGrid.h index b7ba87f..5e6c770 100644 --- a/Swift/QtUI/QtEmojisGrid.h +++ b/Swift/QtUI/QtEmojisGrid.h @@ -1,32 +1,33 @@ /* * Copyright (c) 2016-2017 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once #include <QString> #include <QVector> #include <SwifTools/EmojiMapper.h> #include <Swift/QtUI/FlowLayout.h> namespace Swift { class QtEmojisGrid : public FlowLayout { Q_OBJECT public: explicit QtEmojisGrid(); explicit QtEmojisGrid(QString categoryName); protected: void setEmojis(const QVector<QString>& emojis); + void addEmoticon(QIcon icon, QString text); private: void clearEmojis(); signals: void onEmojiSelected(QString emoji); }; } diff --git a/Swift/QtUI/QtEmojisScroll.cpp b/Swift/QtUI/QtEmojisScroll.cpp index 2c347bb..be505b6 100644 --- a/Swift/QtUI/QtEmojisScroll.cpp +++ b/Swift/QtUI/QtEmojisScroll.cpp @@ -1,32 +1,38 @@ /* * 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 <Swiften/Base/Platform.h> + #include <Swift/QtUI/QtEmojisGrid.h> #include <Swift/QtUI/QtRecentEmojisGrid.h> namespace Swift { QtEmojisScroll::QtEmojisScroll(QLayout* emojiLayout, QWidget *parent) : QWidget(parent) { auto selector = new QWidget(); auto scrollArea = new QScrollArea(); scrollArea->setWidgetResizable(true); scrollArea->setWidget(selector); selector->setLayout(emojiLayout); this->setLayout(new QVBoxLayout); this->layout()->addWidget(scrollArea); this->layout()->setContentsMargins(0,0,0,0); if (emojiLayout->itemAt(0)) { +#ifdef SWIFTEN_PLATFORM_MACOSX setMinimumHeight(emojiLayout->itemAt(0)->minimumSize().height() * 8); +#else + setMinimumHeight(emojiLayout->itemAt(0)->minimumSize().height() * 2); +#endif } } } diff --git a/Swift/QtUI/QtEmojisSelector.cpp b/Swift/QtUI/QtEmojisSelector.cpp index 8b21380..62ed862 100644 --- a/Swift/QtUI/QtEmojisSelector.cpp +++ b/Swift/QtUI/QtEmojisSelector.cpp @@ -1,75 +1,95 @@ /* * 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 <QTabBar> #include <QWidget> +#include <Swiften/Base/Platform.h> + #include <SwifTools/EmojiMapper.h> #include <Swift/QtUI/QtEmojisGrid.h> #include <Swift/QtUI/QtEmojisScroll.h> +#include <Swift/QtUI/QtEmoticonsGrid.h> #include <Swift/QtUI/QtRecentEmojisGrid.h> #include <Swift/QtUI/QtSwiftUtil.h> namespace Swift { - QtEmojisSelector::QtEmojisSelector(QSettings* settings, QWidget* parent) : QTabWidget(parent), settings_(settings) { + QtEmojisSelector::QtEmojisSelector(QSettings* settings, const std::map<std::string, std::string>& emoticonsMap, QWidget* parent) : QTabWidget(parent), settings_(settings), emoticonsMap_(emoticonsMap) { +#ifdef SWIFTEN_PLATFORM_MACOSX recentEmojisGrid_ = addRecentTab(); connect(recentEmojisGrid_, SIGNAL(onEmojiSelected(QString)), this, SLOT(emojiClickedSlot(QString))); for (const auto& category : EmojiMapper::getCategories()) { if (category != "modifier") { QtEmojisGrid* grid = addTab(P2QSTRING(category)); connect(grid, SIGNAL(onEmojiSelected(QString)), this, SLOT(emojiClickedSlot(QString))); } } loadSettings(); +#else + setupEmoticonsTab(); +#endif } QtEmojisSelector::~QtEmojisSelector() { +#ifdef SWIFTEN_PLATFORM_MACOSX writeSettings(); +#endif } 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::setupEmoticonsTab() { + QtEmojisGrid* grid = new QtEmoticonsGrid(emoticonsMap_); + QtEmojisScroll* scroll = new QtEmojisScroll(grid); + QTabWidget::addTab(scroll, QIcon(":/emoticons/smile.png"),""); + connect(grid, SIGNAL(onEmojiSelected(QString)), this, SLOT(emojiClickedSlot(QString))); + tabBar()->hide(); + } + void QtEmojisSelector::emojiClickedSlot(QString emoji) { - recentEmojisGrid_->handleEmojiClicked(emoji); + if (recentEmojisGrid_) { + recentEmojisGrid_->handleEmojiClicked(emoji); + } emit emojiClicked(emoji); } } diff --git a/Swift/QtUI/QtEmojisSelector.h b/Swift/QtUI/QtEmojisSelector.h index 1065aa0..7ac11d0 100644 --- a/Swift/QtUI/QtEmojisSelector.h +++ b/Swift/QtUI/QtEmojisSelector.h @@ -1,40 +1,46 @@ /* * Copyright (c) 2016-2017 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once +#include <map> +#include <string> + #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(QSettings* settings, const std::map<std::string, std::string>& emoticonsMap, 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(); + void setupEmoticonsTab(); + private: QSettings* settings_ = nullptr; QtRecentEmojisGrid* recentEmojisGrid_ = nullptr; + std::map<std::string, std::string> emoticonsMap_; }; } diff --git a/Swift/QtUI/QtEmoticonsGrid.cpp b/Swift/QtUI/QtEmoticonsGrid.cpp new file mode 100644 index 0000000..a592e90 --- /dev/null +++ b/Swift/QtUI/QtEmoticonsGrid.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include <Swift/QtUI/QtEmoticonsGrid.h> + +#include <unordered_set> + +#include <SwifTools/EmojiMapper.h> + +#include <Swift/QtUI/QtEmojiCell.h> +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + + QtEmoticonsGrid::QtEmoticonsGrid(const std::map<std::string, std::string>& emoticonsMap) : QtEmojisGrid() { + std::unordered_set<std::string> usedEmoticons; + for (const auto& emoticonPair : emoticonsMap) { + if (usedEmoticons.find(emoticonPair.second) == usedEmoticons.end()) { + usedEmoticons.insert(emoticonPair.second); + + auto filePath = P2QSTRING(emoticonPair.second); + if (filePath.startsWith("qrc:/")) { + filePath.remove(0, 3); + } + auto icon = QIcon(filePath); + auto text = P2QSTRING(emoticonPair.first); + + addEmoticon(icon, text); + } + } + } + + QtEmoticonsGrid::~QtEmoticonsGrid() { + + } + +} diff --git a/Swift/QtUI/QtEmoticonsGrid.h b/Swift/QtUI/QtEmoticonsGrid.h new file mode 100644 index 0000000..2fc2907 --- /dev/null +++ b/Swift/QtUI/QtEmoticonsGrid.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2017 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include <map> +#include <memory> +#include <string> + +#include <Swift/QtUI/QtEmojisGrid.h> + +namespace Swift { + class QtEmoticonsGrid : public QtEmojisGrid { + Q_OBJECT + public: + explicit QtEmoticonsGrid(const std::map<std::string, std::string>& emoticonsMap); + ~QtEmoticonsGrid(); + + }; +} diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp index b61147d..7b4e2c3 100644 --- a/Swift/QtUI/QtSwift.cpp +++ b/Swift/QtUI/QtSwift.cpp @@ -202,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/"); + chatWindowFactory_ = new QtChatWindowFactory(splitter_, settingsHierachy_, qtSettings_, tabs_, ":/themes/Default/", emoticons); soundPlayer_ = new QtSoundPlayer(applicationPathProvider_); // Ugly, because the dock depends on the tray, but the temporary // 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 diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index 69e99e7..1bcba64 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -115,60 +115,61 @@ sources = [ "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", "QtEmojisGrid.cpp", "QtEmojiCell.cpp", "QtEmojisScroll.cpp", "QtEmojisSelector.cpp", "QtRecentEmojisGrid.cpp", + "QtEmoticonsGrid.cpp", "QtContactEditWindow.cpp", "QtContactEditWidget.cpp", "QtSingleWindow.cpp", "QtColorToolButton.cpp", "QtClosableLineEdit.cpp", "QtHighlightNotificationConfigDialog.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", |