diff options
Diffstat (limited to 'Swift/QtUI')
41 files changed, 1198 insertions, 140 deletions
| diff --git a/Swift/QtUI/.gitignore b/Swift/QtUI/.gitignore index f539e86..53acb9f 100644 --- a/Swift/QtUI/.gitignore +++ b/Swift/QtUI/.gitignore @@ -1,3 +1,4 @@  Swift  BuildVersion.h  *.dmg +swift-open-uri diff --git a/Swift/QtUI/ChatList/ChatListDelegate.cpp b/Swift/QtUI/ChatList/ChatListDelegate.cpp index 274a10a..b2bfe0a 100644 --- a/Swift/QtUI/ChatList/ChatListDelegate.cpp +++ b/Swift/QtUI/ChatList/ChatListDelegate.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -11,6 +11,7 @@  #include "Swift/QtUI/Roster/GroupItemDelegate.h"  #include "Swift/QtUI/ChatList/ChatListItem.h"  #include "Swift/QtUI/ChatList/ChatListMUCItem.h" +#include "Swift/QtUI/ChatList/ChatListRecentItem.h"  #include "Swift/QtUI/ChatList/ChatListGroupItem.h"  namespace Swift { @@ -27,7 +28,11 @@ QSize ChatListDelegate::sizeHint(const QStyleOptionViewItem& option, const QMode  	ChatListItem* item = static_cast<ChatListItem*>(index.internalPointer());  	if (item && dynamic_cast<ChatListMUCItem*>(item)) {  		return mucSizeHint(option, index); -	} else if (item && dynamic_cast<ChatListGroupItem*>(item)) { +	} +	else if (item && dynamic_cast<ChatListRecentItem*>(item)) { +		return recentSizeHint(option, index); +	} +	else if (item && dynamic_cast<ChatListGroupItem*>(item)) {  		return groupDelegate_->sizeHint(option, index);  	}   	return QStyledItemDelegate::sizeHint(option, index); @@ -40,14 +45,23 @@ QSize ChatListDelegate::mucSizeHint(const QStyleOptionViewItem& /*option*/, cons  	return QSize(150, sizeByText);  } +QSize ChatListDelegate::recentSizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { +	return mucSizeHint(option, index); +} +  void ChatListDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {  	ChatListItem* item = static_cast<ChatListItem*>(index.internalPointer());  	if (item && dynamic_cast<ChatListMUCItem*>(item)) {  		paintMUC(painter, option, dynamic_cast<ChatListMUCItem*>(item)); -	} else if (item && dynamic_cast<ChatListGroupItem*>(item)) { +	} +	else if (item && dynamic_cast<ChatListRecentItem*>(item)) { +		paintRecent(painter, option, dynamic_cast<ChatListRecentItem*>(item)); +	} +	else if (item && dynamic_cast<ChatListGroupItem*>(item)) {  		ChatListGroupItem* group = dynamic_cast<ChatListGroupItem*>(item);  		groupDelegate_->paint(painter, option, group->data(Qt::DisplayRole).toString(), group->rowCount(), option.state & QStyle::State_Open);  -	} else { +	} +	else {  		QStyledItemDelegate::paint(painter, option, index);  	}  } @@ -78,9 +92,40 @@ void ChatListDelegate::paintMUC(QPainter* painter, const QStyleOptionViewItem& o  	painter->setPen(QPen(QColor(160,160,160)));  	QRect detailRegion(textRegion.adjusted(0, nameHeight, 0, 0)); -	DelegateCommons::drawElidedText(painter, detailRegion, item->data(DetailTextRole).toString()); +	DelegateCommons::drawElidedText(painter, detailRegion, item->data(ChatListMUCItem::DetailTextRole).toString());  	painter->restore();  } +void ChatListDelegate::paintRecent(QPainter* painter, const QStyleOptionViewItem& option, ChatListRecentItem* item) const { +	painter->save(); +	QRect fullRegion(option.rect); +	if ( option.state & QStyle::State_Selected ) { +		painter->fillRect(fullRegion, option.palette.highlight()); +		painter->setPen(option.palette.highlightedText().color()); +	} else { +		QColor nameColor = item->data(Qt::TextColorRole).value<QColor>(); +		painter->setPen(QPen(nameColor)); +	} + +	QFontMetrics nameMetrics(common_.nameFont); +	painter->setFont(common_.nameFont); +	int extraFontWidth = nameMetrics.width("H"); +	int leftOffset = common_.horizontalMargin * 2 + extraFontWidth / 2; +	QRect textRegion(fullRegion.adjusted(leftOffset, 0, 0, 0)); + +	int nameHeight = nameMetrics.height() + common_.verticalMargin; +	QRect nameRegion(textRegion.adjusted(0, common_.verticalMargin, 0, 0)); + +	DelegateCommons::drawElidedText(painter, nameRegion, item->data(Qt::DisplayRole).toString()); + +	painter->setFont(common_.detailFont); +	painter->setPen(QPen(QColor(160,160,160))); + +	QRect detailRegion(textRegion.adjusted(0, nameHeight, 0, 0)); +	DelegateCommons::drawElidedText(painter, detailRegion, item->data(ChatListRecentItem::DetailTextRole).toString()); + +	painter->restore(); +} +  } diff --git a/Swift/QtUI/ChatList/ChatListDelegate.h b/Swift/QtUI/ChatList/ChatListDelegate.h index f6c6c40..a898df4 100644 --- a/Swift/QtUI/ChatList/ChatListDelegate.h +++ b/Swift/QtUI/ChatList/ChatListDelegate.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -12,6 +12,7 @@  namespace Swift {  	class ChatListMUCItem; +	class ChatListRecentItem;  	class ChatListDelegate : public QStyledItemDelegate {  		public:  			ChatListDelegate(); @@ -20,7 +21,9 @@ namespace Swift {  			void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;  		private:  			void paintMUC(QPainter* painter, const QStyleOptionViewItem& option, ChatListMUCItem* item) const; +			void paintRecent(QPainter* painter, const QStyleOptionViewItem& option, ChatListRecentItem* item) const;  			QSize mucSizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const; +			QSize recentSizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const;  			DelegateCommons common_;  			GroupItemDelegate* groupDelegate_; diff --git a/Swift/QtUI/ChatList/ChatListGroupItem.h b/Swift/QtUI/ChatList/ChatListGroupItem.h index cc4d4af..a1e479f 100644 --- a/Swift/QtUI/ChatList/ChatListGroupItem.h +++ b/Swift/QtUI/ChatList/ChatListGroupItem.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -13,8 +13,8 @@  namespace Swift {  	class ChatListGroupItem : public ChatListItem {  		public: -			ChatListGroupItem(const QString& name, ChatListGroupItem* parent) : ChatListItem(parent), name_(name) {}; -			void addItem(ChatListItem* item) {items_.push_back(item); qStableSort(items_.begin(), items_.end(), pointerItemLessThan);}; +			ChatListGroupItem(const QString& name, ChatListGroupItem* parent, bool sorted = true) : ChatListItem(parent), name_(name), sorted_(sorted) {}; +			void addItem(ChatListItem* item) {items_.push_back(item); if (sorted_) {qStableSort(items_.begin(), items_.end(), pointerItemLessThan);}};  			void remove(int index) {items_.removeAt(index);};  			int rowCount() {return items_.size();};  			ChatListItem* item(int i) {return items_[i];}; @@ -30,5 +30,6 @@ namespace Swift {  			QString name_;  			QList<ChatListItem*> items_; +			bool sorted_;  	};  } diff --git a/Swift/QtUI/ChatList/ChatListMUCItem.h b/Swift/QtUI/ChatList/ChatListMUCItem.h index 068f5d6..f26aa67 100644 --- a/Swift/QtUI/ChatList/ChatListMUCItem.h +++ b/Swift/QtUI/ChatList/ChatListMUCItem.h @@ -15,14 +15,14 @@  #include "Swift/QtUI/ChatList/ChatListItem.h"  namespace Swift { -	enum MUCItemRoles { -		DetailTextRole = Qt::UserRole/*, -		AvatarRole = Qt::UserRole + 1, -		PresenceIconRole = Qt::UserRole + 2, -		StatusShowTypeRole = Qt::UserRole + 3*/ -	};  	class ChatListMUCItem : public ChatListItem {  		public: +			enum MUCItemRoles { +				DetailTextRole = Qt::UserRole/*, +				AvatarRole = Qt::UserRole + 1, +				PresenceIconRole = Qt::UserRole + 2, +				StatusShowTypeRole = Qt::UserRole + 3*/ +			};  			ChatListMUCItem(const MUCBookmark& bookmark, ChatListGroupItem* parent);  			const MUCBookmark& getBookmark();  			QVariant data(int role) const; diff --git a/Swift/QtUI/ChatList/ChatListModel.cpp b/Swift/QtUI/ChatList/ChatListModel.cpp index ba7b766..681c1c2 100644 --- a/Swift/QtUI/ChatList/ChatListModel.cpp +++ b/Swift/QtUI/ChatList/ChatListModel.cpp @@ -1,22 +1,25 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include "Swift/QtUI/ChatList/ChatListModel.h" +#include <Swift/QtUI/ChatList/ChatListModel.h> -#include "Swift/QtUI/ChatList/ChatListMUCItem.h" +#include <Swift/QtUI/ChatList/ChatListMUCItem.h> +#include <Swift/QtUI/ChatList/ChatListRecentItem.h>  namespace Swift {  ChatListModel::ChatListModel() { -	root_ = new ChatListGroupItem("", NULL); +	root_ = new ChatListGroupItem("", NULL, false);  	mucBookmarks_ = new ChatListGroupItem(tr("Bookmarked Rooms"), root_); +	recents_ = new ChatListGroupItem(tr("Recent Chats"), root_, false); +	root_->addItem(recents_);  	root_->addItem(mucBookmarks_);  } -void ChatListModel::clear() { +void ChatListModel::clearBookmarks() {  	emit layoutAboutToBeChanged();  	mucBookmarks_->clear();  	emit layoutChanged(); @@ -43,6 +46,15 @@ void ChatListModel::removeMUCBookmark(const Swift::MUCBookmark& bookmark) {  	}  } +void ChatListModel::setRecents(const std::list<ChatListWindow::Chat>& recents) { +	emit layoutAboutToBeChanged(); +	recents_->clear(); +	foreach (const ChatListWindow::Chat chat, recents) { +		recents_->addItem(new ChatListRecentItem(chat, recents_)); +	} +	emit layoutChanged(); +} +  int ChatListModel::columnCount(const QModelIndex& /*parent*/) const {  	return 1;  } diff --git a/Swift/QtUI/ChatList/ChatListModel.h b/Swift/QtUI/ChatList/ChatListModel.h index adde148..8e7828c 100644 --- a/Swift/QtUI/ChatList/ChatListModel.h +++ b/Swift/QtUI/ChatList/ChatListModel.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -11,9 +11,10 @@  #include <QAbstractItemModel>  #include <QList> -#include "Swiften/MUC/MUCBookmark.h" +#include <Swiften/MUC/MUCBookmark.h> +#include <Swift/Controllers/UIInterfaces/ChatListWindow.h> -#include "Swift/QtUI/ChatList/ChatListGroupItem.h" +#include <Swift/QtUI/ChatList/ChatListGroupItem.h>  namespace Swift {  	class ChatListModel : public QAbstractItemModel { @@ -28,9 +29,11 @@ namespace Swift {  			QModelIndex parent(const QModelIndex& index) const;  			int rowCount(const QModelIndex& parent = QModelIndex()) const;  			ChatListItem* getItemForIndex(const QModelIndex& index) const; -			void clear(); +			void clearBookmarks(); +			void setRecents(const std::list<ChatListWindow::Chat>& recents);  		private:  			ChatListGroupItem* mucBookmarks_; +			ChatListGroupItem* recents_;  			ChatListGroupItem* root_;  	}; diff --git a/Swift/QtUI/ChatList/ChatListRecentItem.cpp b/Swift/QtUI/ChatList/ChatListRecentItem.cpp new file mode 100644 index 0000000..8b6707c --- /dev/null +++ b/Swift/QtUI/ChatList/ChatListRecentItem.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/QtUI/ChatList/ChatListRecentItem.h> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { +ChatListRecentItem::ChatListRecentItem(const ChatListWindow::Chat& chat, ChatListGroupItem* parent) : ChatListItem(parent), chat_(chat) { + +} + +const ChatListWindow::Chat& ChatListRecentItem::getChat() { +	return chat_; +} + +QVariant ChatListRecentItem::data(int role) const { +	switch (role) { +		case Qt::DisplayRole: return P2QSTRING(chat_.chatName); +		case DetailTextRole: return P2QSTRING(chat_.activity); +			/*case Qt::TextColorRole: return textColor_; +		case Qt::BackgroundColorRole: return backgroundColor_; +		case Qt::ToolTipRole: return isContact() ? toolTipString() : QVariant(); +		case StatusTextRole: return statusText_; +		case AvatarRole: return avatar_; +		case PresenceIconRole: return getPresenceIcon();*/ +		default: return QVariant(); +	} +} + +} diff --git a/Swift/QtUI/ChatList/ChatListRecentItem.h b/Swift/QtUI/ChatList/ChatListRecentItem.h new file mode 100644 index 0000000..c2646cc --- /dev/null +++ b/Swift/QtUI/ChatList/ChatListRecentItem.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <QList> + +#include <boost/shared_ptr.hpp> + +#include <Swiften/MUC/MUCBookmark.h> +#include <Swift/Controllers/UIInterfaces/ChatListWindow.h> + +#include <Swift/QtUI/ChatList/ChatListItem.h> + +namespace Swift { +	class ChatListRecentItem : public ChatListItem { +		public: +			enum RecentItemRoles { +				DetailTextRole = Qt::UserRole/*, +				AvatarRole = Qt::UserRole + 1, +				PresenceIconRole = Qt::UserRole + 2, +				StatusShowTypeRole = Qt::UserRole + 3*/ +			}; +			ChatListRecentItem(const ChatListWindow::Chat& chat, ChatListGroupItem* parent); +			const ChatListWindow::Chat& getChat(); +			QVariant data(int role) const; +		private: +			ChatListWindow::Chat chat_; +	}; +} diff --git a/Swift/QtUI/ChatList/QtChatListWindow.cpp b/Swift/QtUI/ChatList/QtChatListWindow.cpp index b532cdb..d71563d 100644 --- a/Swift/QtUI/ChatList/QtChatListWindow.cpp +++ b/Swift/QtUI/ChatList/QtChatListWindow.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -10,9 +10,11 @@  #include <QContextMenuEvent>  #include "Swift/QtUI/ChatList/ChatListMUCItem.h" +#include "Swift/QtUI/ChatList/ChatListRecentItem.h"  #include "Swift/QtUI/QtAddBookmarkWindow.h"  #include "Swift/QtUI/QtEditBookmarkWindow.h"  #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h" +#include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"  #include "Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h"  #include "Swift/Controllers/UIEvents/RemoveMUCBookmarkUIEvent.h"  #include "Swift/Controllers/UIEvents/EditMUCBookmarkUIEvent.h" @@ -68,19 +70,30 @@ void QtChatListWindow::setupContextMenus() {  }  void QtChatListWindow::handleItemActivated(const QModelIndex& index) { -	if (!bookmarksEnabled_) { -		return; -	}  	ChatListItem* item = model_->getItemForIndex(index);  	ChatListMUCItem* mucItem = dynamic_cast<ChatListMUCItem*>(item); -	if (mucItem) { +	if (bookmarksEnabled_ && mucItem) {  		boost::shared_ptr<UIEvent> event(new JoinMUCUIEvent(mucItem->getBookmark().getRoom(), mucItem->getBookmark().getNick()));  		eventStream_->send(event);  	} +	ChatListRecentItem* recentItem = dynamic_cast<ChatListRecentItem*>(item); +	if (recentItem) { +		boost::shared_ptr<UIEvent> event; +		if (recentItem->getChat().isMUC) { +			if (!bookmarksEnabled_) { +				return; +			} +			return; +		} +		else { +			event = boost::shared_ptr<UIEvent>(new RequestChatUIEvent(recentItem->getChat().jid)); +		} +		eventStream_->send(event); +	}  } -void QtChatListWindow::clear() { -	model_->clear(); +void QtChatListWindow::clearBookmarks() { +	model_->clearBookmarks();  }  void QtChatListWindow::addMUCBookmark(const MUCBookmark& bookmark) { @@ -91,6 +104,10 @@ void QtChatListWindow::removeMUCBookmark(const MUCBookmark& bookmark) {  	model_->removeMUCBookmark(bookmark);  } +void QtChatListWindow::setRecents(const std::list<ChatListWindow::Chat>& recents) { +	model_->setRecents(recents); +} +  void QtChatListWindow::handleRemoveBookmark() {  	ChatListMUCItem* mucItem = dynamic_cast<ChatListMUCItem*>(contextMenuItem_);  	if (!mucItem) return; diff --git a/Swift/QtUI/ChatList/QtChatListWindow.h b/Swift/QtUI/ChatList/QtChatListWindow.h index 3a3e95f..f5c12f6 100644 --- a/Swift/QtUI/ChatList/QtChatListWindow.h +++ b/Swift/QtUI/ChatList/QtChatListWindow.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -23,7 +23,8 @@ namespace Swift {  			void addMUCBookmark(const MUCBookmark& bookmark);  			void removeMUCBookmark(const MUCBookmark& bookmark);  			void setBookmarksEnabled(bool enabled); -			void clear(); +			void setRecents(const std::list<ChatListWindow::Chat>& recents); +			void clearBookmarks();  		private slots:  			void handleItemActivated(const QModelIndex&);  			void handleAddBookmark(); diff --git a/Swift/QtUI/ChatSnippet.h b/Swift/QtUI/ChatSnippet.h index 3aa5fcc..7cbb3b3 100644 --- a/Swift/QtUI/ChatSnippet.h +++ b/Swift/QtUI/ChatSnippet.h @@ -17,7 +17,7 @@ namespace Swift {  		public:  			ChatSnippet(bool appendToPrevious);  			virtual ~ChatSnippet(); -			 +  			virtual const QString& getContent() const = 0;  			virtual QString getContinuationElementID() const { return ""; } @@ -26,7 +26,7 @@ namespace Swift {  			bool getAppendToPrevious() const {  				return appendToPrevious_;  			} -			 +  			static QString escape(const QString& original) {  				QString result(original);  				result.replace("%message%", "%message%"); @@ -37,11 +37,12 @@ namespace Swift {  				return result;  			} +			static QString timeToEscapedString(const QDateTime& time); +  		protected:  			void setContinuationFallbackSnippet(boost::shared_ptr<ChatSnippet> continuationFallback) {  				continuationFallback_ = continuationFallback;  			} -			static QString timeToEscapedString(const QDateTime& time);  		private:  			bool appendToPrevious_;  			boost::shared_ptr<ChatSnippet> continuationFallback_; diff --git a/Swift/QtUI/QtAdHocCommandWindow.cpp b/Swift/QtUI/QtAdHocCommandWindow.cpp new file mode 100644 index 0000000..a3bb077 --- /dev/null +++ b/Swift/QtUI/QtAdHocCommandWindow.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2010-2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/QtUI/QtAdHocCommandWindow.h> + +#include <boost/bind.hpp> +#include <QBoxLayout> +#include <Swift/QtUI/QtFormWidget.h> +#include <Swiften/Elements/Command.h> + +namespace Swift { +QtAdHocCommandWindow::QtAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command) : command_(command) { + +	formWidget_ = NULL; + +	setAttribute(Qt::WA_DeleteOnClose); +	command->onNextStageReceived.connect(boost::bind(&QtAdHocCommandWindow::handleNextStageReceived, this, _1)); +	command->onError.connect(boost::bind(&QtAdHocCommandWindow::handleError, this, _1)); +	command->start(); + +	QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom, this); +	layout->setContentsMargins(0,0,0,0); +	layout->setSpacing(2); +	label_ = new QLabel(this); +	layout->addWidget(label_); +	QWidget* formContainer = new QWidget(this); +	layout->addWidget(formContainer); +	formLayout_ = new QBoxLayout(QBoxLayout::TopToBottom, formContainer); +	QWidget* buttonsWidget = new QWidget(this); +	layout->addWidget(buttonsWidget); + +	QBoxLayout* buttonsLayout = new QBoxLayout(QBoxLayout::LeftToRight, buttonsWidget); +	cancelButton_ = new QPushButton(tr("Cancel"), buttonsWidget); +	buttonsLayout->addWidget(cancelButton_); +	connect(cancelButton_, SIGNAL(clicked()), this, SLOT(handleCancelClicked())); +	backButton_ = new QPushButton(tr("Back"), buttonsWidget); +	buttonsLayout->addWidget(backButton_); +	connect(backButton_, SIGNAL(clicked()), this, SLOT(handlePrevClicked())); +	nextButton_ = new QPushButton(tr("Next"), buttonsWidget); +	buttonsLayout->addWidget(nextButton_); +	connect(nextButton_, SIGNAL(clicked()), this, SLOT(handleNextClicked())); +	completeButton_ = new QPushButton(tr("Complete"), buttonsWidget); +	buttonsLayout->addWidget(completeButton_); +	connect(completeButton_, SIGNAL(clicked()), this, SLOT(handleCompleteClicked())); +	nextButton_->setEnabled(false); +	backButton_->setEnabled(false); +	completeButton_->setEnabled(false); +	actions_[Command::Next] = nextButton_; +	actions_[Command::Prev] = backButton_; +	actions_[Command::Complete] = completeButton_; +	actions_[Command::Cancel] = cancelButton_; +	show(); +} + +QtAdHocCommandWindow::~QtAdHocCommandWindow() { + +} + +void QtAdHocCommandWindow::handleCancelClicked() { +	command_->cancel(); +} + +void QtAdHocCommandWindow::handlePrevClicked() { +	command_->goBack(); +} + +void QtAdHocCommandWindow::handleNextClicked() { +	command_->goNext(formWidget_ ? formWidget_->getCompletedForm() : Form::ref()); +} + +void QtAdHocCommandWindow::handleCompleteClicked() { +	command_->complete(formWidget_ ? formWidget_->getCompletedForm() : Form::ref()); +} + +void QtAdHocCommandWindow::handleNextStageReceived(Command::ref command) { +	if (command->getForm()) { +		setForm(command->getForm()); +	} else { +		setNoForm(); +	} +	QString notes; +	foreach (Command::Note note, command->getNotes()) { +		if (!notes.isEmpty()) { +			notes += "\n"; +			QString qNote(note.note.c_str()); +			switch (note.type) { +				case Command::Note::Error: notes += tr("Error: %1").arg(qNote); break; +				case Command::Note::Warn: notes += tr("Warning: %1").arg(qNote); break; +				case Command::Note::Info: notes += qNote; break; +			} +		} +	} +	label_->setText(notes); +	setAvailableActions(command); +} + +void QtAdHocCommandWindow::handleError(ErrorPayload::ref /*error*/) { +	nextButton_->setEnabled(false); +	backButton_->setEnabled(false); +	completeButton_->setEnabled(false); +	label_->setText(tr("Error executing command")); +} + +void QtAdHocCommandWindow::setForm(Form::ref form) { +	delete formWidget_; +	formWidget_ = new QtFormWidget(form, this); +	formLayout_->addWidget(formWidget_); +} + +void QtAdHocCommandWindow::setNoForm() { +	delete formWidget_; +} + +typedef std::pair<Command::Action, QPushButton*> ActionButton; + +void QtAdHocCommandWindow::setAvailableActions(Command::ref /*commandResult*/) { +	foreach (ActionButton pair, actions_) { +		OutgoingAdHocCommandSession::ActionState state = command_->getActionState(pair.first); +		if (state & OutgoingAdHocCommandSession::Present) { +			pair.second->show(); +		} +		else { +			pair.second->hide(); +		} +		if (state & OutgoingAdHocCommandSession::Enabled) { +			pair.second->setEnabled(true); +		} +		else { +			pair.second->setEnabled(false); +		} +	} +} + +} diff --git a/Swift/QtUI/QtAdHocCommandWindow.h b/Swift/QtUI/QtAdHocCommandWindow.h new file mode 100644 index 0000000..adeb3e6 --- /dev/null +++ b/Swift/QtUI/QtAdHocCommandWindow.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010-2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <QWidget> +#include <QPushButton> +#include <QLabel> + +#include <Swift/Controllers/UIInterfaces/AdHocCommandWindow.h> +#include <Swiften/AdHoc/OutgoingAdHocCommandSession.h> + +class QBoxLayout; + +namespace Swift { +	class QtFormWidget; +	class QtAdHocCommandWindow : public QWidget, public AdHocCommandWindow { +		Q_OBJECT +		public: +			QtAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command); +			virtual ~QtAdHocCommandWindow(); +		private: +			void handleNextStageReceived(Command::ref command); +			void handleError(ErrorPayload::ref error); +			void setForm(Form::ref); +			void setNoForm(); +			void setAvailableActions(Command::ref commandResult); +		private slots: +			void handleCancelClicked(); +			void handlePrevClicked(); +			void handleNextClicked(); +			void handleCompleteClicked(); +		private: +			boost::shared_ptr<OutgoingAdHocCommandSession> command_; +			QtFormWidget* formWidget_; +			QBoxLayout* formLayout_; +			Form::ref form_; +			QLabel* label_; +			QPushButton* backButton_; +			QPushButton* nextButton_; +			QPushButton* completeButton_; +			QPushButton* cancelButton_; +			std::map<Command::Action, QPushButton*> actions_; +	}; +} diff --git a/Swift/QtUI/QtAvatarWidget.cpp b/Swift/QtUI/QtAvatarWidget.cpp index 3e3aa69..1efb787 100644 --- a/Swift/QtUI/QtAvatarWidget.cpp +++ b/Swift/QtUI/QtAvatarWidget.cpp @@ -47,8 +47,8 @@ void QtAvatarWidget::setAvatar(const ByteArray& data, const std::string& type) {  	this->type = type;  	QImage image; -	if (!data.isEmpty()) { -		image.loadFromData(reinterpret_cast<const uchar*>(data.getData()), data.getSize()); +	if (!data.empty()) { +		image.loadFromData(reinterpret_cast<const uchar*>(vecptr(data)), data.size());  	}  	if (image.isNull()) { @@ -81,10 +81,10 @@ void QtAvatarWidget::mousePressEvent(QMouseEvent* event) {  		QString fileName = QFileDialog::getOpenFileName(this, tr("Select picture"), "", tr("Image Files (*.png *.jpg *.gif)"));  		if (!fileName.isEmpty()) {  			ByteArray data; -			data.readFromFile(Q2PSTRING(fileName)); +			readByteArrayFromFile(data, Q2PSTRING(fileName));  			QBuffer buffer; -			buffer.setData(reinterpret_cast<const char*>(data.getData()), data.getSize()); +			buffer.setData(reinterpret_cast<const char*>(vecptr(data)), data.size());  			buffer.open(QIODevice::ReadOnly);  			QString type = QImageReader::imageFormat(&buffer).toLower();  			if (!type.isEmpty()) { diff --git a/Swift/QtUI/QtChatTabs.cpp b/Swift/QtUI/QtChatTabs.cpp index 25c7ca2..249080b 100644 --- a/Swift/QtUI/QtChatTabs.cpp +++ b/Swift/QtUI/QtChatTabs.cpp @@ -7,6 +7,10 @@  #include "QtChatTabs.h"  #include <algorithm> +#include <vector> + +#include <Swift/Controllers/ChatMessageSummarizer.h> +#include <Swift/QtUI/QtSwiftUtil.h>  #include <QCloseEvent>  #include <QDesktopWidget> @@ -236,16 +240,18 @@ void QtChatTabs::handleTabTitleUpdated(QWidget* widget) {  	default : tabTextColor = QColor();  	}  	tabs_->tabBar()->setTabTextColor(index, tabTextColor); -	int unread = 0; + +	std::vector<std::pair<std::string, int> > unreads;  	for (int i = 0; i < tabs_->count(); i++) {  		QtTabbable* tab = qobject_cast<QtTabbable*>(tabs_->widget(i));  		if (tab) { -			unread += tab->getCount(); +			unreads.push_back(std::pair<std::string, int>(Q2PSTRING(tab->windowTitle()), tab->getCount()));  		}  	} -	QtTabbable* current = qobject_cast<QtTabbable*>(tabs_->currentWidget()); -	setWindowTitle(unread > 0 ? QString("(%1) %2").arg(unread).arg(current->windowTitle()) : current->windowTitle()); +	std::string current(Q2PSTRING(qobject_cast<QtTabbable*>(tabs_->currentWidget())->windowTitle())); +	ChatMessageSummarizer summary; +	setWindowTitle(summary.getSummary(current, unreads).c_str());  }  void QtChatTabs::flash() { diff --git a/Swift/QtUI/QtChatView.cpp b/Swift/QtUI/QtChatView.cpp index 521b072..e3fc2b3 100644 --- a/Swift/QtUI/QtChatView.cpp +++ b/Swift/QtUI/QtChatView.cpp @@ -24,7 +24,7 @@  namespace Swift { -QtChatView::QtChatView(QtChatTheme* theme, QWidget* parent) : QWidget(parent) { +QtChatView::QtChatView(QtChatTheme* theme, QWidget* parent) : QWidget(parent), fontSizeSteps_(0) {  	theme_ = theme;  	QVBoxLayout* mainLayout = new QVBoxLayout(this); @@ -35,6 +35,8 @@ QtChatView::QtChatView(QtChatTheme* theme, QWidget* parent) : QWidget(parent) {  	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); @@ -77,13 +79,14 @@ void QtChatView::addMessage(boost::shared_ptr<ChatSnippet> snippet) {  	if (viewReady_) {  		addToDOM(snippet);  	} else { -		queuedSnippets_.append(snippet); +		/* If this asserts, the previous queuing code was necessary and should be reinstated */ +		assert(false);  	}  }  QWebElement QtChatView::snippetToDOM(boost::shared_ptr<ChatSnippet> snippet) {  	QWebElement newElement = newInsertPoint_.clone(); -	newElement.setInnerXml(snippet->getContent()); /* FIXME: Outer, surely? */ +	newElement.setInnerXml(snippet->getContent());  	Q_ASSERT(!newElement.isNull());  	return newElement;  } @@ -103,12 +106,31 @@ void QtChatView::addToDOM(boost::shared_ptr<ChatSnippet> snippet) {  		newInsertPoint_.prependOutside(newElement);  	}  	lastElement_ = newElement; -	//qApp->processEvents(); +	if (fontSizeSteps_ != 0) { +		double size = 1.0 + 0.2 * fontSizeSteps_; +		QString sizeString(QString().setNum(size, 'g', 3) + "em"); +		const QWebElementCollection spans = lastElement_.findAll("span"); +		foreach (QWebElement span, spans) { +			span.setStyleProperty("font-size", sizeString); +		} +	} +} + +void QtChatView::addLastSeenLine() { +	if (lineSeparator_.isNull()) { +		lineSeparator_ = newInsertPoint_.clone(); +		lineSeparator_.setInnerXml(QString("<hr/>")); +		newInsertPoint_.prependOutside(lineSeparator_); +	} +	else { +		QWebElement lineSeparatorC = lineSeparator_.clone(); +		lineSeparatorC.removeFromDocument(); +	} +	newInsertPoint_.prependOutside(lineSeparator_);  }  void QtChatView::replaceLastMessage(const QString& newMessage) {  	assert(viewReady_); -	/* FIXME: must be queued? */  	rememberScrolledToBottom();  	assert(!lastElement_.isNull());  	QWebElement replace = lastElement_.findFirst("span.swift_message"); @@ -125,6 +147,25 @@ void QtChatView::replaceLastMessage(const QString& newMessage, const QString& no  	replace.setInnerXml(ChatSnippet::escape(note));  } +QString QtChatView::getLastSentMessage() { +	return lastElement_.toPlainText(); +} + +void QtChatView::replaceMessage(const QString& newMessage, const QString& id, const QDateTime& editTime) { +	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(newMessage)); +		QWebElement replaceTime = message.findFirst("span.swift_time"); +		assert(!replaceTime.isNull()); +		old = replaceTime.toOuterXml(); +		replaceTime.setInnerXml(ChatSnippet::escape(tr("%1 edited").arg(ChatSnippet::timeToEscapedString(editTime)))); +	} +} +  void QtChatView::copySelectionToClipboard() {  	if (!webPage_->selectedText().isEmpty()) {  		webPage_->triggerAction(QWebPage::Copy); @@ -160,17 +201,34 @@ void QtChatView::handleLinkClicked(const QUrl& url) {  	QDesktopServices::openUrl(url);  } -void QtChatView::addQueuedSnippets() { -	for (int i = 0; i < queuedSnippets_.count(); i++) { -		addToDOM(queuedSnippets_[i]); -	} -	queuedSnippets_.clear(); -} -  void QtChatView::handleViewLoadFinished(bool ok) {  	Q_ASSERT(ok);  	viewReady_ = true; -	addQueuedSnippets(); +} + +void QtChatView::increaseFontSize(int numSteps) { +	qDebug() << "Increasing"; +	fontSizeSteps_ += numSteps; +	emit fontResized(fontSizeSteps_); +} + +void QtChatView::decreaseFontSize() { +	fontSizeSteps_--; +	if (fontSizeSteps_ < 0) { +		fontSizeSteps_ = 0; +	} +	emit fontResized(fontSizeSteps_); +} + +void QtChatView::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"); +	foreach (QWebElement span, spans) { +		span.setStyleProperty("font-size", sizeString); +	}  }  void QtChatView::resetView() { diff --git a/Swift/QtUI/QtChatView.h b/Swift/QtUI/QtChatView.h index 58b33df..eda7e42 100644 --- a/Swift/QtUI/QtChatView.h +++ b/Swift/QtUI/QtChatView.h @@ -26,15 +26,18 @@ namespace Swift {  			Q_OBJECT  		public:  			QtChatView(QtChatTheme* theme, QWidget* parent); -  			void addMessage(boost::shared_ptr<ChatSnippet> snippet); +			void addLastSeenLine();  			void replaceLastMessage(const QString& newMessage);  			void replaceLastMessage(const QString& newMessage, const QString& note); +			void replaceMessage(const QString& newMessage, const QString& id, const QDateTime& time);  			void rememberScrolledToBottom();  			void setAckXML(const QString& id, const QString& xml); +			QString getLastSentMessage();  		signals:  			void gotFocus(); +			void fontResized(int);  		public slots:  			void copySelectionToClipboard(); @@ -42,6 +45,9 @@ namespace Swift {  			void handleLinkClicked(const QUrl&);  			void handleKeyPressEvent(QKeyEvent* event);  			void resetView(); +			void increaseFontSize(int numSteps = 1); +			void decreaseFontSize(); +			void resizeFont(int fontSizeSteps);  		private slots:  			void handleViewLoadFinished(bool); @@ -51,7 +57,6 @@ namespace Swift {  		private:  			void headerEncode();  			void messageEncode(); -			void addQueuedSnippets();  			void addToDOM(boost::shared_ptr<ChatSnippet> snippet);  			QWebElement snippetToDOM(boost::shared_ptr<ChatSnippet> snippet); @@ -59,10 +64,10 @@ namespace Swift {  			bool isAtBottom_;  			QtWebView* webView_;  			QWebPage* webPage_; -			QList<boost::shared_ptr<ChatSnippet> > queuedSnippets_; - +			int fontSizeSteps_;  			QtChatTheme* theme_;  			QWebElement newInsertPoint_; +			QWebElement lineSeparator_;  			QWebElement lastElement_;  			QWebElement document_;  	}; diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp index 1a909fd..b2644b2 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -13,10 +13,12 @@  #include "MessageSnippet.h"  #include "SystemMessageSnippet.h"  #include "QtTextEdit.h" +#include "QtSettingsProvider.h"  #include "QtScaledAvatarCache.h"  #include "SwifTools/TabComplete.h" +#include <QLabel>  #include <QApplication>  #include <QBoxLayout>  #include <QCloseEvent> @@ -35,25 +37,27 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	inputEnabled_ = true;  	completer_ = NULL;  	theme_ = theme; +	isCorrection_ = false;  	updateTitleWithUnreadCount(); +	QtSettingsProvider settings;  	QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, this);  	layout->setContentsMargins(0,0,0,0);  	layout->setSpacing(2); -	 -	QSplitter *logRosterSplitter = new QSplitter(this); -	logRosterSplitter->setAutoFillBackground(true); -	layout->addWidget(logRosterSplitter); +	logRosterSplitter_ = new QSplitter(this); +	logRosterSplitter_->setAutoFillBackground(true); +	layout->addWidget(logRosterSplitter_);  	messageLog_ = new QtChatView(theme, this); -	logRosterSplitter->addWidget(messageLog_); +	logRosterSplitter_->addWidget(messageLog_);  	treeWidget_ = new QtTreeWidget(eventStream_);  	treeWidget_->setEditable(false);  	treeWidget_->hide(); -	logRosterSplitter->addWidget(treeWidget_); -	logRosterSplitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); +	logRosterSplitter_->addWidget(treeWidget_); +	logRosterSplitter_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); +	connect(logRosterSplitter_, SIGNAL(splitterMoved(int, int)), this, SLOT(handleSplitterMoved(int, int)));  	QWidget* midBar = new QWidget(this);  	layout->addWidget(midBar); @@ -69,10 +73,17 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	labelsWidget_->setSizeAdjustPolicy(QComboBox::AdjustToContents);  	midBarLayout->addWidget(labelsWidget_,0); +	QHBoxLayout* inputBarLayout = new QHBoxLayout(); +	inputBarLayout->setContentsMargins(0,0,0,0); +	inputBarLayout->setSpacing(2);  	input_ = new QtTextEdit(this);  	input_->setAcceptRichText(false); -	layout->addWidget(input_); -	 +	inputBarLayout->addWidget(input_); +	correctingLabel_ = new QLabel(tr("Correcting"), this); +	inputBarLayout->addWidget(correctingLabel_); +	correctingLabel_->hide(); +	layout->addLayout(inputBarLayout); +  	inputClearing_ = false;  	contactIsTyping_ = false; @@ -80,18 +91,23 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	connect(input_, SIGNAL(returnPressed()), this, SLOT(returnPressed()));  	connect(input_, SIGNAL(textChanged()), this, SLOT(handleInputChanged()));  	setFocusProxy(input_); -	logRosterSplitter->setFocusProxy(input_); +	logRosterSplitter_->setFocusProxy(input_);  	midBar->setFocusProxy(input_);  	messageLog_->setFocusProxy(input_);  	connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(qAppFocusChanged(QWidget*, QWidget*)));  	connect(messageLog_, SIGNAL(gotFocus()), input_, SLOT(setFocus()));  	resize(400,300); +	connect(messageLog_, SIGNAL(fontResized(int)), this, SIGNAL(fontResized(int)));  }  QtChatWindow::~QtChatWindow() {  } +void QtChatWindow::handleFontResized(int fontSizeSteps) { +	messageLog_->resizeFont(fontSizeSteps); +} +  void QtChatWindow::setTabComplete(TabComplete* completer) {  	completer_ = completer;  } @@ -118,11 +134,45 @@ void QtChatWindow::handleKeyPressEvent(QKeyEvent* event) {  		emit requestActiveTab();  	} else if (key == Qt::Key_Tab) {  		tabComplete(); +	} else if ((key == Qt::Key_Up) && input_->toPlainText().isEmpty() && !(lastSentMessage_.isEmpty())) { +		beginCorrection(); +	} else if (key == Qt::Key_Down && isCorrection_ && input_->textCursor().atBlockEnd()) { +		cancelCorrection();  	} else {  		messageLog_->handleKeyPressEvent(event);  	}  } +void QtChatWindow::beginCorrection() { +	QTextCursor cursor = input_->textCursor(); +	cursor.select(QTextCursor::Document); +	cursor.beginEditBlock(); +	cursor.insertText(QString(lastSentMessage_)); +	cursor.endEditBlock(); +	isCorrection_ = true; +	correctingLabel_->show(); +} + +void QtChatWindow::cancelCorrection() { +	QTextCursor cursor = input_->textCursor(); +	cursor.select(QTextCursor::Document); +	cursor.removeSelectedText(); +	isCorrection_ = false; +	correctingLabel_->hide(); +} + +QByteArray QtChatWindow::getSplitterState() { +	return logRosterSplitter_->saveState(); +} + +void QtChatWindow::handleChangeSplitterState(QByteArray state) { +	logRosterSplitter_->restoreState(state); +} + +void QtChatWindow::handleSplitterMoved(int, int) { +	emit splitterMoved(); +} +  void QtChatWindow::tabComplete() {  	if (!completer_) {  		return; @@ -150,7 +200,7 @@ void QtChatWindow::tabComplete() {  }  void QtChatWindow::setRosterModel(Roster* roster) { -	treeWidget_->setRosterModel(roster);	 +	treeWidget_->setRosterModel(roster);  }  void QtChatWindow::setAvailableSecurityLabels(const std::vector<SecurityLabelsCatalog::Item>& labels) { @@ -204,10 +254,13 @@ void QtChatWindow::qAppFocusChanged(QWidget *old, QWidget *now) {  	Q_UNUSED(old);  	Q_UNUSED(now);  	if (isWidgetSelected()) { +		lastLineTracker_.setHasFocus(true);  		input_->setFocus();  		onAllMessagesRead();  	} -	 +	else { +		lastLineTracker_.setHasFocus(false); +	}  }  void QtChatWindow::setInputEnabled(bool enabled) { @@ -236,7 +289,7 @@ void QtChatWindow::setContactChatState(ChatState::ChatStateType state) {  QtTabbable::AlertType QtChatWindow::getWidgetAlertState() {  	if (contactIsTyping_) {  		return ImpendingActivity; -	}  +	}  	if (unreadCount_ > 0) {  		return WaitingActivity;  	} @@ -265,7 +318,6 @@ std::string QtChatWindow::addMessage(const std::string &message, const std::stri  	if (isWidgetSelected()) {  		onAllMessagesRead();  	} -  	QString scaledAvatarPath = QtScaledAvatarCache(32).getScaledAvatarPath(avatarPath.c_str());  	QString htmlString; @@ -281,6 +333,12 @@ std::string QtChatWindow::addMessage(const std::string &message, const std::stri  	htmlString += styleSpanStart + messageHTML + styleSpanEnd;  	bool appendToPrevious = !previousMessageWasSystem_ && !previousMessageWasPresence_ && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName))); +	if (lastLineTracker_.getShouldMoveLastLine()) { +		/* should this be queued? */ +		messageLog_->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_.generateID();  	messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id)))); @@ -343,6 +401,15 @@ void QtChatWindow::addSystemMessage(const std::string& message) {  	previousMessageWasPresence_ = false;  } +void QtChatWindow::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) { +	if (!id.empty()) { +		QString messageHTML(Qt::escape(P2QSTRING(message))); +		messageHTML = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML))); +		messageHTML.replace("\n","<br/>"); +		messageLog_->replaceMessage(messageHTML, P2QSTRING(id), B2QDATE(time)); +	} +} +  void QtChatWindow::addPresenceMessage(const std::string& message) {  	if (isWidgetSelected()) {  		onAllMessagesRead(); @@ -364,9 +431,11 @@ void QtChatWindow::returnPressed() {  		return;  	}  	messageLog_->scrollToBottom(); -	onSendMessageRequest(Q2PSTRING(input_->toPlainText())); +	lastSentMessage_ = QString(input_->toPlainText()); +	onSendMessageRequest(Q2PSTRING(input_->toPlainText()), isCorrection_);  	inputClearing_ = true;  	input_->clear(); +	cancelCorrection();  	inputClearing_ = false;  } @@ -382,7 +451,9 @@ void QtChatWindow::handleInputChanged() {  }  void QtChatWindow::show() { -	QWidget::show(); +	if (parentWidget() == NULL) { +		QWidget::show(); +	}  	emit windowOpening();  } @@ -399,7 +470,7 @@ void QtChatWindow::resizeEvent(QResizeEvent*) {  }  void QtChatWindow::moveEvent(QMoveEvent*) { -	emit geometryChanged();	 +	emit geometryChanged();  }  void QtChatWindow::replaceLastMessage(const std::string& message) { diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h index 910019b..78d8f91 100644 --- a/Swift/QtUI/QtChatWindow.h +++ b/Swift/QtUI/QtChatWindow.h @@ -1,21 +1,24 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#ifndef SWIFT_QtChatWindow_H -#define SWIFT_QtChatWindow_H +#pragma once  #include "Swift/Controllers/UIInterfaces/ChatWindow.h"  #include "QtTabbable.h" +#include "SwifTools/LastLineTracker.h" +  #include "Swiften/Base/IDGenerator.h"  class QTextEdit;  class QLineEdit;  class QComboBox; +class QLabel; +class QSplitter;  namespace Swift {  	class QtChatView; @@ -35,6 +38,7 @@ namespace Swift {  			void addSystemMessage(const std::string& message);  			void addPresenceMessage(const std::string& message);  			void addErrorMessage(const std::string& errorMessage); +			void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time);  			void show();  			void activate();  			void setUnreadMessageCount(int count); @@ -54,9 +58,16 @@ namespace Swift {  			void replaceLastMessage(const std::string& message);  			void setAckState(const std::string& id, AckState state);  			void flash(); +			QByteArray getSplitterState(); + +		public slots: +			void handleChangeSplitterState(QByteArray state); +			void handleFontResized(int fontSizeSteps);  		signals:  			void geometryChanged(); +			void splitterMoved(); +			void fontResized(int);  		protected slots:  			void qAppFocusChanged(QWidget* old, QWidget* now); @@ -71,22 +82,29 @@ namespace Swift {  			void returnPressed();  			void handleInputChanged();  			void handleKeyPressEvent(QKeyEvent* event); +			void handleSplitterMoved(int pos, int index);  		private:  			void updateTitleWithUnreadCount();  			void tabComplete(); +			void beginCorrection(); +			void cancelCorrection();  			std::string addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time);  			int unreadCount_;  			bool contactIsTyping_; +			LastLineTracker lastLineTracker_;  			QString contact_; +			QString lastSentMessage_;  			QtChatView* messageLog_;  			QtChatTheme* theme_;  			QtTextEdit* input_;  			QComboBox* labelsWidget_;  			QtTreeWidget* treeWidget_; +			QLabel* correctingLabel_;  			TabComplete* completer_;  			std::vector<SecurityLabelsCatalog::Item> availableLabels_; +			bool isCorrection_;  			bool previousMessageWasSelf_;  			bool previousMessageWasSystem_;  			bool previousMessageWasPresence_; @@ -95,7 +113,6 @@ namespace Swift {  			UIEventStream* eventStream_;  			bool inputEnabled_;  			IDGenerator id_; +			QSplitter *logRosterSplitter_;  	};  } - -#endif diff --git a/Swift/QtUI/QtChatWindowFactory.cpp b/Swift/QtUI/QtChatWindowFactory.cpp index d146474..4943c0e 100644 --- a/Swift/QtUI/QtChatWindowFactory.cpp +++ b/Swift/QtUI/QtChatWindowFactory.cpp @@ -16,6 +16,10 @@  namespace Swift { + +static const QString SPLITTER_STATE = "mucSplitterState"; +static const QString CHAT_TABS_GEOMETRY = "chatTabsGeometry"; +  QtChatWindowFactory::QtChatWindowFactory(QSplitter* splitter, QtSettingsProvider* settings, QtChatTabs* tabs, const QString& themePath) : themePath_(themePath) {  	settings_ = settings;  	tabs_ = tabs; @@ -23,7 +27,7 @@ QtChatWindowFactory::QtChatWindowFactory(QSplitter* splitter, QtSettingsProvider  	if (splitter) {  		splitter->addWidget(tabs_);  	} else if (tabs_) { -		QVariant chatTabsGeometryVariant = settings_->getQSettings()->value("chatTabsGeometry"); +		QVariant chatTabsGeometryVariant = settings_->getQSettings()->value(CHAT_TABS_GEOMETRY);  		if (chatTabsGeometryVariant.isValid()) {  			tabs_->restoreGeometry(chatTabsGeometryVariant.toByteArray());  		} @@ -43,11 +47,20 @@ ChatWindow* QtChatWindowFactory::createChatWindow(const JID &contact,UIEventStre  			theme_ = new QtChatTheme(""); /* Use the inbuilt theme */  		}  	} +  	QtChatWindow *chatWindow = new QtChatWindow(P2QSTRING(contact.toString()), theme_, eventStream); +	connect(chatWindow, SIGNAL(splitterMoved()), this, SLOT(handleSplitterMoved())); +	connect(this, SIGNAL(changeSplitterState(QByteArray)), chatWindow, SLOT(handleChangeSplitterState(QByteArray))); + +	QVariant splitterState = settings_->getQSettings()->value(SPLITTER_STATE); +	if(splitterState.isValid()) { +		chatWindow->handleChangeSplitterState(splitterState.toByteArray()); +	} +  	if (tabs_) {  		tabs_->addTab(chatWindow);  	} else { -		QVariant chatGeometryVariant = settings_->getQSettings()->value("chatTabsGeometry"); +		QVariant chatGeometryVariant = settings_->getQSettings()->value(CHAT_TABS_GEOMETRY);  		if (chatGeometryVariant.isValid()) {  			chatWindow->restoreGeometry(chatGeometryVariant.toByteArray());  		} @@ -57,7 +70,13 @@ ChatWindow* QtChatWindowFactory::createChatWindow(const JID &contact,UIEventStre  }  void QtChatWindowFactory::handleWindowGeometryChanged() { -	settings_->getQSettings()->setValue("chatTabsGeometry", qobject_cast<QWidget*>(sender())->saveGeometry()); +	settings_->getQSettings()->setValue(CHAT_TABS_GEOMETRY, qobject_cast<QWidget*>(sender())->saveGeometry()); +} + +void QtChatWindowFactory::handleSplitterMoved() { +	QByteArray splitterState = qobject_cast<QtChatWindow*>(sender())->getSplitterState(); +	settings_->getQSettings()->setValue(SPLITTER_STATE, QVariant(splitterState)); +	emit changeSplitterState(splitterState);  }  } diff --git a/Swift/QtUI/QtChatWindowFactory.h b/Swift/QtUI/QtChatWindowFactory.h index 0d47854..f3e8956 100644 --- a/Swift/QtUI/QtChatWindowFactory.h +++ b/Swift/QtUI/QtChatWindowFactory.h @@ -22,8 +22,11 @@ namespace Swift {  			QtChatWindowFactory(QSplitter* splitter, QtSettingsProvider* settings, QtChatTabs* tabs, const QString& themePath);  			~QtChatWindowFactory();  			ChatWindow* createChatWindow(const JID &contact, UIEventStream* eventStream); +		signals: +			void changeSplitterState(QByteArray);  		private slots:  			void handleWindowGeometryChanged(); +			void handleSplitterMoved();  		private:  			QString themePath_;  			QtSettingsProvider* settings_; diff --git a/Swift/QtUI/QtDBUSURIHandler.cpp b/Swift/QtUI/QtDBUSURIHandler.cpp new file mode 100644 index 0000000..9b69ca6 --- /dev/null +++ b/Swift/QtUI/QtDBUSURIHandler.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "QtDBUSURIHandler.h" + +#include <QDBusAbstractAdaptor> +#include <QDBusConnection> + +#include "QtSwiftUtil.h" + +using namespace Swift; + +namespace { +	class DBUSAdaptor: public QDBusAbstractAdaptor { +			Q_OBJECT +			Q_CLASSINFO("D-Bus Interface", "im.swift.Swift.URIHandler"); +		public: +			DBUSAdaptor(QtDBUSURIHandler* uriHandler) : QDBusAbstractAdaptor(uriHandler), uriHandler(uriHandler) { +			} + +		public slots: +			void openURI(const QString& uri) { +				uriHandler->onURI(Q2PSTRING(uri)); +			} + +		private: +			QtDBUSURIHandler* uriHandler; +	}; +} + +QtDBUSURIHandler::QtDBUSURIHandler() { +	new DBUSAdaptor(this); +	QDBusConnection connection = QDBusConnection::sessionBus(); +	connection.registerService("im.swift.Swift.URIHandler"); +	connection.registerObject("/", this); +} + +#include "QtDBUSURIHandler.moc" diff --git a/Swift/QtUI/QtDBUSURIHandler.h b/Swift/QtUI/QtDBUSURIHandler.h new file mode 100644 index 0000000..be1872e --- /dev/null +++ b/Swift/QtUI/QtDBUSURIHandler.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <QObject> +#include <SwifTools/URIHandler/URIHandler.h> + +namespace Swift { +	class QtDBUSURIHandler : public QObject, public URIHandler { +		public: +			QtDBUSURIHandler(); +	}; +} diff --git a/Swift/QtUI/QtFormWidget.cpp b/Swift/QtUI/QtFormWidget.cpp new file mode 100644 index 0000000..050ff27 --- /dev/null +++ b/Swift/QtUI/QtFormWidget.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2010-2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/QtUI/QtFormWidget.h> + +#include <QGridLayout> +#include <QLabel> +#include <QListWidget> +#include <QLineEdit> +#include <QTextEdit> +#include <QCheckBox> +#include <QScrollArea> + +#include <Swift/QtUI/QtSwiftUtil.h> +#include <Swiften/Base/foreach.h> + +namespace Swift { + +QtFormWidget::QtFormWidget(Form::ref form, QWidget* parent) : QWidget(parent), form_(form) { +	QGridLayout* thisLayout = new QGridLayout(this); +	int row = 0; +	if (!form->getTitle().empty()) { +		QLabel* instructions = new QLabel(("<b>" + form->getTitle() + "</b>").c_str(), this); +		thisLayout->addWidget(instructions, row++, 0, 1, 2); +	} +	if (!form->getInstructions().empty()) { +		QLabel* instructions = new QLabel(form->getInstructions().c_str(), this); +		thisLayout->addWidget(instructions, row++, 0, 1, 2); +	} +	QScrollArea* scrollArea = new QScrollArea(this); +	thisLayout->addWidget(scrollArea); +	QWidget* scroll = new QWidget(this); +	QGridLayout* layout = new QGridLayout(scroll); +	foreach (boost::shared_ptr<FormField> field, form->getFields()) { +		QWidget* widget = createWidget(field); +		if (widget) { +			layout->addWidget(new QLabel(field->getLabel().c_str(), this), row, 0); +			layout->addWidget(widget, row++, 1); +		} +	} +	scrollArea->setWidget(scroll); +	scrollArea->setWidgetResizable(true); +} + +QtFormWidget::~QtFormWidget() { + +} + +QListWidget* QtFormWidget::createList(FormField::ref field) { +	QListWidget* listWidget = new QListWidget(this); +	listWidget->setSortingEnabled(false); +	listWidget->setSelectionMode(boost::dynamic_pointer_cast<ListMultiFormField>(field) ? QAbstractItemView::MultiSelection : QAbstractItemView::SingleSelection); +	foreach (FormField::Option option, field->getOptions()) { +		listWidget->addItem(option.label.c_str()); +	} +	boost::shared_ptr<ListMultiFormField> listMultiField = boost::dynamic_pointer_cast<ListMultiFormField>(field); +	boost::shared_ptr<ListSingleFormField> listSingleField = boost::dynamic_pointer_cast<ListSingleFormField>(field); +	for (int i = 0; i < listWidget->count(); i++) { +		QListWidgetItem* item = listWidget->item(i); +		bool selected = false; +		if (listSingleField) { +			selected = (item->text() == QString(listSingleField->getValue().c_str())); +		} +		else if (listMultiField) { +			std::string text = Q2PSTRING(item->text()); +			selected = (std::find(listMultiField->getValue().begin(), listMultiField->getValue().end(), text) != listMultiField->getValue().end()); +		} +		item->setSelected(selected); +	} +	return listWidget; +} + +QWidget* QtFormWidget::createWidget(FormField::ref field) { +	QWidget* widget = NULL; +	boost::shared_ptr<BooleanFormField> booleanField = boost::dynamic_pointer_cast<BooleanFormField>(field); +	if (booleanField) { +		QCheckBox* checkWidget = new QCheckBox(this); +		checkWidget->setCheckState(booleanField->getValue() ? Qt::Checked : Qt::Unchecked); +		widget = checkWidget; +	} +	boost::shared_ptr<FixedFormField> fixedField = boost::dynamic_pointer_cast<FixedFormField>(field); +	if (fixedField) { +		QString value = fixedField->getValue().c_str(); +		widget = new QLabel(value, this); +	} +	boost::shared_ptr<ListSingleFormField> listSingleField = boost::dynamic_pointer_cast<ListSingleFormField>(field); +	if (listSingleField) { +		widget = createList(field); +	} +	boost::shared_ptr<TextMultiFormField> textMultiField = boost::dynamic_pointer_cast<TextMultiFormField>(field); +	if (textMultiField) { +		QString value = textMultiField->getValue().c_str(); +		widget = new QTextEdit(value, this); +	} +	boost::shared_ptr<TextPrivateFormField> textPrivateField = boost::dynamic_pointer_cast<TextPrivateFormField>(field); +	if (textPrivateField) { +		QString value = textPrivateField->getValue().c_str(); +		QLineEdit* lineWidget = new QLineEdit(value, this); +		lineWidget->setEchoMode(QLineEdit::Password); +		widget = lineWidget; +	} +	boost::shared_ptr<TextSingleFormField> textSingleField = boost::dynamic_pointer_cast<TextSingleFormField>(field); +	if (textSingleField) { +		QString value = textSingleField->getValue().c_str(); +		widget = new QLineEdit(value, this); +	} +	boost::shared_ptr<JIDSingleFormField> jidSingleField = boost::dynamic_pointer_cast<JIDSingleFormField>(field); +	if (jidSingleField) { +		QString value = jidSingleField->getValue().toString().c_str(); +		widget = new QLineEdit(value, this); +	} +	boost::shared_ptr<JIDMultiFormField> jidMultiField = boost::dynamic_pointer_cast<JIDMultiFormField>(field); +	if (jidMultiField) { +		QString text; +		bool prev = false; +		foreach (JID line, jidMultiField->getValue()) { +			if (prev) { +				text += "\n"; +			} +			prev = true; +			text += line.toString().c_str(); +		} +		widget = new QTextEdit(text, this); +	} +	boost::shared_ptr<ListMultiFormField> listMultiField = boost::dynamic_pointer_cast<ListMultiFormField>(field); +	if (listMultiField) { +		widget = createList(field); +	} +	boost::shared_ptr<HiddenFormField> hiddenField = boost::dynamic_pointer_cast<HiddenFormField>(field); +	if (hiddenField) { +	} +	fields_[field->getName()] = widget; +	return widget; +} + +Form::ref QtFormWidget::getCompletedForm() { +	Form::ref result(new Form(Form::SubmitType)); +	foreach (boost::shared_ptr<FormField> field, form_->getFields()) { +		boost::shared_ptr<FormField> resultField; +		boost::shared_ptr<BooleanFormField> booleanField = boost::dynamic_pointer_cast<BooleanFormField>(field); +		if (booleanField) { +			resultField = FormField::ref(BooleanFormField::create(qobject_cast<QCheckBox*>(fields_[field->getName()])->checkState() == Qt::Checked)); +		} +		boost::shared_ptr<FixedFormField> fixedField = boost::dynamic_pointer_cast<FixedFormField>(field); +		if (fixedField) { +			resultField = FormField::ref(FixedFormField::create(fixedField->getValue())); +		} +		boost::shared_ptr<ListSingleFormField> listSingleField = boost::dynamic_pointer_cast<ListSingleFormField>(field); +		if (listSingleField) { +			QListWidget* listWidget = qobject_cast<QListWidget*>(fields_[field->getName()]); +			if (listWidget->selectedItems().size() > 0) { +				int i = listWidget->row(listWidget->selectedItems()[0]); +				resultField = FormField::ref(ListSingleFormField::create(field->getOptions()[i].value)); +			} +			else { +				resultField = FormField::ref(ListSingleFormField::create()); +			} +		} +		boost::shared_ptr<TextMultiFormField> textMultiField = boost::dynamic_pointer_cast<TextMultiFormField>(field); +		if (textMultiField) { +			QTextEdit* widget = qobject_cast<QTextEdit*>(fields_[field->getName()]); +			QString string = widget->toPlainText(); +			if (string.isEmpty()) { +				resultField = FormField::ref(TextMultiFormField::create()); +			} +			else { +				resultField = FormField::ref(TextMultiFormField::create(Q2PSTRING(string))); +			} +		} +		boost::shared_ptr<TextPrivateFormField> textPrivateField = boost::dynamic_pointer_cast<TextPrivateFormField>(field); +		if (textPrivateField) { +			QLineEdit* widget = qobject_cast<QLineEdit*>(fields_[field->getName()]); +			QString string = widget->text(); +			if (string.isEmpty()) { +				resultField = FormField::ref(TextPrivateFormField::create()); +			} +			else { +				resultField = FormField::ref(TextPrivateFormField::create(Q2PSTRING(string))); +			} +		} +		boost::shared_ptr<TextSingleFormField> textSingleField = boost::dynamic_pointer_cast<TextSingleFormField>(field); +		if (textSingleField) { +			QLineEdit* widget = qobject_cast<QLineEdit*>(fields_[field->getName()]); +			QString string = widget->text(); +			if (string.isEmpty()) { +				resultField = FormField::ref(TextSingleFormField::create()); +			} +			else { +				resultField = FormField::ref(TextSingleFormField::create(Q2PSTRING(string))); +			} +		} +		boost::shared_ptr<JIDSingleFormField> jidSingleField = boost::dynamic_pointer_cast<JIDSingleFormField>(field); +		if (jidSingleField) { +			QLineEdit* widget = qobject_cast<QLineEdit*>(fields_[field->getName()]); +			QString string = widget->text(); +			JID jid(Q2PSTRING(string)); +			if (string.isEmpty()) { +				resultField = FormField::ref(JIDSingleFormField::create()); +			} +			else { +				resultField = FormField::ref(JIDSingleFormField::create(jid)); +			} +		} +		boost::shared_ptr<JIDMultiFormField> jidMultiField = boost::dynamic_pointer_cast<JIDMultiFormField>(field); +		if (jidMultiField) { +			QTextEdit* widget = qobject_cast<QTextEdit*>(fields_[field->getName()]); +			QString string = widget->toPlainText(); +			if (string.isEmpty()) { +				resultField = FormField::ref(JIDMultiFormField::create()); +			} +			else { +				QStringList lines = string.split("\n"); +				std::vector<JID> value; +				foreach (QString line, lines) { +					value.push_back(JID(Q2PSTRING(line))); +				} +				resultField = FormField::ref(JIDMultiFormField::create(value)); +			} +		} +		boost::shared_ptr<ListMultiFormField> listMultiField = boost::dynamic_pointer_cast<ListMultiFormField>(field); +		if (listMultiField) { +			QListWidget* listWidget = qobject_cast<QListWidget*>(fields_[field->getName()]); +			std::vector<std::string> values; +			foreach (QListWidgetItem* item, listWidget->selectedItems()) { +				values.push_back(field->getOptions()[listWidget->row(item)].value); +			} +			resultField = FormField::ref(ListMultiFormField::create(values)); +		} +		boost::shared_ptr<HiddenFormField> hiddenField = boost::dynamic_pointer_cast<HiddenFormField>(field); +		if (hiddenField) { +			resultField = FormField::ref(HiddenFormField::create(hiddenField->getValue())); +		} +		resultField->setName(field->getName()); +		result->addField(resultField); +	} +	return result; +} + +} diff --git a/Swift/QtUI/QtFormWidget.h b/Swift/QtUI/QtFormWidget.h new file mode 100644 index 0000000..2fb7b98 --- /dev/null +++ b/Swift/QtUI/QtFormWidget.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <QWidget> + +#include <map> +#include <Swiften/Elements/Form.h> + +class QListWidget; + +namespace Swift { + +class QtFormWidget : public QWidget { +	Q_OBJECT +	public: +		QtFormWidget(Form::ref form, QWidget* parent = NULL); +		virtual ~QtFormWidget(); +		Form::ref getCompletedForm(); +	private: +		QWidget* createWidget(FormField::ref field); +		QListWidget* createList(FormField::ref field); +		std::map<std::string, QWidget*> fields_; +		Form::ref form_; +}; + +} diff --git a/Swift/QtUI/QtLoginWindow.cpp b/Swift/QtUI/QtLoginWindow.cpp index d0ab61e..4302c10 100644 --- a/Swift/QtUI/QtLoginWindow.cpp +++ b/Swift/QtUI/QtLoginWindow.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -56,9 +56,9 @@ QtLoginWindow::QtLoginWindow(UIEventStream* uiEventStream) : QMainWindow() {  	stack_ = new QStackedWidget(centralWidget);  	topLayout->addWidget(stack_);  	topLayout->setMargin(0); -	QWidget *wrapperWidget = new QWidget(this); -	wrapperWidget->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); -	QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, wrapperWidget); +	loginWidgetWrapper_ = new QWidget(this); +	loginWidgetWrapper_->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); +	QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, loginWidgetWrapper_);  	layout->addStretch(2);  	QLabel* logo = new QLabel(this); @@ -139,7 +139,7 @@ QtLoginWindow::QtLoginWindow(UIEventStream* uiEventStream) : QMainWindow() {  	layout->addWidget(loginAutomatically_);  	connect(loginButton_, SIGNAL(clicked()), SLOT(loginClicked())); -	stack_->addWidget(wrapperWidget); +	stack_->addWidget(loginWidgetWrapper_);  #ifdef SWIFTEN_PLATFORM_MACOSX  	menuBar_ = new QMenuBar(NULL);  #else @@ -284,11 +284,9 @@ void QtLoginWindow::handleUsernameTextChanged() {  }  void QtLoginWindow::loggedOut() { -	if (stack_->count() > 1) { -		QWidget* current = stack_->currentWidget(); -		stack_->setCurrentIndex(0); -		stack_->removeWidget(current); -	} +	stack_->removeWidget(stack_->currentWidget()); +	stack_->addWidget(loginWidgetWrapper_); +	stack_->setCurrentWidget(loginWidgetWrapper_);  	setInitialMenus();  	setIsLoggingIn(false);  } @@ -370,6 +368,7 @@ void QtLoginWindow::setInitialMenus() {  void QtLoginWindow::morphInto(MainWindow *mainWindow) {  	QtMainWindow *qtMainWindow = dynamic_cast<QtMainWindow*>(mainWindow);  	assert(qtMainWindow); +	stack_->removeWidget(loginWidgetWrapper_);  	stack_->addWidget(qtMainWindow);  	stack_->setCurrentWidget(qtMainWindow);  	setEnabled(true); diff --git a/Swift/QtUI/QtLoginWindow.h b/Swift/QtUI/QtLoginWindow.h index 3f3b5f8..b667a4b 100644 --- a/Swift/QtUI/QtLoginWindow.h +++ b/Swift/QtUI/QtLoginWindow.h @@ -1,11 +1,10 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#ifndef SWIFT_QtLoginWindow_H -#define SWIFT_QtLoginWindow_H +#pragma once  #include <QMainWindow>  #include <QPointer> @@ -65,6 +64,7 @@ namespace Swift {  		private:  			void setInitialMenus(); +			QWidget* loginWidgetWrapper_;  			QStringList usernames_;  			QStringList passwords_;  			QStringList certificateFiles_; @@ -87,5 +87,3 @@ namespace Swift {  			QPointer<QtAboutWidget> aboutDialog_;  	};  } - -#endif diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp index 6391961..0c959d6 100644 --- a/Swift/QtUI/QtMainWindow.cpp +++ b/Swift/QtUI/QtMainWindow.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -21,20 +21,25 @@  #include <QAction>  #include <QTabWidget> -#include "QtSwiftUtil.h" -#include "QtTabWidget.h" -#include "Roster/QtTreeWidget.h" -#include "Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h" -#include "Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h" -#include "Swift/Controllers/UIEvents/RequestChatWithUserDialogUIEvent.h" -#include "Swift/Controllers/UIEvents/RequestProfileEditorUIEvent.h" -#include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h" -#include "Swift/Controllers/UIEvents/ToggleShowOfflineUIEvent.h" +#include <Swift/QtUI/QtSwiftUtil.h> +#include <Swift/QtUI/QtTabWidget.h> +#include <Swift/QtUI/QtSettingsProvider.h> +#include <Roster/QtTreeWidget.h> +#include <Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestChatWithUserDialogUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestProfileEditorUIEvent.h> +#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h> +#include <Swift/Controllers/UIEvents/ToggleShowOfflineUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestAdHocUIEvent.h>  namespace Swift { +#define CURRENT_ROSTER_TAB "current_roster_tab" +  QtMainWindow::QtMainWindow(QtSettingsProvider* settings, UIEventStream* uiEventStream) : QWidget(), MainWindow(false) {  	uiEventStream_ = uiEventStream; +	settings_ = settings;  	setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));  	QBoxLayout *mainLayout = new QBoxLayout(QBoxLayout::TopToBottom, this);  	mainLayout->setContentsMargins(0,0,0,0); @@ -68,8 +73,12 @@ QtMainWindow::QtMainWindow(QtSettingsProvider* settings, UIEventStream* uiEventS  	chatListWindow_ = new QtChatListWindow(uiEventStream_); -	tabs_->addTab(eventWindow_, tr("&Notices"));  	tabs_->addTab(chatListWindow_, tr("C&hats")); +	tabs_->addTab(eventWindow_, tr("&Notices")); + +	tabs_->setCurrentIndex(settings_->getIntSetting(CURRENT_ROSTER_TAB, 0)); + +	connect(tabs_, SIGNAL(currentChanged(int)), this, SLOT(handleTabChanged(int)));  	this->setLayout(mainLayout); @@ -99,6 +108,8 @@ QtMainWindow::QtMainWindow(QtSettingsProvider* settings, UIEventStream* uiEventS  	chatUserAction_ = new QAction(tr("Start &Chat"), this);  	connect(chatUserAction_, SIGNAL(triggered(bool)), this, SLOT(handleChatUserActionTriggered(bool)));  	actionsMenu->addAction(chatUserAction_); +	serverAdHocMenu_ = new QMenu(tr("Run Server Command"), this); +	actionsMenu->addMenu(serverAdHocMenu_);  	actionsMenu->addSeparator();  	QAction* signOutAction = new QAction(tr("&Sign Out"), this);  	connect(signOutAction, SIGNAL(triggered()), SLOT(handleSignOutAction())); @@ -106,6 +117,12 @@ QtMainWindow::QtMainWindow(QtSettingsProvider* settings, UIEventStream* uiEventS  	connect(treeWidget_, SIGNAL(onSomethingSelectedChanged(bool)), editUserAction_, SLOT(setEnabled(bool))); +	setAvailableAdHocCommands(std::vector<DiscoItems::Item>()); +	QAction* adHocAction = new QAction(tr("Collecting commands..."), this); +	adHocAction->setEnabled(false); +	serverAdHocMenu_->addAction(adHocAction); +	serverAdHocCommandActions_.append(adHocAction); +  	lastOfflineState_ = false;  	uiEventStream_->onUIEvent.connect(boost::bind(&QtMainWindow::handleUIEvent, this, _1));  } @@ -114,6 +131,10 @@ QtMainWindow::~QtMainWindow() {  	uiEventStream_->onUIEvent.disconnect(boost::bind(&QtMainWindow::handleUIEvent, this, _1));  } +void QtMainWindow::handleTabChanged(int index) { +	settings_->storeInt(CURRENT_ROSTER_TAB, index); +} +  QtEventWindow* QtMainWindow::getEventWindow() {  	return eventWindow_;  } @@ -132,7 +153,7 @@ void QtMainWindow::handleEditProfileRequest() {  void QtMainWindow::handleEventCountUpdated(int count) {  	QColor eventTabColor = (count == 0) ? QColor() : QColor(255, 0, 0); // invalid resets to default -	int eventIndex = 1; +	int eventIndex = 2;  	tabs_->tabBar()->setTabTextColor(eventIndex, eventTabColor);  	QString text = tr("&Notices");  	if (count > 0) { @@ -206,6 +227,33 @@ void QtMainWindow::setConnecting() {  	meView_->setConnecting();  } +void QtMainWindow::handleAdHocActionTriggered(bool /*checked*/) { +	QAction* action = qobject_cast<QAction*>(sender()); +	assert(action); +	DiscoItems::Item command = serverAdHocCommands_[serverAdHocCommandActions_.indexOf(action)]; +	uiEventStream_->send(boost::shared_ptr<UIEvent>(new RequestAdHocUIEvent(command))); +} + +void QtMainWindow::setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands) { +	serverAdHocCommands_ = commands; +	foreach (QAction* action, serverAdHocCommandActions_) { +		delete action; +	} +	serverAdHocMenu_->clear(); +	serverAdHocCommandActions_.clear(); +	foreach (DiscoItems::Item command, commands) { +		QAction* action = new QAction(P2QSTRING(command.getName()), this); +		connect(action, SIGNAL(triggered(bool)), this, SLOT(handleAdHocActionTriggered(bool))); +		serverAdHocMenu_->addAction(action); +		serverAdHocCommandActions_.append(action); +	} +	if (serverAdHocCommandActions_.isEmpty()) { +		QAction* action = new QAction(tr("No Available Commands"), this); +		action->setEnabled(false); +		serverAdHocMenu_->addAction(action); +		serverAdHocCommandActions_.append(action); +	} +}  } diff --git a/Swift/QtUI/QtMainWindow.h b/Swift/QtUI/QtMainWindow.h index 3462bb0..5c29f6d 100644 --- a/Swift/QtUI/QtMainWindow.h +++ b/Swift/QtUI/QtMainWindow.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -8,6 +8,7 @@  #include <QWidget>  #include <QMenu> +#include <QList>  #include "Swift/Controllers/UIInterfaces/MainWindow.h"  #include "Swift/QtUI/QtRosterHeader.h"  #include "Swift/QtUI/EventViewer/QtEventWindow.h" @@ -20,7 +21,7 @@ class QLineEdit;  class QPushButton;  class QToolBar;  class QAction; - +class QMenu;  class QTabWidget;  namespace Swift { @@ -46,6 +47,7 @@ namespace Swift {  			QtEventWindow* getEventWindow();  			QtChatListWindow* getChatListWindow();  			void setRosterModel(Roster* roster); +			void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands);  		private slots:  			void handleStatusChanged(StatusShow::Type showType, const QString &statusMessage);  			void handleUIEvent(boost::shared_ptr<UIEvent> event); @@ -55,10 +57,13 @@ namespace Swift {  			void handleEditProfileAction();  			void handleAddUserActionTriggered(bool checked);  			void handleChatUserActionTriggered(bool checked); +			void handleAdHocActionTriggered(bool checked);  			void handleEventCountUpdated(int count);  			void handleEditProfileRequest(); +			void handleTabChanged(int index);  		private: +			QtSettingsProvider* settings_;  			std::vector<QMenu*> menus_;  			QtTreeWidget* treeWidget_;  			QtRosterHeader* meView_; @@ -66,6 +71,7 @@ namespace Swift {  			QAction* editUserAction_;  			QAction* chatUserAction_;  			QAction* showOfflineAction_; +			QMenu* serverAdHocMenu_;  			QtTabWidget* tabs_;  			QWidget* contactsTabWidget_;  			QWidget* eventsTabWidget_; @@ -73,5 +79,7 @@ namespace Swift {  			QtChatListWindow* chatListWindow_;  			UIEventStream* uiEventStream_;  			bool lastOfflineState_; +			std::vector<DiscoItems::Item> serverAdHocCommands_; +			QList<QAction*> serverAdHocCommandActions_;  	};  } diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp index d4c306f..d7a1f78 100644 --- a/Swift/QtUI/QtSwift.cpp +++ b/Swift/QtUI/QtSwift.cpp @@ -18,13 +18,11 @@  #include "QtUIFactory.h"  #include "QtChatWindowFactory.h"  #include <Swiften/Base/Log.h> -#include <Swift/Controllers/CertificateFileStorageFactory.h> +#include <Swift/Controllers/Storages/CertificateFileStorageFactory.h> +#include "Swift/Controllers/Storages/FileStoragesFactory.h"  #include "SwifTools/Application/PlatformApplicationPathProvider.h" -#include "Swiften/Avatars/AvatarFileStorage.h" -#include "Swiften/Disco/CapsFileStorage.h"  #include <string>  #include "Swiften/Base/Platform.h" -#include "Swift/Controllers/FileStoragesFactory.h"  #include "Swiften/Elements/Presence.h"  #include "Swiften/Client/Client.h"  #include "Swift/Controllers/MainController.h" @@ -32,20 +30,30 @@  #include "Swift/Controllers/BuildVersion.h"  #include "SwifTools/AutoUpdater/AutoUpdater.h"  #include "SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h" +  #if defined(SWIFTEN_PLATFORM_WINDOWS)  #include "WindowsNotifier.h" -#endif -#if defined(HAVE_GROWL) +#elif defined(HAVE_GROWL)  #include "SwifTools/Notifier/GrowlNotifier.h"  #elif defined(SWIFTEN_PLATFORM_LINUX)  #include "FreeDesktopNotifier.h"  #else  #include "SwifTools/Notifier/NullNotifier.h"  #endif +  #if defined(SWIFTEN_PLATFORM_MACOSX)  #include "SwifTools/Dock/MacOSXDock.h" -#endif +#else  #include "SwifTools/Dock/NullDock.h" +#endif + +#if defined(SWIFTEN_PLATFORM_MACOSX) +#include "QtURIHandler.h" +#elif defined(SWIFTEN_PLATFORM_WIN32) +#include <SwifTools/URIHandler/NullURIHandler.h> +#else +#include "QtDBUSURIHandler.h" +#endif  namespace Swift{ @@ -123,6 +131,14 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa  	dock_ = new NullDock();  #endif +#if defined(SWIFTEN_PLATFORM_MACOSX) +	uriHandler_ = new QtURIHandler(); +#elif defined(SWIFTEN_PLATFORM_WIN32) +	uriHandler_ = new NullURIHandler(); +#else +	uriHandler_ = new QtDBUSURIHandler(); +#endif +  	if (splitter_) {  		splitter_->show();  	} @@ -145,6 +161,7 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa  				certificateStorageFactory_,  				dock_,  				notifier_, +				uriHandler_,  				options.count("latency-debug") > 0);  		mainControllers_.push_back(mainController);  	} @@ -172,6 +189,7 @@ QtSwift::~QtSwift() {  	}  	delete tabs_;  	delete splitter_; +	delete uriHandler_;  	delete dock_;  	delete soundPlayer_;  	delete chatWindowFactory_; diff --git a/Swift/QtUI/QtSwift.h b/Swift/QtUI/QtSwift.h index 978fa14..4bf5c97 100644 --- a/Swift/QtUI/QtSwift.h +++ b/Swift/QtUI/QtSwift.h @@ -44,6 +44,7 @@ namespace Swift {  	class QtMUCSearchWindowFactory;  	class QtUserSearchWindowFactory;  	class EventLoop; +	class URIHandler;  	class QtSwift : public QObject {  		Q_OBJECT @@ -63,6 +64,7 @@ namespace Swift {  			QSplitter* splitter_;  			QtSoundPlayer* soundPlayer_;  			Dock* dock_; +			URIHandler* uriHandler_;  			QtChatTabs* tabs_;  			ApplicationPathProvider* applicationPathProvider_;  			StoragesFactory* storagesFactory_; diff --git a/Swift/QtUI/QtTextEdit.cpp b/Swift/QtUI/QtTextEdit.cpp index 3668220..3a62325 100644 --- a/Swift/QtUI/QtTextEdit.cpp +++ b/Swift/QtUI/QtTextEdit.cpp @@ -4,7 +4,7 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include "QtTextEdit.h" +#include <Swift/QtUI/QtTextEdit.h>  #include <QFontMetrics>  #include <QKeyEvent> @@ -22,19 +22,25 @@ void QtTextEdit::keyPressEvent(QKeyEvent* event) {  	if ((key == Qt::Key_Enter || key == Qt::Key_Return)  		&& (modifiers == Qt::NoModifier || modifiers == Qt::KeypadModifier)) {  		emit returnPressed(); -	} else if (((key == Qt::Key_PageUp || key == Qt::Key_PageDown) && modifiers == Qt::ShiftModifier) +	} +	else if (((key == Qt::Key_PageUp || key == Qt::Key_PageDown) && modifiers == Qt::ShiftModifier)  			   || (key == Qt::Key_C && modifiers == Qt::ControlModifier && textCursor().selectedText().isEmpty())  			   || (key == Qt::Key_W && modifiers == Qt::ControlModifier)  			   || (key == Qt::Key_PageUp && modifiers == Qt::ControlModifier)  			   || (key == Qt::Key_PageDown && modifiers == Qt::ControlModifier) -//			   || (key == Qt::Key_Left && modifiers == (Qt::ControlModifier | Qt::ShiftModifier)) -//			   || (key == Qt::Key_Right && modifiers == (Qt::ControlModifier | Qt::ShiftModifier))  			   || (key == Qt::Key_Tab && modifiers == Qt::ControlModifier)  			   || (key == Qt::Key_A && modifiers == Qt::AltModifier)  			   || (key == Qt::Key_Tab)  	) {  		emit unhandledKeyPressEvent(event); -	} else { +	} +	else if ((key == Qt::Key_Up) +			   || (key == Qt::Key_Down) +	){ +		emit unhandledKeyPressEvent(event); +		QTextEdit::keyPressEvent(event); +	} +	else {  		QTextEdit::keyPressEvent(event);  	}  } diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp index 35fbfcd..ffbe3c0 100644 --- a/Swift/QtUI/QtUIFactory.cpp +++ b/Swift/QtUI/QtUIFactory.cpp @@ -23,10 +23,14 @@  #include "UserSearch/QtUserSearchWindow.h"  #include "QtProfileWindow.h"  #include "QtContactEditWindow.h" +#include "QtAdHocCommandWindow.h" + +#define CHATWINDOW_FONT_SIZE "chatWindowFontSize"  namespace Swift {  QtUIFactory::QtUIFactory(QtSettingsProvider* settings, QtChatTabs* tabs, QSplitter* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, bool startMinimized) : settings(settings), tabs(tabs), netbookSplitter(netbookSplitter), systemTray(systemTray), chatWindowFactory(chatWindowFactory), lastMainWindow(NULL), loginWindow(NULL), startMinimized(startMinimized)  { +	chatFontSize = settings->getIntSetting(CHATWINDOW_FONT_SIZE, 0);  }  XMLConsoleWidget* QtUIFactory::createXMLConsoleWidget() { @@ -80,7 +84,26 @@ MUCSearchWindow* QtUIFactory::createMUCSearchWindow() {  }  ChatWindow* QtUIFactory::createChatWindow(const JID& contact, UIEventStream* eventStream) { -	return chatWindowFactory->createChatWindow(contact, eventStream); +	QtChatWindow* window = dynamic_cast<QtChatWindow*>(chatWindowFactory->createChatWindow(contact, eventStream)); +	chatWindows.push_back(window); +	foreach (QtChatWindow* existingWindow, chatWindows) { +		connect(window, SIGNAL(fontResized(int)), existingWindow, SLOT(handleFontResized(int))); +		connect(existingWindow, SIGNAL(fontResized(int)), window, SLOT(handleFontResized(int))); +	} +	connect(window, SIGNAL(fontResized(int)), this, SLOT(handleChatWindowFontResized(int))); +	connect(window, SIGNAL(destroyed(QObject*)), this, SLOT(handleChatWindowDestroyed(QObject*))); +	window->handleFontResized(chatFontSize); +	return window; +} + +void QtUIFactory::handleChatWindowFontResized(int size) { +	chatFontSize = size; +	settings->storeInt(CHATWINDOW_FONT_SIZE, size); +} + +void QtUIFactory::handleChatWindowDestroyed(QObject* window) { +	QtChatWindow* chatWindow = qobject_cast<QtChatWindow*>(window); +	chatWindows.erase(std::remove(chatWindows.begin(), chatWindows.end(), chatWindow), chatWindows.end());  }  UserSearchWindow* QtUIFactory::createUserSearchWindow(UserSearchWindow::Type type, UIEventStream* eventStream, const std::set<std::string>& groups) { @@ -99,5 +122,8 @@ ContactEditWindow* QtUIFactory::createContactEditWindow() {  	return new QtContactEditWindow();  } +void QtUIFactory::createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command) { +	new QtAdHocCommandWindow(command); +}  } diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h index ddaaf6e..828f1b4 100644 --- a/Swift/QtUI/QtUIFactory.h +++ b/Swift/QtUI/QtUIFactory.h @@ -20,6 +20,7 @@ namespace Swift {  	class QtMainWindow;  	class QtChatTheme;  	class QtChatWindowFactory; +	class QtChatWindow;  	class QtUIFactory : public QObject, public UIFactory {  			Q_OBJECT @@ -37,9 +38,12 @@ namespace Swift {  			virtual JoinMUCWindow* createJoinMUCWindow();  			virtual ProfileWindow* createProfileWindow();  			virtual ContactEditWindow* createContactEditWindow(); +			virtual void createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command);  		private slots:  			void handleLoginWindowGeometryChanged(); +			void handleChatWindowDestroyed(QObject*); +			void handleChatWindowFontResized(int);  		private:  			QtSettingsProvider* settings; @@ -49,6 +53,8 @@ namespace Swift {  			QtChatWindowFactory* chatWindowFactory;  			QtMainWindow* lastMainWindow;  			QtLoginWindow* loginWindow; +			std::vector<QtChatWindow*> chatWindows;  			bool startMinimized; +			int chatFontSize;  	};  } diff --git a/Swift/QtUI/QtURIHandler.cpp b/Swift/QtUI/QtURIHandler.cpp new file mode 100644 index 0000000..43f3ed1 --- /dev/null +++ b/Swift/QtUI/QtURIHandler.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "QtURIHandler.h" + +#include <QCoreApplication> +#include <QFileOpenEvent> +#include <QUrl> + +#include "QtSwiftUtil.h" +#ifdef Q_WS_MAC +#include <SwifTools/URIHandler/MacOSXURIHandlerHelpers.h> +#endif + +using namespace Swift; + +QtURIHandler::QtURIHandler() { +	qApp->installEventFilter(this); +#ifdef Q_WS_MAC +	registerAppAsDefaultXMPPURIHandler(); +#endif +} + +bool QtURIHandler::eventFilter(QObject*, QEvent* event) { +	if (event->type() == QEvent::FileOpen) { +		QFileOpenEvent* fileOpenEvent = static_cast<QFileOpenEvent*>(event); +		if (fileOpenEvent->url().scheme() == "xmpp") { +			onURI(Q2PSTRING(fileOpenEvent->url().toString())); +			return true; +		} +	} +	return false; +} diff --git a/Swift/QtUI/QtURIHandler.h b/Swift/QtUI/QtURIHandler.h new file mode 100644 index 0000000..a02114a --- /dev/null +++ b/Swift/QtUI/QtURIHandler.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <QObject> +#include <SwifTools/URIHandler/URIHandler.h> + +class QUrl; + +namespace Swift { +	class QtURIHandler : public QObject, public URIHandler { +		public: +			QtURIHandler(); + +		private: +			bool eventFilter(QObject* obj, QEvent* event); +	}; +} diff --git a/Swift/QtUI/QtWebView.cpp b/Swift/QtUI/QtWebView.cpp index 9d12010..5d25071 100644 --- a/Swift/QtUI/QtWebView.cpp +++ b/Swift/QtUI/QtWebView.cpp @@ -59,6 +59,8 @@ void QtWebView::contextMenuEvent(QContextMenuEvent* ev) {  	// Add our own custom actions  	menu->addAction(tr("Clear"), this, SIGNAL(clearRequested())); +	menu->addAction(tr("Increase font size"), this, SIGNAL(fontGrowRequested())); +	menu->addAction(tr("Decrease font size"), this, SIGNAL(fontShrinkRequested()));  	menu->exec(ev->globalPos());  	delete menu; diff --git a/Swift/QtUI/QtWebView.h b/Swift/QtUI/QtWebView.h index fbd31e3..eb5a82d 100644 --- a/Swift/QtUI/QtWebView.h +++ b/Swift/QtUI/QtWebView.h @@ -22,6 +22,8 @@ namespace Swift {  		signals:  			void gotFocus();  			void clearRequested(); +			void fontGrowRequested(); +			void fontShrinkRequested();  		protected:  			void focusInEvent(QFocusEvent* event); diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index 494731c..90d528a 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -28,13 +28,7 @@ if myenv["HAVE_XSS"] :  if myenv["HAVE_SPARKLE"] :    myenv.MergeFlags(env["SPARKLE_FLAGS"])  myenv.MergeFlags(env["SWIFTEN_FLAGS"]) -myenv.MergeFlags(env["LIBIDN_FLAGS"]) -myenv.MergeFlags(env["BOOST_FLAGS"]) -myenv.MergeFlags(env.get("SQLITE_FLAGS", {})) -myenv.MergeFlags(env["ZLIB_FLAGS"]) -myenv.MergeFlags(env["OPENSSL_FLAGS"]) -myenv.MergeFlags(env.get("LIBXML_FLAGS", "")) -myenv.MergeFlags(env.get("EXPAT_FLAGS", "")) +myenv.MergeFlags(env["SWIFTEN_DEP_FLAGS"])  if myenv.get("HAVE_GROWL", False) :  	myenv.MergeFlags(myenv["GROWL_FLAGS"])  	myenv.Append(CPPDEFINES = ["HAVE_GROWL"]) @@ -77,6 +71,7 @@ sources = [      "QtStatusWidget.cpp",  		"QtScaledAvatarCache.cpp",      "QtSwift.cpp", +    "QtURIHandler.cpp",      "QtChatView.cpp",      "QtChatTheme.cpp",      "QtChatTabs.cpp", @@ -87,6 +82,7 @@ sources = [      "QtTabWidget.cpp",      "QtTextEdit.cpp",      "QtXMLConsoleWidget.cpp", +    "QtAdHocCommandWindow.cpp",      "QtUtilities.cpp",      "QtBookmarkDetailWindow.cpp",      "QtAddBookmarkWindow.cpp", @@ -97,6 +93,7 @@ sources = [      "MessageSnippet.cpp",      "SystemMessageSnippet.cpp",      "QtElidingLabel.cpp", +    "QtFormWidget.cpp",      "QtLineEdit.cpp",      "QtJoinMUCWindow.cpp",      "Roster/RosterModel.cpp", @@ -114,6 +111,7 @@ sources = [      "ChatList/ChatListModel.cpp",      "ChatList/ChatListDelegate.cpp",      "ChatList/ChatListMUCItem.cpp", +    "ChatList/ChatListRecentItem.cpp",      "MUCSearch/QtMUCSearchWindow.cpp",      "MUCSearch/MUCSearchModel.cpp",      "MUCSearch/MUCSearchRoomItem.cpp", @@ -136,20 +134,31 @@ sources = [  myenv["SWIFT_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift")  if env["PLATFORM"] == "win32" : -  myenv.RES("../resources/Windows/Swift.rc") +  res = myenv.RES("../resources/Windows/Swift.rc") +  # For some reason, SCons isn't picking up the dependency correctly +	# Adding it explicitly until i figure out why +  myenv.Depends(res, "../Controllers/BuildVersion.h")    sources += [  			"WindowsNotifier.cpp",  			"../resources/Windows/Swift.res"  		]  if env["PLATFORM"] == "posix" : -	sources += ["FreeDesktopNotifier.cpp"] +	sources += [ +			"FreeDesktopNotifier.cpp", +			"QtDBUSURIHandler.cpp", +	]  if env["PLATFORM"] == "darwin" or env["PLATFORM"] == "win32" :    swiftProgram = myenv.Program("Swift", sources)  else :    swiftProgram = myenv.Program("swift", sources) +if env["PLATFORM"] != "darwin" and env["PLATFORM"] != "win32" : +	openURIProgram = myenv.Program("swift-open-uri", "swift-open-uri.cpp") +else : +	openURIProgram = [] +  myenv.Uic4("MUCSearch/QtMUCSearchWindow.ui")  myenv.Uic4("UserSearch/QtUserSearchWizard.ui")  myenv.Uic4("UserSearch/QtUserSearchFirstPage.ui") @@ -215,12 +224,12 @@ if env["PLATFORM"] == "darwin" :    if env["HAVE_GROWL"] :      frameworks.append(env["GROWL_FRAMEWORK"])    commonResources[""] = commonResources.get("", []) + ["../resources/MacOSX/Swift.icns"] -  app = myenv.AppBundle("Swift", version = myenv["SWIFT_VERSION"], resources = commonResources, frameworks = frameworks) +  app = myenv.AppBundle("Swift", version = myenv["SWIFT_VERSION"], resources = commonResources, frameworks = frameworks, handlesXMPPURIs = True)    if env["DIST"] :      myenv.Command(["Swift-${SWIFT_VERSION}.dmg"], [app], ["Swift/Packaging/MacOSX/package.sh " + app.path + " Swift/Packaging/MacOSX/Swift.dmg.gz $TARGET $QTDIR"])  if env.get("SWIFT_INSTALLDIR", "") : -  env.Install(os.path.join(env["SWIFT_INSTALLDIR"], "bin"), swiftProgram) +  env.Install(os.path.join(env["SWIFT_INSTALLDIR"], "bin"), swiftProgram + openURIProgram)    env.InstallAs(os.path.join(env["SWIFT_INSTALLDIR"], "share", "pixmaps", "swift.xpm"), "../resources/logo/logo-icon-32.xpm")    icons_path = os.path.join(env["SWIFT_INSTALLDIR"], "share", "icons", "hicolor")    env.InstallAs(os.path.join(icons_path, "32x32", "apps", "swift.xpm"), "../resources/logo/logo-icon-32.xpm") diff --git a/Swift/QtUI/swift-open-uri.cpp b/Swift/QtUI/swift-open-uri.cpp new file mode 100644 index 0000000..2d5ef19 --- /dev/null +++ b/Swift/QtUI/swift-open-uri.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <QCoreApplication> +#include <QDBusConnection> +#include <QDBusMessage> +#include <iostream> + +int main(int argc, char* argv[]) { +	QCoreApplication app(argc, argv); + +	QDBusConnection bus = QDBusConnection::sessionBus(); +	if (!bus.isConnected()) { +		return -1; +	} +	if (argc != 2) { +		std::cerr << "Usage: " << argv[0] << " uri" << std::endl; +		return -1; +	} + +	QDBusMessage msg = QDBusMessage::createMethodCall("im.swift.Swift.URIHandler", "/", "im.swift.Swift.URIHandler", "openURI"); +	msg << argv[1]; + +	bus.call(msg); + +	return 0; +} | 
 Swift
 Swift