/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ /* * Copyright (c) 2012 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "QtWebView.h" #include "QtChatTheme.h" #include "QtSwiftUtil.h" #include "QtScaledAvatarCache.h" #include "SwifTools/Linkify.h" #include "SystemMessageSnippet.h" #include "QtWebKitChatView.h" #include "QtChatWindowJSBridge.h" namespace Swift { const QString QtWebKitChatView::ButtonFileTransferCancel = QString("filetransfer-cancel"); const QString QtWebKitChatView::ButtonFileTransferSetDescription = QString("filetransfer-setdescription"); const QString QtWebKitChatView::ButtonFileTransferSendRequest = QString("filetransfer-sendrequest"); const QString QtWebKitChatView::ButtonFileTransferAcceptRequest = QString("filetransfer-acceptrequest"); const QString QtWebKitChatView::ButtonMUCInvite = QString("mucinvite"); QString encodeButtonArgument(const QString& str) { return Qt::escape(P2QSTRING(Base64::encode(createByteArray(Q2PSTRING(str))))); } QString decodeButtonArgument(const QString& str) { return P2QSTRING(byteArrayToString(Base64::decode(Q2PSTRING(str)))); } QtWebKitChatView::QtWebKitChatView(QtChatTheme* theme, QWidget* parent, UIEventStream* eventStream, SettingsProvider* settings, QMap emoticons) : QtChatView(parent), fontSizeSteps_(0), previousMessageWasSelf_(false), previousMessageKind_(PreviosuMessageWasNone), eventStream_(eventStream), settings_(settings), emoticons_(emoticons) { theme_ = theme; showEmoticons_ = true; QVBoxLayout* mainLayout = new QVBoxLayout(this); mainLayout->setSpacing(0); mainLayout->setContentsMargins(0,0,0,0); webView_ = new QtWebView(this); connect(webView_, SIGNAL(linkClicked(const QUrl&)), SLOT(handleLinkClicked(const QUrl&))); connect(webView_, SIGNAL(loadFinished(bool)), SLOT(handleViewLoadFinished(bool))); connect(webView_, SIGNAL(gotFocus()), SIGNAL(gotFocus())); connect(webView_, SIGNAL(clearRequested()), SLOT(handleClearRequested())); connect(webView_, SIGNAL(fontGrowRequested()), SLOT(increaseFontSize())); connect(webView_, SIGNAL(fontShrinkRequested()), SLOT(decreaseFontSize())); #ifdef Q_WS_X11 /* To give a border on Linux, where it looks bad without */ QStackedWidget* stack = new QStackedWidget(this); stack->addWidget(webView_); stack->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); stack->setLineWidth(2); mainLayout->addWidget(stack); #else mainLayout->addWidget(webView_); #endif #ifdef SWIFT_EXPERIMENTAL_FT setAcceptDrops(true); #endif webPage_ = new QWebPage(this); webPage_->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); //webPage_->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); webView_->setPage(webPage_); connect(webPage_, SIGNAL(selectionChanged()), SLOT(copySelectionToClipboard())); viewReady_ = false; isAtBottom_ = true; resetView(); idCounter_ = 0; jsBridge_ = new QtChatWindowJSBridge(); addToJSEnvironment("chatwindow", jsBridge_); connect(jsBridge_, SIGNAL(buttonClicked(QString,QString,QString,QString)), this, SLOT(handleHTMLButtonClicked(QString,QString,QString,QString))); settings_->onSettingChanged.connect(boost::bind(&QtWebKitChatView::handleSettingChanged, this, _1)); showEmoticons_ = settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS); } QtWebKitChatView::~QtWebKitChatView() { delete jsBridge_; } void QtWebKitChatView::handleSettingChanged(const std::string& setting) { if (setting == QtUISettingConstants::SHOW_EMOTICONS.getKey()) { showEmoticons_ = settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS); showEmoticons(showEmoticons_); } } void QtWebKitChatView::handleClearRequested() { QMessageBox messageBox(this); messageBox.setWindowTitle(tr("Clear log")); messageBox.setText(tr("You are about to clear the contents of your chat log.")); messageBox.setInformativeText(tr("Are you sure?")); messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); messageBox.setDefaultButton(QMessageBox::Yes); int button = messageBox.exec(); if (button == QMessageBox::Yes) { logCleared(); resetView(); } } void QtWebKitChatView::handleKeyPressEvent(QKeyEvent* event) { webView_->keyPressEvent(event); } void QtWebKitChatView::addMessage(boost::shared_ptr snippet) { if (viewReady_) { addToDOM(snippet); } else { /* If this asserts, the previous queuing code was necessary and should be reinstated */ assert(false); } } QWebElement QtWebKitChatView::snippetToDOM(boost::shared_ptr snippet) { QWebElement newElement = newInsertPoint_.clone(); newElement.setInnerXml(snippet->getContent()); Q_ASSERT(!newElement.isNull()); return newElement; } void QtWebKitChatView::addToDOM(boost::shared_ptr snippet) { rememberScrolledToBottom(); bool insert = snippet->getAppendToPrevious(); QWebElement continuationElement = lastElement_.findFirst("#insert"); bool fallback = insert && continuationElement.isNull(); boost::shared_ptr newSnippet = (insert && fallback) ? snippet->getContinuationFallbackSnippet() : snippet; QWebElement newElement = snippetToDOM(newSnippet); if (insert && !fallback) { Q_ASSERT(!continuationElement.isNull()); continuationElement.replace(newElement); } else { continuationElement.removeFromDocument(); newInsertPoint_.prependOutside(newElement); } lastElement_ = newElement; if (fontSizeSteps_ != 0) { double size = 1.0 + 0.2 * fontSizeSteps_; QString sizeString(QString().setNum(size, 'g', 3) + "em"); const QWebElementCollection spans = lastElement_.findAll("span.swift_resizable"); foreach (QWebElement span, spans) { span.setStyleProperty("font-size", sizeString); } } } void QtWebKitChatView::addLastSeenLine() { if (lineSeparator_.isNull()) { lineSeparator_ = newInsertPoint_.clone(); lineSeparator_.setInnerXml(QString("
")); newInsertPoint_.prependOutside(lineSeparator_); } else { QWebElement lineSeparatorC = lineSeparator_.clone(); lineSeparatorC.removeFromDocument(); } newInsertPoint_.prependOutside(lineSeparator_); } void QtWebKitChatView::replaceLastMessage(const QString& message) { QString newMessage = linkimoticonify(message); assert(viewReady_); rememberScrolledToBottom(); assert(!lastElement_.isNull()); QWebElement replace = lastElement_.findFirst("span.swift_message"); assert(!replace.isNull()); QString old = lastElement_.toOuterXml(); replace.setInnerXml(ChatSnippet::escape(newMessage)); } void QtWebKitChatView::replaceLastMessage(const QString& newMessage, const QString& note) { rememberScrolledToBottom(); replaceLastMessage(newMessage); QWebElement replace = lastElement_.findFirst("span.swift_time"); assert(!replace.isNull()); replace.setInnerXml(ChatSnippet::escape(note)); } QString QtWebKitChatView::getLastSentMessage() { return lastElement_.toPlainText(); } void QtWebKitChatView::addToJSEnvironment(const QString& name, QObject* obj) { webView_->page()->currentFrame()->addToJavaScriptWindowObject(name, obj); } void QtWebKitChatView::replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) { replaceMessage(" *" + linkimoticonify(P2QSTRING(message)) + "*", P2QSTRING(id), B2QDATE(time), "font-style:italic "); } void QtWebKitChatView::replaceMessage(const std::string& newMessage, const QString& id, const QDateTime& editTime, const QString& style) { replaceMessage(linkimoticonify(P2QSTRING(newMessage)), id, editTime, style); } void QtWebKitChatView::replaceMessage(const QString& newMessage, const QString& id, const QDateTime& editTime, const QString& style) { if (id.isEmpty()) { qWarning() << "Trying to replace a message with no id"; return; } QString styleSpanStart = style == "" ? "" : ""; QString styleSpanEnd = style == "" ? "" : ""; QString messageWithStyle = styleSpanStart + newMessage + styleSpanEnd; rememberScrolledToBottom(); QWebElement message = document_.findFirst("#" + id); if (!message.isNull()) { QWebElement replaceContent = message.findFirst("span.swift_message"); assert(!replaceContent.isNull()); QString old = replaceContent.toOuterXml(); replaceContent.setInnerXml(ChatSnippet::escape(messageWithStyle)); QWebElement replaceTime = message.findFirst("span.swift_time"); assert(!replaceTime.isNull()); old = replaceTime.toOuterXml(); replaceTime.setInnerXml(ChatSnippet::escape(tr("%1 edited").arg(ChatSnippet::timeToEscapedString(editTime)))); } else { qWarning() << "Trying to replace element with id " << id << " but it's not there."; } } void QtWebKitChatView::showEmoticons(bool show) { { const QWebElementCollection spans = document_.findAll("span.swift_emoticon_image"); foreach (QWebElement span, spans) { span.setStyleProperty("display", show ? "inline" : "none"); } } { const QWebElementCollection spans = document_.findAll("span.swift_emoticon_text"); foreach (QWebElement span, spans) { span.setStyleProperty("display", show ? "none" : "inline"); } } } void QtWebKitChatView::copySelectionToClipboard() { if (!webPage_->selectedText().isEmpty()) { webPage_->triggerAction(QWebPage::Copy); } } void QtWebKitChatView::setAckXML(const QString& id, const QString& xml) { QWebElement message = document_.findFirst("#" + id); /* Deliberately not asserting here, so that when we start expiring old messages it won't hit us */ if (message.isNull()) return; QWebElement ackElement = message.findFirst("span.swift_ack"); assert(!ackElement.isNull()); ackElement.setInnerXml(xml); } void QtWebKitChatView::setReceiptXML(const QString& id, const QString& xml) { QWebElement message = document_.findFirst("#" + id); if (message.isNull()) return; QWebElement receiptElement = message.findFirst("span.swift_receipt"); assert(!receiptElement.isNull()); receiptElement.setInnerXml(xml); } void QtWebKitChatView::displayReceiptInfo(const QString& id, bool showIt) { QWebElement message = document_.findFirst("#" + id); if (message.isNull()) return; QWebElement receiptElement = message.findFirst("span.swift_receipt"); assert(!receiptElement.isNull()); receiptElement.setStyleProperty("display", showIt ? "inline" : "none"); } void QtWebKitChatView::rememberScrolledToBottom() { isAtBottom_ = webPage_->mainFrame()->scrollBarValue(Qt::Vertical) == webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical); } void QtWebKitChatView::scrollToBottom() { isAtBottom_ = true; webPage_->mainFrame()->setScrollBarValue(Qt::Vertical, webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical)); webView_->update(); /* Work around redraw bug in some versions of Qt. */ } void QtWebKitChatView::handleFrameSizeChanged() { if (isAtBottom_) { scrollToBottom(); } } void QtWebKitChatView::handleLinkClicked(const QUrl& url) { QDesktopServices::openUrl(url); } void QtWebKitChatView::handleViewLoadFinished(bool ok) { Q_ASSERT(ok); viewReady_ = true; } void QtWebKitChatView::increaseFontSize(int numSteps) { //qDebug() << "Increasing"; fontSizeSteps_ += numSteps; emit fontResized(fontSizeSteps_); } void QtWebKitChatView::decreaseFontSize() { fontSizeSteps_--; if (fontSizeSteps_ < 0) { fontSizeSteps_ = 0; } emit fontResized(fontSizeSteps_); } bool QtWebKitChatView::appendToPreviousCheck(QtWebKitChatView::PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf) const { return previousMessageKind_ == messageKind && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName))); } void QtWebKitChatView::resizeFont(int fontSizeSteps) { fontSizeSteps_ = fontSizeSteps; double size = 1.0 + 0.2 * fontSizeSteps_; QString sizeString(QString().setNum(size, 'g', 3) + "em"); //qDebug() << "Setting to " << sizeString; const QWebElementCollection spans = document_.findAll("span.swift_resizable"); foreach (QWebElement span, spans) { span.setStyleProperty("font-size", sizeString); } webView_->setFontSizeIsMinimal(size == 1.0); } void QtWebKitChatView::resetView() { lastElement_ = QWebElement(); QString pageHTML = theme_->getTemplate(); pageHTML.replace("==bodyBackground==", "background-color:#e3e3e3"); pageHTML.replace(pageHTML.indexOf("%@"), 2, theme_->getBase()); if (pageHTML.count("%@") > 3) { pageHTML.replace(pageHTML.indexOf("%@"), 2, theme_->getMainCSS()); } pageHTML.replace(pageHTML.indexOf("%@"), 2, "Variants/Blue on Green.css"); pageHTML.replace(pageHTML.indexOf("%@"), 2, ""/*headerSnippet.getContent()*/); pageHTML.replace(pageHTML.indexOf("%@"), 2, ""/*footerSnippet.getContent()*/); QEventLoop syncLoop; connect(webView_, SIGNAL(loadFinished(bool)), &syncLoop, SLOT(quit())); webPage_->mainFrame()->setHtml(pageHTML); while (!viewReady_) { QTimer t; t.setSingleShot(true); connect(&t, SIGNAL(timeout()), &syncLoop, SLOT(quit())); t.start(50); syncLoop.exec(); } document_ = webPage_->mainFrame()->documentElement(); QWebElement chatElement = document_.findFirst("#Chat"); newInsertPoint_ = chatElement.clone(); newInsertPoint_.setOuterXml("
"); chatElement.appendInside(newInsertPoint_); Q_ASSERT(!newInsertPoint_.isNull()); connect(webPage_->mainFrame(), SIGNAL(contentsSizeChanged(const QSize&)), this, SLOT(handleFrameSizeChanged()), Qt::UniqueConnection); } QWebElement findDivElementWithID(QWebElement document, QString id) { QWebElementCollection divs = document.findAll("div"); foreach(QWebElement div, divs) { if (div.attribute("id") == id) { return div; } } return QWebElement(); } void QtWebKitChatView::setFileTransferProgress(QString id, const int percentageDone) { QWebElement ftElement = findDivElementWithID(document_, id); if (ftElement.isNull()) { SWIFT_LOG(debug) << "Tried to access FT UI via invalid id!" << std::endl; return; } QWebElement progressBar = ftElement.findFirst("div.progressbar"); progressBar.setStyleProperty("width", QString::number(percentageDone) + "%"); QWebElement progressBarValue = ftElement.findFirst("div.progressbar-value"); progressBarValue.setInnerXml(QString::number(percentageDone) + " %"); } void QtWebKitChatView::setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& /* msg */) { QWebElement ftElement = findDivElementWithID(document_, id); if (ftElement.isNull()) { SWIFT_LOG(debug) << "Tried to access FT UI via invalid id! id = " << Q2PSTRING(id) << std::endl; return; } QString newInnerHTML = ""; if (state == ChatWindow::WaitingForAccept) { newInnerHTML = tr("Waiting for other side to accept the transfer.") + "
" + QtWebKitChatView::buildChatWindowButton(tr("Cancel"), QtWebKitChatView::ButtonFileTransferCancel, id); } if (state == ChatWindow::Negotiating) { // replace with text "Negotiaging" + Cancel button newInnerHTML = tr("Negotiating...") + "
" + QtWebKitChatView::buildChatWindowButton(tr("Cancel"), QtWebKitChatView::ButtonFileTransferCancel, id); } else if (state == ChatWindow::Transferring) { // progress bar + Cancel Button newInnerHTML = "
" "
" "
" "0%" "
" "
" "
" + QtWebKitChatView::buildChatWindowButton(tr("Cancel"), QtWebKitChatView::ButtonFileTransferCancel, id); } else if (state == ChatWindow::Canceled) { newInnerHTML = tr("Transfer has been canceled!"); } else if (state == ChatWindow::Finished) { // text "Successful transfer" newInnerHTML = tr("Transfer completed successfully."); } else if (state == ChatWindow::FTFailed) { newInnerHTML = tr("Transfer failed."); } ftElement.setInnerXml(newInnerHTML); } void QtWebKitChatView::setMUCInvitationJoined(QString id) { QWebElement divElement = findDivElementWithID(document_, id); QWebElement buttonElement = divElement.findFirst("input#mucinvite"); if (!buttonElement.isNull()) { buttonElement.setAttribute("value", tr("Return to room")); } } void QtWebKitChatView::setAckState(std::string const& id, ChatWindow::AckState state) { QString xml; switch (state) { case ChatWindow::Pending: xml = ""; displayReceiptInfo(P2QSTRING(id), false); break; case ChatWindow::Received: xml = ""; displayReceiptInfo(P2QSTRING(id), true); break; case ChatWindow::Failed: xml = ""; break; } setAckXML(P2QSTRING(id), xml); } void QtWebKitChatView::setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state) { QString xml; switch (state) { case ChatWindow::ReceiptReceived: xml = ""; break; case ChatWindow::ReceiptRequested: xml = ""; break; } setReceiptXML(P2QSTRING(id), xml); } QString QtWebKitChatView::linkimoticonify(const QString& message) const { QString messageHTML(message); messageHTML = Qt::escape(messageHTML); QMapIterator it(emoticons_); QString textStyle = showEmoticons_ ? "style='display:none'" : ""; QString imageStyle = showEmoticons_ ? "" : "style='display:none'"; if (messageHTML.length() < 500) { while (it.hasNext()) { it.next(); messageHTML.replace(it.key(), ""+it.key() + ""); } messageHTML = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML))); } messageHTML.replace("\n","
"); return messageHTML; } std::string QtWebKitChatView::addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time) { return addMessage(linkimoticonify(P2QSTRING(message)), senderName, senderIsSelf, label, avatarPath, style, time); } std::string QtWebKitChatView::addMessage(const QString &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time) { SWIFT_LOG(debug) << "[QtWebKitChatView::addMessage] - entered" << std::endl; QString scaledAvatarPath = QtScaledAvatarCache(32).getScaledAvatarPath(avatarPath.c_str()); QString htmlString; if (label) { htmlString = QString("").arg(Qt::escape(P2QSTRING(label->getForegroundColor()))).arg(Qt::escape(P2QSTRING(label->getBackgroundColor()))); htmlString += QString("%3 ").arg(Qt::escape(P2QSTRING(label->getDisplayMarking()))); } QString messageHTML(message); QString styleSpanStart = style == "" ? "" : ""; QString styleSpanEnd = style == "" ? "" : ""; htmlString += styleSpanStart + messageHTML + styleSpanEnd; SWIFT_LOG(debug) << "[QtWebKitChatView::addMessage] - message html string: " << Q2PSTRING(htmlString) << std::endl; bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMessage, senderName, senderIsSelf); if (lastLineTracker_.getShouldMoveLastLine()) { /* should this be queued? */ addLastSeenLine(); /* if the line is added we should break the snippet */ appendToPrevious = false; } QString qAvatarPath = scaledAvatarPath.isEmpty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(scaledAvatarPath).toEncoded(); std::string id = "id" + boost::lexical_cast(idCounter_++); addMessage(boost::shared_ptr(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id)))); previousMessageWasSelf_ = senderIsSelf; previousSenderName_ = P2QSTRING(senderName); previousMessageKind_ = PreviousMessageWasMessage; SWIFT_LOG(debug) << "[QtWebKitChatView::addMessage] - return: " << id << std::endl; return id; } void QtWebKitChatView::setChatWindowHasFocus(bool focus) { lastLineTracker_.setHasFocus(focus); } std::string QtWebKitChatView::addAction(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr label, const std::string& avatarPath, const boost::posix_time::ptime& time) { return addMessage(" *" + message + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time); } std::string formatSize(const boost::uintmax_t bytes) { static const char *siPrefix[] = {"k", "M", "G", "T", "P", "E", "Z", "Y", NULL}; int power = 0; double engBytes = bytes; while (engBytes >= 1000) { ++power; engBytes = engBytes / 1000.0; } return str( boost::format("%.1lf %sB") % engBytes % (power > 0 ? siPrefix[power-1] : "") ); } QString QtWebKitChatView::buildChatWindowButton(const QString& name, const QString& id, const QString& arg1, const QString& arg2, const QString& arg3) { QRegExp regex("[A-Za-z][A-Za-z0-9\\-\\_]+"); Q_ASSERT(regex.exactMatch(id)); QString html = QString("").arg(name).arg(id).arg(encodeButtonArgument(arg1)).arg(encodeButtonArgument(arg2)).arg(encodeButtonArgument(arg3)); return html; } std::string QtWebKitChatView::addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) { qDebug() << "addFileTransfer"; QString ft_id = "ft" + P2QSTRING(boost::lexical_cast(idCounter_++)); QString htmlString; QString formattedSize = P2QSTRING(formatSize(sizeInBytes)); if (senderIsSelf) { // outgoing htmlString = tr("Send file") + ": " + P2QSTRING(filename) + " ( " + formattedSize + ")
" + "
" + buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) + buildChatWindowButton(tr("Set Description"), ButtonFileTransferSetDescription, ft_id) + buildChatWindowButton(tr("Send"), ButtonFileTransferSendRequest, ft_id) + "
"; } else { // incoming htmlString = tr("Receiving file") + ": " + P2QSTRING(filename) + " ( " + formattedSize + ")
" + "
" + buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) + buildChatWindowButton(tr("Accept"), ButtonFileTransferAcceptRequest, ft_id, P2QSTRING(filename)) + "
"; } bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasFileTransfer, senderName, senderIsSelf); if ( lastLineTracker_.getShouldMoveLastLine()) { /* should this be queued? */ addLastSeenLine(); /* if the line is added we should break the snippet */ appendToPrevious = false; } QString qAvatarPath = "qrc:/icons/avatar.png"; std::string id = "ftmessage" + boost::lexical_cast(idCounter_++); addMessage(boost::shared_ptr(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id)))); previousMessageWasSelf_ = senderIsSelf; previousSenderName_ = P2QSTRING(senderName); previousMessageKind_ = PreviousMessageWasFileTransfer; return Q2PSTRING(ft_id); } void QtWebKitChatView::handleHTMLButtonClicked(QString id, QString encodedArgument1, QString encodedArgument2, QString encodedArgument3) { QString arg1 = decodeButtonArgument(encodedArgument1); QString arg2 = decodeButtonArgument(encodedArgument2); QString arg3 = decodeButtonArgument(encodedArgument3); if (id.startsWith(ButtonFileTransferCancel)) { QString ft_id = arg1; onFileTransferCancel(Q2PSTRING(ft_id)); } else if (id.startsWith(ButtonFileTransferSetDescription)) { QString ft_id = arg1; bool ok = false; QString text = QInputDialog::getText(this, tr("File transfer description"), tr("Description:"), QLineEdit::Normal, "", &ok); if (ok) { descriptions_[ft_id] = text; } } else if (id.startsWith(ButtonFileTransferSendRequest)) { QString ft_id = arg1; QString text = descriptions_.find(ft_id) == descriptions_.end() ? QString() : descriptions_[ft_id]; onFileTransferStart(Q2PSTRING(ft_id), Q2PSTRING(text)); } else if (id.startsWith(ButtonFileTransferAcceptRequest)) { QString ft_id = arg1; QString filename = arg2; QString path = QFileDialog::getSaveFileName(this, tr("Save File"), filename); if (!path.isEmpty()) { onFileTransferAccept(Q2PSTRING(ft_id), Q2PSTRING(path)); } } else if (id.startsWith(ButtonMUCInvite)) { QString roomJID = arg1; QString password = arg2; QString elementID = arg3; eventStream_->send(boost::make_shared(Q2PSTRING(roomJID), Q2PSTRING(password))); setMUCInvitationJoined(elementID); } else { SWIFT_LOG(debug) << "Unknown HTML button! ( " << Q2PSTRING(id) << " )" << std::endl; } } void QtWebKitChatView::addErrorMessage(const std::string& errorMessage) { QString errorMessageHTML(Qt::escape(P2QSTRING(errorMessage))); errorMessageHTML.replace("\n","
"); addMessage(boost::shared_ptr(new SystemMessageSnippet("" + errorMessageHTML + "", QDateTime::currentDateTime(), false, theme_))); previousMessageWasSelf_ = false; previousMessageKind_ = PreviousMessageWasSystem; } void QtWebKitChatView::addSystemMessage(const std::string& message) { QString messageHTML(Qt::escape(P2QSTRING(message))); messageHTML = linkimoticonify(messageHTML); addMessage(boost::shared_ptr(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_))); previousMessageWasSelf_ = false; previousMessageKind_ = PreviousMessageWasSystem; } void QtWebKitChatView::addPresenceMessage(const std::string& message) { QString messageHTML(linkimoticonify(P2QSTRING(message))); addMessage(boost::shared_ptr(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_))); previousMessageWasSelf_ = false; previousMessageKind_ = PreviousMessageWasPresence; } void QtWebKitChatView::addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct) { QString htmlString = QObject::tr("You've been invited to enter the %1 room.").arg(P2QSTRING(jid.toString())) + "
"; if (!reason.empty()) { htmlString += QObject::tr("Reason: %1").arg(P2QSTRING(reason)) + "
"; } if (!direct) { htmlString += QObject::tr("This person may not have really sent this invitation!") + "
"; } QString id = QString(ButtonMUCInvite + "%1").arg(P2QSTRING(boost::lexical_cast(idCounter_++))); htmlString += "
" + buildChatWindowButton(tr("Accept Invite"), ButtonMUCInvite, Qt::escape(P2QSTRING(jid.toString())), Qt::escape(P2QSTRING(password)), id) + "
"; bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMUCInvite, senderName, false); if (lastLineTracker_.getShouldMoveLastLine()) { /* should this be queued? */ addLastSeenLine(); /* if the line is added we should break the snippet */ appendToPrevious = false; } QString qAvatarPath = "qrc:/icons/avatar.png"; addMessage(boost::shared_ptr(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, appendToPrevious, theme_, id))); previousMessageWasSelf_ = false; previousSenderName_ = P2QSTRING(senderName); previousMessageKind_ = PreviousMessageWasMUCInvite; } }