diff options
| -rw-r--r-- | Swift/QtUI/QtChatWindow.cpp | 2 | ||||
| -rw-r--r-- | Swift/QtUI/QtChatWindow.h | 5 | ||||
| -rw-r--r-- | Swift/QtUI/QtFileTransferListItemModel.cpp | 5 | ||||
| -rw-r--r-- | Swift/QtUI/QtHighlightRulesItemModel.cpp | 4 | ||||
| -rw-r--r-- | Swift/QtUI/QtPlainChatView.cpp | 340 | ||||
| -rw-r--r-- | Swift/QtUI/QtPlainChatView.h | 98 | ||||
| -rw-r--r-- | Swift/QtUI/QtWebKitChatView.cpp | 17 | ||||
| -rw-r--r-- | Swiften/Base/FileSize.cpp | 23 | ||||
| -rw-r--r-- | Swiften/Base/FileSize.h | 17 | ||||
| -rw-r--r-- | Swiften/Base/SConscript | 3 | 
10 files changed, 461 insertions, 53 deletions
| diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp index d4b1d0f..826ec9e 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.cpp @@ -109,7 +109,7 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	logRosterSplitter_->setAutoFillBackground(true);  	layout->addWidget(logRosterSplitter_);  	if (settings_->getSetting(QtUISettingConstants::USE_PLAIN_CHATS) || settings_->getSetting(QtUISettingConstants::USE_SCREENREADER)) { -		messageLog_ = new QtPlainChatView(this); +		messageLog_ = new QtPlainChatView(this, eventStream_);  	}  	else {  		messageLog_ = new QtWebKitChatView(this, eventStream_, theme, this); // I accept that passing the ChatWindow in so that the view can call the signals is somewhat inelegant, but it saves a lot of boilerplate. This patch is unpleasant enough already. So let's fix this soon (it at least needs fixing by the time history is sorted), but not now. diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h index ca0ecad..df1e619 100644 --- a/Swift/QtUI/QtChatWindow.h +++ b/Swift/QtUI/QtChatWindow.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010-2013 Kevin Smith + * Copyright (c) 2010-2014 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -41,9 +41,6 @@ namespace Swift {  	class QtChatWindowJSBridge;  	class SettingsProvider; -	// FIXME: Move this to a different file -	std::string formatSize(const boost::uintmax_t bytes); -  	class LabelModel : public QAbstractListModel {  		Q_OBJECT  		public: diff --git a/Swift/QtUI/QtFileTransferListItemModel.cpp b/Swift/QtUI/QtFileTransferListItemModel.cpp index 00afacb..b9b9fd1 100644 --- a/Swift/QtUI/QtFileTransferListItemModel.cpp +++ b/Swift/QtUI/QtFileTransferListItemModel.cpp @@ -9,9 +9,8 @@  #include <boost/bind.hpp>  #include <boost/cstdint.hpp> -#include "QtChatWindow.h" // for formatSize -  #include <Swiften/Base/boost_bsignals.h> +#include <Swiften/Base/FileSize.h>  #include <Swift/Controllers/FileTransfer/FileTransferController.h>  #include <Swift/Controllers/FileTransfer/FileTransferOverview.h>  #include "QtSwiftUtil.h" @@ -109,7 +108,7 @@ int QtFileTransferListItemModel::rowCount(const QModelIndex& /* parent */) const  }  QModelIndex QtFileTransferListItemModel::index(int row, int column, const QModelIndex& /* parent */) const { -	return createIndex(row, column, (void*) 0); +	return createIndex(row, column, static_cast<void*>(0));  }  } diff --git a/Swift/QtUI/QtHighlightRulesItemModel.cpp b/Swift/QtUI/QtHighlightRulesItemModel.cpp index 4efa712..fcbaddc 100644 --- a/Swift/QtUI/QtHighlightRulesItemModel.cpp +++ b/Swift/QtUI/QtHighlightRulesItemModel.cpp @@ -206,7 +206,7 @@ bool QtHighlightRulesItemModel::setData(const QModelIndex &index, const QVariant  			highlightManager_->setRule(index.row(), r);  			emit dataChanged(index, index);  			foreach (int column, changedColumns) { -				QModelIndex i = createIndex(index.row(), column, (void*) 0); +				QModelIndex i = createIndex(index.row(), column, static_cast<void*>(0));  				emit dataChanged(i, i);  			}  		} @@ -227,7 +227,7 @@ int QtHighlightRulesItemModel::rowCount(const QModelIndex& /* parent */) const  QModelIndex QtHighlightRulesItemModel::index(int row, int column, const QModelIndex& /* parent */) const  { -	return createIndex(row, column, (void*) 0); +	return createIndex(row, column, static_cast<void*>(0));  }  bool QtHighlightRulesItemModel::insertRows(int row, int count, const QModelIndex& /* parent */) diff --git a/Swift/QtUI/QtPlainChatView.cpp b/Swift/QtUI/QtPlainChatView.cpp index 98d2e8b..ee76438 100644 --- a/Swift/QtUI/QtPlainChatView.cpp +++ b/Swift/QtUI/QtPlainChatView.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2013 Kevin Smith + * Copyright (c) 2013-2014 Kevin Smith and Remko Tronçon   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -7,29 +7,40 @@  #include <Swift/QtUI/QtPlainChatView.h>  #include <QTextEdit> +#include <QScrollBar>  #include <QVBoxLayout> +#include <QPushButton> +#include <QLabel> +#include <QDialog> +#include <QProgressBar> +#include <QFileDialog> +#include <QInputDialog> +#include <QMenu>  #include <Swiften/Base/foreach.h> +#include <Swiften/Base/FileSize.h> + +#include <Swift/Controllers/UIEvents/UIEventStream.h> +#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>  #include <Swift/QtUI/ChatSnippet.h>  #include <Swift/QtUI/QtSwiftUtil.h>  #include <Swift/QtUI/QtUtilities.h> -  namespace Swift { -QtPlainChatView::QtPlainChatView(QWidget* parent) : QtChatView(parent) { +QtPlainChatView::QtPlainChatView(QtChatWindow *window, UIEventStream* eventStream) +: QtChatView(window), window_(window), eventStream_(eventStream), idGenerator_(0) {  	QVBoxLayout* mainLayout = new QVBoxLayout(this);  	mainLayout->setSpacing(0);  	mainLayout->setContentsMargins(0,0,0,0); -	log_ = new QTextEdit(this); +	log_ = new LogTextEdit(this);  	log_->setReadOnly(true);  	log_->setAccessibleName(tr("Chat Messages"));  	mainLayout->addWidget(log_);  }  QtPlainChatView::~QtPlainChatView() { -	  }  QString chatMessageToString(const ChatWindow::ChatMessage& message) { @@ -73,8 +84,10 @@ std::string QtPlainChatView::addMessage(const ChatWindow::ChatMessage& message,  	text += chatMessageToString(message);  	text += "</p>";  	log_->append(text); -	return ""; -}; +	const std::string idx = senderIsSelf ? "" : senderName; +	lastMessageLabel_[idx] = label; +	return idx; +}  std::string QtPlainChatView::addAction(const ChatWindow::ChatMessage& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& /*avatarPath*/, const boost::posix_time::ptime& time, const HighlightAction& /*highlight*/) {  	QString text = "<p>"; @@ -82,12 +95,319 @@ std::string QtPlainChatView::addAction(const ChatWindow::ChatMessage& message, c  		text += P2QSTRING(label->getLabel()) + "<br/>";  	}  	QString name = senderIsSelf ? "you" : P2QSTRING(senderName); -	text += QString(tr("At %1 %2 ")).arg(ChatSnippet::timeToEscapedString(B2QDATE(time))).arg(name); +	text += QString(tr("At %1 <i>%2 ")).arg(ChatSnippet::timeToEscapedString(B2QDATE(time))).arg(name); +	text += chatMessageToString(message); +	text += "</i></p>"; +	log_->append(text); +	const std::string idx = senderIsSelf ? "" : senderName; +	lastMessageLabel_[idx] = label; +	return idx; +} + +void QtPlainChatView::addSystemMessage(const ChatWindow::ChatMessage& message, ChatWindow::Direction /*direction*/) +{ +	QString text = "<p><i>"; +	text += chatMessageToString(message); +	text += "</i></p>"; +	log_->append(text); +} + +void QtPlainChatView::addPresenceMessage(const ChatWindow::ChatMessage& message, ChatWindow::Direction /*direction*/) +{ +	QString text = "<p><i>"; +	text += chatMessageToString(message); +	text += "</i></p>"; +	log_->append(text); +} + +void QtPlainChatView::addErrorMessage(const ChatWindow::ChatMessage& message) +{ +	QString text = "<p><i>"; +	text += chatMessageToString(message); +	text += "</i></p>"; +	log_->append(text); +} + +void QtPlainChatView::replaceMessage(const ChatWindow::ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& /*highlight*/) +{ +	QString text = "<p>"; +	if (lastMessageLabel_[id]) { +		text += P2QSTRING(lastMessageLabel_[id]->getLabel()) + "<br/>"; +	} +	QString name = id.empty() ? "you" : P2QSTRING(id); +	text += QString(tr("At %1 %2 corrected the last message to:")).arg(ChatSnippet::timeToEscapedString(B2QDATE(time))).arg(name) + "<br/>"; +	text += chatMessageToString(message); +	text += "</p>"; +	log_->append(text); +} + +void QtPlainChatView::replaceWithAction(const ChatWindow::ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& /*highlight*/) +{ +	QString text = "<p>"; +	if (lastMessageLabel_[id]) { +		text += P2QSTRING(lastMessageLabel_[id]->getLabel()) + "<br/>"; +	} +	QString name = id.empty() ? "you" : P2QSTRING(id); +	text += QString(tr("At %1 %2 corrected the last action to: <i>")).arg(ChatSnippet::timeToEscapedString(B2QDATE(time))).arg(name); +	text += chatMessageToString(message); +	text += "</i></p>"; +	log_->append(text); +} + +void QtPlainChatView::replaceLastMessage(const ChatWindow::ChatMessage& message) +{ +	QString text = "<p>The last message was corrected to:<br/>";  	text += chatMessageToString(message);  	text += "</p>";  	log_->append(text); -	return ""; -}; +} + +void QtPlainChatView::setAckState(const std::string& /*id*/, ChatWindow::AckState state) +{ +	if (state == ChatWindow::Failed) { +		addSystemMessage(ChatWindow::ChatMessage("Message delivery failed due to disconnection from server."), ChatWindow::DefaultDirection); +	} +} + +std::string QtPlainChatView::addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) +{ +	const std::string ftId = "ft" + boost::lexical_cast<std::string>(idGenerator_++); +	const std::string sizeString = formatSize(sizeInBytes); +	FileTransfer* transfer; +	if (senderIsSelf) { +		QString description = QInputDialog::getText(this, tr("File transfer description"), +			tr("Description:"), QLineEdit::Normal, ""); +		/* NOTE: it is not possible to abort if description is not provided, since we must always return a valid transfer id */ +		const std::string message = std::string() + "Confirm file transfer: <i>" + filename + " (" + sizeString + " bytes)</i>"; +		transfer = new FileTransfer(this, senderIsSelf, ftId, filename, ChatWindow::WaitingForAccept, Q2PSTRING(description), message, true); +		addSystemMessage(ChatWindow::ChatMessage("Preparing to start file transfer..."), ChatWindow::DefaultDirection); +	} else { /* incoming transfer */ +		const std::string message = std::string() + "Incoming file transfer: <i>" + filename + " (" + sizeString + " bytes)</i>"; +		transfer = new FileTransfer(this, senderIsSelf, ftId, filename, ChatWindow::WaitingForAccept, "", message, true); +		addSystemMessage("Incoming file transfer from " + senderName + "...", ChatWindow::DefaultDirection); +	} + +	fileTransfers_[ftId] = transfer; +	layout()->addWidget(transfer->dialog_); + +	return ftId; +} + +void QtPlainChatView::setFileTransferProgress(std::string id, const int percentageDone) +{ +	FileTransferMap::iterator transfer = fileTransfers_.find(id); +	if (transfer != fileTransfers_.end()) { +		transfer->second->bar_->setValue(percentageDone); +	} +} + +void QtPlainChatView::setFileTransferStatus(std::string id, const ChatWindow::FileTransferState state, const std::string& msg) +{ +	FileTransferMap::iterator transferIter = fileTransfers_.find(id); +	if (transferIter == fileTransfers_.end()) { +		return; +	} + +	/* store the layout index so we can restore it to the same location */ +	FileTransfer* oldTransfer = transferIter->second; +	const int layoutIndex = layout()->indexOf(oldTransfer->dialog_); +	layout()->removeWidget(oldTransfer->dialog_); +	const std::string &label = (!msg.empty() ? msg : oldTransfer->message_); +	FileTransfer* transfer = new FileTransfer(this, oldTransfer->senderIsSelf_, oldTransfer->ftId_, oldTransfer->filename_, state, oldTransfer->description_, label, false); +	fileTransfers_[oldTransfer->ftId_] = transfer; /* replace the transfer object for this file id */ +	delete oldTransfer; + +	/* insert the new dialog at the old position in the layout list */ +	QBoxLayout* parentLayout = dynamic_cast<QBoxLayout*>(layout()); +	assert(parentLayout); +	parentLayout->insertWidget(layoutIndex, transfer->dialog_); + +	/* log the transfer end result as a system message */ +	if (state == ChatWindow::Finished) { +		addSystemMessage(ChatWindow::ChatMessage("The file transfer completed successfully."), ChatWindow::DefaultDirection); +	} else if (state == ChatWindow::Canceled) { +		addSystemMessage(ChatWindow::ChatMessage("The file transfer was canceled."), ChatWindow::DefaultDirection); +	} else if (state == ChatWindow::FTFailed) { +		addSystemMessage(ChatWindow::ChatMessage("The file transfer failed."), ChatWindow::DefaultDirection); +	} +} + +void QtPlainChatView::addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& /*reason*/, const std::string& password, bool /*direct*/, bool isImpromptu, bool isContinuation) +{ +	PopupDialog *invite = new PopupDialog(this); + +	QLabel* statusLabel = new QLabel; +	std::string msg = senderName + " has invited you to join " + jid.toString() + "..."; +	statusLabel->setText(P2QSTRING(msg)); +	invite->layout_->addWidget(statusLabel); +	invite->layout_->addWidget(new QLabel); /* padding */ + +	AcceptMUCInviteAction* accept = new AcceptMUCInviteAction(invite, "Accept", jid, senderName, password, isImpromptu, isContinuation); +	connect(accept, SIGNAL(clicked()), SLOT(acceptMUCInvite())); +	invite->layout_->addWidget(accept); + +	AcceptMUCInviteAction* reject = new AcceptMUCInviteAction(invite, "Reject", jid, senderName, password, isImpromptu, isContinuation); +	connect(reject, SIGNAL(clicked()), SLOT(rejectMUCInvite())); +	invite->layout_->addWidget(reject); +	statusLabel->setText(P2QSTRING(msg)); + +	layout()->addWidget(invite->dialog_); + +	addSystemMessage(ChatWindow::ChatMessage(msg), ChatWindow::DefaultDirection); +} + +void QtPlainChatView::scrollToBottom() +{ +	log_->ensureCursorVisible(); +	log_->verticalScrollBar()->setValue(log_->verticalScrollBar()->maximum()); +} + +void QtPlainChatView::fileTransferAccept() +{ +	FileTransfer::Action* action = dynamic_cast<FileTransfer::Action*>(sender()); +	if (!action) { +		return; +	} + +	FileTransferMap::iterator transferIter = fileTransfers_.find(action->id_); +	if (transferIter == fileTransfers_.end()) { +		return; +	} + +	FileTransfer* transfer = transferIter->second; + +	if (transfer->senderIsSelf_) { /* if we are the sender, kick of the transfer */ +		window_->onFileTransferStart(transfer->ftId_, transfer->description_); +	} else { /* ask the user where to save the file first */ +		QString path = QFileDialog::getSaveFileName(this, tr("Save File"), P2QSTRING(transfer->filename_)); +		if (path.isEmpty()) { +			fileTransferReject(); /* because the user did not select a desintation path */ +			return; +		} +		window_->onFileTransferAccept(transfer->ftId_, Q2PSTRING(path)); +	} + +	setFileTransferStatus(transfer->ftId_, ChatWindow::Negotiating, transfer->message_); +} + +void QtPlainChatView::fileTransferReject() +{ +	FileTransfer::Action* action = dynamic_cast<FileTransfer::Action*>(sender()); +	if (action) { +		window_->onFileTransferCancel(action->id_); +		fileTransferFinish(); +	} +} + +void QtPlainChatView::fileTransferFinish() +{ +	FileTransfer::Action* action = dynamic_cast<FileTransfer::Action*>(sender()); +	if (action) { +		FileTransferMap::iterator transferIter = fileTransfers_.find(action->id_); +		if (transferIter != fileTransfers_.end()) { +			delete transferIter->second; /* cause the dialog to close */ +			fileTransfers_.erase(transferIter); +		} +	} +} + +void QtPlainChatView::acceptMUCInvite() +{ +	AcceptMUCInviteAction *action = dynamic_cast<AcceptMUCInviteAction*>(sender()); +	if (action) { +		eventStream_->send(boost::make_shared<JoinMUCUIEvent>(action->jid_.toString(), action->password_, boost::optional<std::string>(), false, false, action->isImpromptu_, action->isContinuation_)); +		delete action->parent_; +	} +} + +void QtPlainChatView::rejectMUCInvite() +{ +	AcceptMUCInviteAction *action = dynamic_cast<AcceptMUCInviteAction*>(sender()); +	if (action) { +		/* NOTE: no action required to reject an invite? */ +		delete action->parent_; +	} +} + +QtPlainChatView::FileTransfer::FileTransfer(QtPlainChatView* parent, bool senderIsSelf, const std::string& ftId, const std::string& filename, const ChatWindow::FileTransferState state, const std::string &desc, const std::string& msg, bool initializing) +: PopupDialog(parent), bar_(0), senderIsSelf_(senderIsSelf), ftId_(ftId), filename_(filename), description_(desc), message_(msg), initializing_(initializing) +{ +	QHBoxLayout* layout = new QHBoxLayout; +	QLabel* statusLabel = new QLabel; +	layout_->addWidget(statusLabel); +	layout_->addWidget(new QLabel); /* padding */ + +	if (initializing_) { +		FileTransfer::Action* accept = new FileTransfer::Action(senderIsSelf?"Confirm":"Accept", ftId); +		parent->connect(accept, SIGNAL(clicked()), SLOT(fileTransferAccept())); +		layout_->addWidget(accept); +		FileTransfer::Action* reject = new FileTransfer::Action(senderIsSelf?"Cancel":"Reject", ftId); +		parent->connect(reject, SIGNAL(clicked()), SLOT(fileTransferReject())); +		layout_->addWidget(reject); +		statusLabel->setText(P2QSTRING(msg)); +		return; +	} + +	std::string status = msg; + +	switch (state) { +		case ChatWindow::WaitingForAccept: { +			status = "Waiting for user to accept <i>" + filename + "</i>..."; +			FileTransfer::Action* cancel = new FileTransfer::Action("Cancel", ftId); +			parent->connect(cancel, SIGNAL(clicked()), SLOT(fileTransferReject())); +			layout_->addWidget(cancel); +			break; +		} +		case ChatWindow::Negotiating: { +			status = "Preparing to transfer <i>" + filename + "</i>..."; +			FileTransfer::Action* cancel = new FileTransfer::Action("Cancel", ftId); +			parent->connect(cancel, SIGNAL(clicked()), SLOT(fileTransferReject())); +			layout_->addWidget(cancel); +			break; +		} +		case ChatWindow::Transferring: { +			status = "Transferring <i>" + filename + "</i>..."; +			bar_ = new QProgressBar; +			bar_->setRange(0, 100); +			bar_->setValue(0); +			layout->addWidget(bar_); +			FileTransfer::Action* cancel = new FileTransfer::Action("Cancel", ftId); +			parent->connect(cancel, SIGNAL(clicked()), SLOT(fileTransferReject())); +			layout_->addWidget(cancel); +			break; +		} +		case ChatWindow::Canceled: { +			status = "File <i>" + filename + "</i> was canceled."; +			FileTransfer::Action* finish = new FileTransfer::Action("Hide", ftId); +			parent->connect(finish, SIGNAL(clicked()), SLOT(fileTransferFinish())); +			layout_->addWidget(finish); +			break; +		} +		case ChatWindow::Finished: { +			status = "File <i>" + filename + "</i> was transfered successfully."; +			FileTransfer::Action* finish = new FileTransfer::Action("Hide", ftId); +			parent->connect(finish, SIGNAL(clicked()), SLOT(fileTransferFinish())); +			layout_->addWidget(finish); +			break; +		} +		case ChatWindow::FTFailed: { +			status = "File transfer failed: <i>" + filename + "</i>"; +			FileTransfer::Action* finish = new FileTransfer::Action("Hide", ftId); +			parent->connect(finish, SIGNAL(clicked()), SLOT(fileTransferFinish())); +			layout_->addWidget(finish); +			break; +		} +	} + +	statusLabel->setText(P2QSTRING(status)); +} + +void QtPlainChatView::LogTextEdit::contextMenuEvent(QContextMenuEvent *event) +{ +	QMenu *menu = createStandardContextMenu(); +	menu->exec(event->globalPos()); +	delete menu; +}  } diff --git a/Swift/QtUI/QtPlainChatView.h b/Swift/QtUI/QtPlainChatView.h index c475862..01b5925 100644 --- a/Swift/QtUI/QtPlainChatView.h +++ b/Swift/QtUI/QtPlainChatView.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2013 Kevin Smith + * Copyright (c) 2013-2014 Kevin Smith and Remko Tronçon   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -11,12 +11,15 @@  #include <boost/date_time/posix_time/posix_time.hpp>  #include <QWidget> +#include <QTextEdit>  #include <Swift/Controllers/UIInterfaces/ChatWindow.h>  #include <Swift/QtUI/QtChatView.h> +#include <Swift/QtUI/QtChatWindow.h>  class QTextEdit; +class QProgressBar;  namespace Swift {  	class HighlightAction; @@ -25,7 +28,7 @@ namespace Swift {  	class QtPlainChatView : public QtChatView {  		Q_OBJECT  		public: -			QtPlainChatView(QWidget* parent); +			QtPlainChatView(QtChatWindow *window, UIEventStream* eventStream);  			virtual ~QtPlainChatView();  			/** Add message to window. @@ -37,19 +40,19 @@ namespace Swift {  			 */  			virtual std::string addAction(const ChatWindow::ChatMessage& /*message*/, const std::string& /*senderName*/, bool /*senderIsSelf*/, boost::shared_ptr<SecurityLabel> /*label*/, const std::string& /*avatarPath*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/); -			virtual void addSystemMessage(const ChatWindow::ChatMessage& /*message*/, ChatWindow::Direction /*direction*/) {}; -			virtual void addPresenceMessage(const ChatWindow::ChatMessage& /*message*/, ChatWindow::Direction /*direction*/) {}; - -			virtual void addErrorMessage(const ChatWindow::ChatMessage& /*message*/) {}; -			virtual void replaceMessage(const ChatWindow::ChatMessage& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/) {}; -			virtual void replaceWithAction(const ChatWindow::ChatMessage& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/) {}; -			virtual void replaceLastMessage(const ChatWindow::ChatMessage& /*message*/) {}; -			virtual void setAckState(const std::string& /*id*/, ChatWindow::AckState /*state*/) {}; -			 -			virtual std::string addFileTransfer(const std::string& /*senderName*/, bool /*senderIsSelf*/, const std::string& /*filename*/, const boost::uintmax_t /*sizeInBytes*/) {return "";}; -			virtual void setFileTransferProgress(std::string, const int /*percentageDone*/) {}; -			virtual void setFileTransferStatus(std::string, const ChatWindow::FileTransferState /*state*/, const std::string& /*msg*/ = "") {}; -			virtual void addMUCInvitation(const std::string& /*senderName*/, const JID& /*jid*/, const std::string& /*reason*/, const std::string& /*password*/, bool /*direct*/, bool /*isImpromptu*/, bool /*isContinuation*/) {}; +			virtual void addSystemMessage(const ChatWindow::ChatMessage& /*message*/, ChatWindow::Direction /*direction*/); +			virtual void addPresenceMessage(const ChatWindow::ChatMessage& /*message*/, ChatWindow::Direction /*direction*/); +			virtual void addErrorMessage(const ChatWindow::ChatMessage& /*message*/); + +			virtual void replaceMessage(const ChatWindow::ChatMessage& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/); +			virtual void replaceWithAction(const ChatWindow::ChatMessage& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/); +			virtual void replaceLastMessage(const ChatWindow::ChatMessage& /*message*/); +			virtual void setAckState(const std::string& /*id*/, ChatWindow::AckState /*state*/); + +			virtual std::string addFileTransfer(const std::string& /*senderName*/, bool /*senderIsSelf*/, const std::string& /*filename*/, const boost::uintmax_t /*sizeInBytes*/); +			virtual void setFileTransferProgress(std::string, const int /*percentageDone*/); +			virtual void setFileTransferStatus(std::string, const ChatWindow::FileTransferState /*state*/, const std::string& /*msg*/ = ""); +			virtual void addMUCInvitation(const std::string& /*senderName*/, const JID& /*jid*/, const std::string& /*reason*/, const std::string& /*password*/, bool /*direct*/, bool /*isImpromptu*/, bool /*isContinuation*/);  			virtual std::string addWhiteboardRequest(const QString& /*contact*/, bool /*senderIsSelf*/) {return "";};  			virtual void setWhiteboardSessionStatus(const std::string& /*id*/, const ChatWindow::WhiteboardSessionState /*state*/) {};  			virtual void setMessageReceiptState(const std::string& /*id*/, ChatWindow::ReceiptState /*state*/) {}; @@ -59,11 +62,72 @@ namespace Swift {  		public slots:  			virtual void resizeFont(int /*fontSizeSteps*/) {}; -			virtual void scrollToBottom() {}; +			virtual void scrollToBottom();  			virtual void handleKeyPressEvent(QKeyEvent* /*event*/) {}; +			virtual void fileTransferAccept(); +			virtual void fileTransferReject(); +			virtual void fileTransferFinish(); +			virtual void acceptMUCInvite(); +			virtual void rejectMUCInvite();  		private: -			QTextEdit* log_; +			struct PopupDialog { +				PopupDialog(QtPlainChatView* parent) { +					dialog_ = new QFrame(parent); +					dialog_->setFrameShape(QFrame::Panel); +					dialog_->setFrameShadow(QFrame::Raised); +					layout_ = new QHBoxLayout; +					dialog_->setLayout(layout_); +				} +				virtual ~PopupDialog() { +					delete dialog_; +				} +				QFrame* dialog_; +				QHBoxLayout* layout_; +			}; + +			struct AcceptMUCInviteAction : public QPushButton { +				AcceptMUCInviteAction(PopupDialog* parent, const std::string& text, const JID& jid, const std::string& senderName, const std::string& password, bool isImpromptu, bool isContinuation) +				: QPushButton(P2QSTRING(text)), parent_(parent), jid_(jid), senderName_(senderName), password_(password), isImpromptu_(isImpromptu), isContinuation_(isContinuation) {} +				PopupDialog *parent_; +				JID jid_; +				std::string senderName_; +				std::string password_; +				bool isImpromptu_; +				bool isContinuation_; +			}; + +			struct FileTransfer : public PopupDialog { +				struct Action : QPushButton { +					Action(const std::string& text, const std::string& id) +					: QPushButton(P2QSTRING(text)), id_(id) {} +					std::string id_; +				}; +				FileTransfer(QtPlainChatView* parent, bool senderIsSelf, const std::string& ftId, const std::string& filename, const ChatWindow::FileTransferState state, const std::string& desc, const std::string& msg, bool initializing); +				QProgressBar* bar_; +				bool senderIsSelf_; +				std::string ftId_; +				std::string filename_; +				std::string description_; +				std::string message_; +				bool initializing_; +			}; + +			class LogTextEdit : public QTextEdit { +			public: +				LogTextEdit(QWidget* parent) : QTextEdit(parent) {} +				virtual ~LogTextEdit() {} +				virtual void contextMenuEvent(QContextMenuEvent *event); +			}; + +			typedef std::map<std::string, FileTransfer*> FileTransferMap; +			QtChatWindow* window_; +			UIEventStream* eventStream_; +			LogTextEdit* log_; +			QMenu* logMenu_; +			FileTransferMap fileTransfers_; +			std::map<std::string, boost::shared_ptr<SecurityLabel> > lastMessageLabel_; +			int idGenerator_;  	};  } diff --git a/Swift/QtUI/QtWebKitChatView.cpp b/Swift/QtUI/QtWebKitChatView.cpp index af21609..d1e250c 100644 --- a/Swift/QtUI/QtWebKitChatView.cpp +++ b/Swift/QtUI/QtWebKitChatView.cpp @@ -1,13 +1,11 @@  /* - * Copyright (c) 2010-2013 Remko Tronçon + * Copyright (c) 2010-2014 Remko Tronçon   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */  #include "QtWebKitChatView.h" -#include <boost/format.hpp> -  #include <QtDebug>  #include <QEventLoop>  #include <QFile> @@ -23,6 +21,7 @@  #include <QFileDialog>  #include <Swiften/Base/Log.h> +#include <Swiften/Base/FileSize.h>  #include <Swiften/StringCodecs/Base64.h>  #include <Swift/Controllers/UIEvents/UIEventStream.h> @@ -621,18 +620,6 @@ std::string QtWebKitChatView::addAction(const ChatWindow::ChatMessage& message,  	return addMessage(" *" + chatMessageToHTML(message) + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time, highlight, ChatSnippet::getDirection(message));  } -// FIXME: Move this to a different file -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] : "") ); -} -  static QString encodeButtonArgument(const QString& str) {  	return QtUtilities::htmlEscape(P2QSTRING(Base64::encode(createByteArray(Q2PSTRING(str)))));  } diff --git a/Swiften/Base/FileSize.cpp b/Swiften/Base/FileSize.cpp new file mode 100644 index 0000000..90fdc9a --- /dev/null +++ b/Swiften/Base/FileSize.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2010-2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Base/FileSize.h> +#include <boost/format.hpp> + +namespace Swift { + +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] : "") ); +} + +} diff --git a/Swiften/Base/FileSize.h b/Swiften/Base/FileSize.h new file mode 100644 index 0000000..c9ed5fe --- /dev/null +++ b/Swiften/Base/FileSize.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2010-2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/API.h> +#include <boost/cstdint.hpp> +#include <string> + +namespace Swift { + +SWIFTEN_API std::string formatSize(const boost::uintmax_t bytes); + +} diff --git a/Swiften/Base/SConscript b/Swiften/Base/SConscript index 094059a..d7fd7cc 100644 --- a/Swiften/Base/SConscript +++ b/Swiften/Base/SConscript @@ -16,6 +16,7 @@ objects = swiften_env.SwiftenObject([  			"BoostRandomGenerator.cpp",  			"sleep.cpp",  			"URL.cpp", -			"Regex.cpp" +			"Regex.cpp", +			"FileSize.cpp"  		])  swiften_env.Append(SWIFTEN_OBJECTS = [objects]) | 
 Swift
 Swift