From 705bd7256fa4812045677743fc1e939ccfd66d05 Mon Sep 17 00:00:00 2001 From: Kevin Smith <git@kismith.co.uk> Date: Sat, 8 May 2010 12:14:01 +0000 Subject: List MUCs available on services. Resolves: #276 diff --git a/Swift/Controllers/Chat/MUCSearchController.cpp b/Swift/Controllers/Chat/MUCSearchController.cpp new file mode 100644 index 0000000..93d2087 --- /dev/null +++ b/Swift/Controllers/Chat/MUCSearchController.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swift/Controllers/Chat/MUCSearchController.h" + +#include <iostream> + +#include <boost/bind.hpp> +#include <boost/shared_ptr.hpp> + +#include "Swiften/Queries/Requests/GetDiscoInfoRequest.h" +#include "Swiften/Queries/Requests/GetDiscoItemsRequest.h" + +#include "Swift/Controllers/UIEvents/UIEventStream.h" +#include "Swift/Controllers/UIEvents/RequestMUCSearchUIEvent.h" +#include "Swift/Controllers/UIInterfaces/MUCSearchWindow.h" +#include "Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h" + +namespace Swift { + +MUCSearchController::MUCSearchController(const JID& jid, UIEventStream* uiEventStream, MUCSearchWindowFactory* factory, IQRouter* iqRouter) : jid_(jid) { + iqRouter_ = iqRouter; + uiEventStream_ = uiEventStream; + uiEventConnection_ = uiEventStream_->onUIEvent.connect(boost::bind(&MUCSearchController::handleUIEvent, this, _1)); + window_ = NULL; + factory_ = factory; +} + +MUCSearchController::~MUCSearchController() { + delete window_; +} + +void MUCSearchController::handleUIEvent(boost::shared_ptr<UIEvent> event) { + boost::shared_ptr<RequestMUCSearchUIEvent> searchEvent = boost::dynamic_pointer_cast<RequestMUCSearchUIEvent>(event); + if (searchEvent) { + if (!window_) { + window_ = factory_->createMUCSearchWindow(uiEventStream_); + window_->onAddService.connect(boost::bind(&MUCSearchController::handleAddService, this, _1, true)); + handleAddService(JID(jid_.getDomain()), true); + } + window_->setMUC(""); + window_->setNick(jid_.getNode()); + window_->show(); + return; + } +} + +void MUCSearchController::handleAddService(const JID& jid, bool userTriggered) { + if (std::find(services_.begin(), services_.end(), jid) != services_.end()) { + if (!userTriggered) { + /* No infinite recursion. (Some buggy servers do infinitely deep disco of themselves)*/ + return; + } + } else if (userTriggered) { + services_.push_back(jid); + serviceDetails_[jid].setComplete(false); + refreshView(); + } + if (!jid.isValid()) { + //Set Window to say error this isn't valid + return; + } + boost::shared_ptr<GetDiscoInfoRequest> discoInfoRequest(new GetDiscoInfoRequest(jid, iqRouter_)); + discoInfoRequest->onResponse.connect(boost::bind(&MUCSearchController::handleDiscoInfoResponse, this, _1, _2, jid)); + discoInfoRequest->send(); +} + +void MUCSearchController::removeService(const JID& jid) { + serviceDetails_.erase(jid); + services_.erase(std::remove(services_.begin(), services_.end(), jid), services_.end()); + refreshView(); +} + +void MUCSearchController::handleDiscoInfoResponse(boost::shared_ptr<DiscoInfo> info, const boost::optional<ErrorPayload>& error, const JID& jid) { + if (error) { + handleDiscoError(jid, error.get()); + return; + } + boost::shared_ptr<GetDiscoItemsRequest> discoItemsRequest(new GetDiscoItemsRequest(jid, iqRouter_)); + bool mucService = false; + bool couldContainServices = false; + String name; + foreach (DiscoInfo::Identity identity, info->getIdentities()) { + if ((identity.getCategory() == "directory" + && identity.getType() == "chatroom") + || (identity.getCategory() == "conference" + && identity.getType() == "text")) { + mucService = true; + name = identity.getName(); + } + if (identity.getCategory() == "server") { + couldContainServices = true; + name = identity.getName(); + } + } + services_.erase(std::remove(services_.begin(), services_.end(), jid), services_.end()); /* Bring it back to the end on a refresh */ + services_.push_back(jid); + serviceDetails_[jid].setName(name); + serviceDetails_[jid].setJID(jid); + serviceDetails_[jid].setComplete(false); + + if (mucService) { + discoItemsRequest->onResponse.connect(boost::bind(&MUCSearchController::handleRoomsItemsResponse, this, _1, _2, jid)); + } else if (couldContainServices) { + discoItemsRequest->onResponse.connect(boost::bind(&MUCSearchController::handleServerItemsResponse, this, _1, _2, jid)); + } else { + removeService(jid); + return; + } + discoItemsRequest->send(); + refreshView(); +} + +void MUCSearchController::handleRoomsItemsResponse(boost::shared_ptr<DiscoItems> items, const boost::optional<ErrorPayload>& error, const JID& jid) { + if (error) { + handleDiscoError(jid, error.get()); + return; + } + serviceDetails_[jid].clearRooms(); + foreach (DiscoItems::Item item, items->getItems()) { + serviceDetails_[jid].addRoom(MUCService::MUCRoom(item.getJID().getNode(), item.getName(), -1)); + } + serviceDetails_[jid].setComplete(true); + refreshView(); +} + +void MUCSearchController::handleServerItemsResponse(boost::shared_ptr<DiscoItems> items, const boost::optional<ErrorPayload>& error, const JID& jid) { + if (error) { + handleDiscoError(jid, error.get()); + return; + } + if (jid.isValid()) { + removeService(jid); + } + foreach (DiscoItems::Item item, items->getItems()) { + handleAddService(item.getJID()); + } + refreshView(); +} + +void MUCSearchController::handleDiscoError(const JID& jid, const ErrorPayload& error) { + serviceDetails_[jid].setComplete(true); + serviceDetails_[jid].setError(error.getText()); +} + +void MUCSearchController::refreshView() { + window_->clearList(); + foreach (JID jid, services_) { + window_->addService(serviceDetails_[jid]); + } +} + +} diff --git a/Swift/Controllers/Chat/MUCSearchController.h b/Swift/Controllers/Chat/MUCSearchController.h new file mode 100644 index 0000000..6eba2a5 --- /dev/null +++ b/Swift/Controllers/Chat/MUCSearchController.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <vector> +#include <map> + +#include <boost/shared_ptr.hpp> +#include <boost/signals.hpp> + +#include "Swiften/Base/String.h" +#include "Swiften/JID/JID.h" + +#include "Swift/Controllers/UIEvents/UIEvent.h" +#include "Swift/Controllers/Chat/MUCSearchController.h" +#include "Swiften/Elements/DiscoInfo.h" +#include "Swiften/Elements/DiscoItems.h" +#include "Swiften/Elements/ErrorPayload.h" + +namespace Swift { + class UIEventStream; + class MUCSearchWindow; + class MUCSearchWindowFactory; + class IQRouter; + + class MUCService { + public: + class MUCRoom { + public: + MUCRoom(const String& node, const String& name, int occupants) : node_(node), name_(name), occupants_(occupants) {} + String getNode() {return node_;} + String getName() {return name_;} + int getOccupantCount() {return occupants_;} + private: + String node_; + String name_; + int occupants_; + }; + + MUCService() {error_ = false; complete_ = false;} + + void setComplete(bool complete) { + complete_ = complete; + } + + void setName(const String& name) { + name_ = name; + } + + void setJID(const JID& jid) { + jid_ = jid; + } + + bool getComplete() const { + return complete_; + } + + JID getJID() const { + return jid_; + } + + String getName() const { + return name_; + } + + void setError(const String& errorText) {error_ = true; errorText_ = errorText;} + + void clearRooms() {rooms_.clear();} + + void addRoom(const MUCRoom& room) {rooms_.push_back(room);} + + std::vector<MUCRoom> getRooms() const {return rooms_;} + private: + String name_; + JID jid_; + std::vector<MUCRoom> rooms_; + bool complete_; + bool error_; + String errorText_; + }; + + class MUCSearchController { + public: + MUCSearchController(const JID& jid, UIEventStream* uiEventStream, MUCSearchWindowFactory* mucSearchWindowFactory, IQRouter* iqRouter); + ~MUCSearchController(); + private: + void handleUIEvent(boost::shared_ptr<UIEvent> event); + void handleAddService(const JID& jid, bool userTriggered=false); + void handleDiscoInfoResponse(boost::shared_ptr<DiscoInfo> info, const boost::optional<ErrorPayload>& error, const JID& jid); + void handleRoomsItemsResponse(boost::shared_ptr<DiscoItems> items, const boost::optional<ErrorPayload>& error, const JID& jid); + void handleServerItemsResponse(boost::shared_ptr<DiscoItems> items, const boost::optional<ErrorPayload>& error, const JID& jid); + void handleDiscoError(const JID& jid, const ErrorPayload& error); + void removeService(const JID& jid); + void refreshView(); + UIEventStream* uiEventStream_; + MUCSearchWindow* window_; + MUCSearchWindowFactory* factory_; + boost::bsignals::scoped_connection uiEventConnection_; + std::vector<JID> services_; + std::map<JID, MUCService> serviceDetails_; + IQRouter* iqRouter_; + JID jid_; + }; +} diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp index e6dfe47..5be8e46 100644 --- a/Swift/Controllers/MainController.cpp +++ b/Swift/Controllers/MainController.cpp @@ -14,15 +14,16 @@ #include "Swiften/Application/Application.h" #include "Swiften/Application/ApplicationMessageDisplay.h" #include "Swift/Controllers/Chat/ChatController.h" -#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" +#include "Swift/Controllers/Chat/MUCSearchController.h" +//#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" #include "Swift/Controllers/Chat/ChatsManager.h" #include "Swift/Controllers/EventController.h" #include "Swift/Controllers/EventWindowController.h" #include "Swift/Controllers/UIInterfaces/LoginWindow.h" #include "Swift/Controllers/UIInterfaces/LoginWindowFactory.h" -#include "Swift/Controllers/UIInterfaces/EventWindowFactory.h" +//#include "Swift/Controllers/UIInterfaces/EventWindowFactory.h" #include "Swift/Controllers/UIInterfaces/MainWindow.h" -#include "Swift/Controllers/UIInterfaces/MainWindowFactory.h" +//#include "Swift/Controllers/UIInterfaces/MainWindowFactory.h" #include "Swift/Controllers/Chat/MUCController.h" #include "Swift/Controllers/NickResolver.h" #include "Swift/Controllers/ProfileSettingsProvider.h" @@ -33,7 +34,7 @@ #include "Swift/Controllers/SystemTrayController.h" #include "Swift/Controllers/XMLConsoleController.h" #include "Swift/Controllers/XMPPRosterController.h" -#include "Swift/Controllers/UIInterfaces/XMLConsoleWidgetFactory.h" +//#include "Swift/Controllers/UIInterfaces/XMLConsoleWidgetFactory.h" #include "Swift/Controllers/UIEvents/UIEventStream.h" #include "Swiften/Base/foreach.h" #include "Swiften/Base/String.h" @@ -60,7 +61,7 @@ static const String CLIENT_VERSION = "0.3"; static const String CLIENT_NODE = "http://swift.im"; -MainController::MainController(ChatWindowFactory* chatWindowFactory, MainWindowFactory *mainWindowFactory, LoginWindowFactory *loginWindowFactory, EventWindowFactory* eventWindowFactory, SettingsProvider *settings, Application* application, SystemTray* systemTray, SoundPlayer* soundPlayer, XMLConsoleWidgetFactory* xmlConsoleWidgetFactory, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency) +MainController::MainController(ChatWindowFactory* chatWindowFactory, MainWindowFactory *mainWindowFactory, LoginWindowFactory *loginWindowFactory, EventWindowFactory* eventWindowFactory, SettingsProvider *settings, Application* application, SystemTray* systemTray, SoundPlayer* soundPlayer, XMLConsoleWidgetFactory* xmlConsoleWidgetFactory, ChatListWindowFactory* chatListWindowFactory, MUCSearchWindowFactory* mucSearchWindowFactory, bool useDelayForLatency) : timerFactory_(&boostIOServiceThread_.getIOService()), idleDetector_(&idleQuerier_, &timerFactory_, 100), chatWindowFactory_(chatWindowFactory), mainWindowFactory_(mainWindowFactory), loginWindowFactory_(loginWindowFactory), settings_(settings), loginWindow_(NULL), useDelayForLatency_(useDelayForLatency) { application_ = application; presenceOracle_ = NULL; @@ -77,6 +78,7 @@ MainController::MainController(ChatWindowFactory* chatWindowFactory, MainWindowF presenceSender_ = NULL; client_ = NULL; + mucSearchWindowFactory_ = mucSearchWindowFactory; eventWindowFactory_ = eventWindowFactory; chatListWindowFactory_ = chatListWindowFactory; uiEventStream_ = new UIEventStream(); @@ -155,6 +157,8 @@ void MainController::resetClient() { presenceSender_ = NULL; delete client_; client_ = NULL; + delete mucSearchController_; + mucSearchController_ = NULL; } @@ -203,6 +207,8 @@ void MainController::handleConnected() { discoResponder_->setDiscoInfo(discoInfo); discoResponder_->setDiscoInfo(capsInfo_->getNode() + "#" + capsInfo_->getVersion(), discoInfo); serverDiscoInfo_ = boost::shared_ptr<DiscoInfo>(new DiscoInfo()); + + mucSearchController_ = new MUCSearchController(jid_, uiEventStream_, mucSearchWindowFactory_, client_); } boost::shared_ptr<GetDiscoInfoRequest> discoInfoRequest(new GetDiscoInfoRequest(JID(), client_)); diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h index 5d3f998..63e77f2 100644 --- a/Swift/Controllers/MainController.h +++ b/Swift/Controllers/MainController.h @@ -58,10 +58,12 @@ namespace Swift { class XMLConsoleWidgetFactory; class EventWindowFactory; class EventWindowController; + class MUCSearchController; + class MUCSearchWindowFactory; class MainController { public: - MainController(ChatWindowFactory* chatWindowFactory, MainWindowFactory *mainWindowFactory, LoginWindowFactory *loginWindowFactory, EventWindowFactory* eventWindowFactory, SettingsProvider *settings, Application* application, SystemTray* systemTray, SoundPlayer* soundPlayer, XMLConsoleWidgetFactory* xmlConsoleWidgetFactory, ChatListWindowFactory* chatListWindowFactory_, bool useDelayForLatency); + MainController(ChatWindowFactory* chatWindowFactory, MainWindowFactory *mainWindowFactory, LoginWindowFactory *loginWindowFactory, EventWindowFactory* eventWindowFactory, SettingsProvider *settings, Application* application, SystemTray* systemTray, SoundPlayer* soundPlayer, XMLConsoleWidgetFactory* xmlConsoleWidgetFactory, ChatListWindowFactory* chatListWindowFactory_, MUCSearchWindowFactory* mucSearchWindowFactory, bool useDelayForLatency); ~MainController(); @@ -126,5 +128,7 @@ namespace Swift { ChatListWindowFactory* chatListWindowFactory_; boost::shared_ptr<ErrorEvent> lastDisconnectError_; bool useDelayForLatency_; + MUCSearchController* mucSearchController_; + MUCSearchWindowFactory* mucSearchWindowFactory_; }; } diff --git a/Swift/Controllers/SConscript b/Swift/Controllers/SConscript index a7ddc79..eb63bed 100644 --- a/Swift/Controllers/SConscript +++ b/Swift/Controllers/SConscript @@ -21,11 +21,12 @@ if env["SCONS_STAGE"] == "build" : "Chat/ChatController.cpp", "Chat/ChatControllerBase.cpp", "Chat/ChatsManager.cpp", + "Chat/MUCController.cpp", + "Chat/MUCSearchController.cpp", "MainController.cpp", "NickResolver.cpp", "RosterController.cpp", "XMPPRosterController.cpp", - "Chat/MUCController.cpp", "EventController.cpp", "EventWindowController.cpp", "SoundEventController.cpp", diff --git a/Swift/Controllers/UIEvents/RequestMUCSearchUIEvent.h b/Swift/Controllers/UIEvents/RequestMUCSearchUIEvent.h new file mode 100644 index 0000000..623cd00 --- /dev/null +++ b/Swift/Controllers/UIEvents/RequestMUCSearchUIEvent.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include "Swift/Controllers/UIEvents/UIEvent.h" + +namespace Swift { + class RequestMUCSearchUIEvent : public UIEvent { + + }; +} diff --git a/Swift/Controllers/UIInterfaces/MUCSearchWindow.h b/Swift/Controllers/UIInterfaces/MUCSearchWindow.h new file mode 100644 index 0000000..3114a5a --- /dev/null +++ b/Swift/Controllers/UIInterfaces/MUCSearchWindow.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <boost/signals.hpp> + +#include "Swiften/Base/String.h" +#include "Swiften/JID/JID.h" + +#include "Swift/Controllers/Chat/MUCSearchController.h" + +namespace Swift { + + class MUCSearchWindow { + public: + virtual ~MUCSearchWindow() {}; + + virtual void setNick(const String& nick) = 0; + virtual void setMUC(const String& nick) = 0; + virtual void clearList() = 0; + virtual void addService(const MUCService& service) = 0; + + virtual void show() = 0; + + boost::signal<void (const JID&)> onAddService; + }; +} diff --git a/Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h b/Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h new file mode 100644 index 0000000..1f0bf90 --- /dev/null +++ b/Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include "Swift/Controllers/UIInterfaces/MUCSearchWindow.h" + +namespace Swift { + class UIEventStream; + class MUCSearchWindowFactory { + public: + virtual ~MUCSearchWindowFactory() {}; + + virtual MUCSearchWindow* createMUCSearchWindow(UIEventStream* eventStream) = 0; + }; +} diff --git a/Swift/QtUI/MUCSearch/MUCSearchDelegate.cpp b/Swift/QtUI/MUCSearch/MUCSearchDelegate.cpp new file mode 100644 index 0000000..be92efe --- /dev/null +++ b/Swift/QtUI/MUCSearch/MUCSearchDelegate.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <QPen> +#include <QPainter> + +#include "Swift/QtUI/MUCSearch/MUCSearchDelegate.h" +#include "Swift/QtUI/Roster/GroupItemDelegate.h" +#include "Swift/QtUI/MUCSearch/MUCSearchItem.h" +#include "Swift/QtUI/MUCSearch/MUCSearchRoomItem.h" +#include "Swift/QtUI/MUCSearch/MUCSearchServiceItem.h" + +namespace Swift { + +MUCSearchDelegate::MUCSearchDelegate() { + +} + +MUCSearchDelegate::~MUCSearchDelegate() { + +} + +// QSize MUCSearchDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index ) const { +// // MUCSearchItem* item = static_cast<MUCSearchItem*>(index.internalPointer()); +// // if (item && dynamic_cast<MUCSearchMUCItem*>(item)) { +// // return mucSizeHint(option, index); +// // } else if (item && dynamic_cast<MUCSearchGroupItem*>(item)) { +// // return groupDelegate_->sizeHint(option, index); +// // } +// return QStyledItemDelegate::sizeHint(option, index); +// } + +// QSize MUCSearchDelegate::mucSizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const { +// QFontMetrics nameMetrics(common_.nameFont); +// QFontMetrics statusMetrics(common_.detailFont); +// int sizeByText = 2 * common_.verticalMargin + nameMetrics.height() + statusMetrics.height(); +// return QSize(150, sizeByText); +// } + +// void MUCSearchDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { +// MUCSearchItem* item = static_cast<MUCSearchItem*>(index.internalPointer()); +// if (item && dynamic_cast<MUCSearchMUCItem*>(item)) { +// paintMUC(painter, option, dynamic_cast<MUCSearchMUCItem*>(item)); +// } else if (item && dynamic_cast<MUCSearchGroupItem*>(item)) { +// MUCSearchGroupItem* group = dynamic_cast<MUCSearchGroupItem*>(item); +// groupDelegate_->paint(painter, option, group->data(Qt::DisplayRole).toString(), group->rowCount(), option.state & QStyle::State_Open); +// } else { +// QStyledItemDelegate::paint(painter, option, index); +// } +// } + +// void MUCSearchDelegate::paintMUC(QPainter* painter, const QStyleOptionViewItem& option, MUCSearchMUCItem* 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)); + +// painter->drawText(nameRegion, Qt::AlignTop, item->data(Qt::DisplayRole).toString()); + +// painter->setFont(common_.detailFont); +// painter->setPen(QPen(QColor(160,160,160))); + +// QRect detailRegion(textRegion.adjusted(0, nameHeight, 0, 0)); +// painter->drawText(detailRegion, Qt::AlignTop, item->data(DetailTextRole).toString()); + +// painter->restore(); +// } + +} diff --git a/Swift/QtUI/MUCSearch/MUCSearchDelegate.h b/Swift/QtUI/MUCSearch/MUCSearchDelegate.h new file mode 100644 index 0000000..2662bb9 --- /dev/null +++ b/Swift/QtUI/MUCSearch/MUCSearchDelegate.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <QStyledItemDelegate> + +#include "Swift/QtUI/Roster/DelegateCommons.h" + +namespace Swift { + class MUCSearchDelegate : public QStyledItemDelegate { + public: + MUCSearchDelegate(); + ~MUCSearchDelegate(); + /* QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; */ + /* void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; */ + private: +// void paintMUC(QPainter* painter, const QStyleOptionViewItem& option, MUCSearchMUCItem* item) const; +// QSize mucSizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const; + + DelegateCommons common_; + }; + +} + diff --git a/Swift/QtUI/MUCSearch/MUCSearchItem.h b/Swift/QtUI/MUCSearch/MUCSearchItem.h new file mode 100644 index 0000000..7bac25d --- /dev/null +++ b/Swift/QtUI/MUCSearch/MUCSearchItem.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <QVariant> + +namespace Swift { + class MUCSearchItem { + public: + virtual ~MUCSearchItem() {} + virtual QVariant data(int role) = 0; + }; +} diff --git a/Swift/QtUI/MUCSearch/MUCSearchModel.cpp b/Swift/QtUI/MUCSearch/MUCSearchModel.cpp new file mode 100644 index 0000000..eb7fe20 --- /dev/null +++ b/Swift/QtUI/MUCSearch/MUCSearchModel.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swift/QtUI/MUCSearch/MUCSearchModel.h" + +namespace Swift { + +MUCSearchModel::MUCSearchModel() { +} + +void MUCSearchModel::clear() { + emit layoutAboutToBeChanged(); + services_.clear(); + emit layoutChanged(); +} + +void MUCSearchModel::addService(MUCSearchServiceItem* service) { + emit layoutAboutToBeChanged(); + services_.push_back(service); + emit layoutChanged(); +} + +int MUCSearchModel::columnCount(const QModelIndex& /*parent*/) const { + return 1; +} + +QVariant MUCSearchModel::data(const QModelIndex& index, int role) const { + return index.isValid() ? static_cast<MUCSearchItem*>(index.internalPointer())->data(role) : QVariant(); +} + +QModelIndex MUCSearchModel::index(int row, int column, const QModelIndex & parent) const { + if (!hasIndex(row, column, parent)) { + return QModelIndex(); + } + + if (parent.isValid()) { + MUCSearchServiceItem* parentItem = static_cast<MUCSearchServiceItem*>(parent.internalPointer()); + return row < parentItem->rowCount() ? createIndex(row, column, parentItem->getItem(row)) : QModelIndex(); + + } else { + return row < services_.size() ? createIndex(row, column, services_[row]) : QModelIndex(); + } + + +} + +QModelIndex MUCSearchModel::parent(const QModelIndex& index) const { + if (!index.isValid()) { + return QModelIndex(); + } + MUCSearchItem* item = static_cast<MUCSearchItem*>(index.internalPointer()); + if (!item) { + return QModelIndex(); + } + if (dynamic_cast<MUCSearchServiceItem*>(item)) { + return QModelIndex(); + } + MUCSearchServiceItem* parent = dynamic_cast<MUCSearchRoomItem*>(item)->getParent(); + int row = services_.indexOf(parent); + return parent ? createIndex(row, 1, parent) : QModelIndex(); +} + +int MUCSearchModel::rowCount(const QModelIndex& parentIndex) const { + if (!parentIndex.isValid()) { + return services_.size(); + } + if (dynamic_cast<MUCSearchServiceItem*>(static_cast<MUCSearchItem*>(parentIndex.internalPointer()))) { + return services_[parentIndex.row()]->rowCount(); + } else { + return 0; + } +} + +} diff --git a/Swift/QtUI/MUCSearch/MUCSearchModel.h b/Swift/QtUI/MUCSearch/MUCSearchModel.h new file mode 100644 index 0000000..0c02c72 --- /dev/null +++ b/Swift/QtUI/MUCSearch/MUCSearchModel.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <boost/shared_ptr.hpp> + +#include <QAbstractItemModel> +#include <QList> + +#include "Swift/QtUI/MUCSearch/MUCSearchServiceItem.h" + +namespace Swift { + class MUCSearchModel : public QAbstractItemModel { + Q_OBJECT + public: + MUCSearchModel(); + void clear(); + void addService(MUCSearchServiceItem* service); + int columnCount(const QModelIndex& parent = QModelIndex()) const; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex& index) const; + int rowCount(const QModelIndex& parent = QModelIndex()) const; +// ChatListItem* getItemForIndex(const QModelIndex& index) const; + private: +// ChatListGroupItem* mucBookmarks_; +// ChatListGroupItem* root_; + QList<MUCSearchServiceItem*> services_; + }; + +} diff --git a/Swift/QtUI/MUCSearch/MUCSearchRoomItem.cpp b/Swift/QtUI/MUCSearch/MUCSearchRoomItem.cpp new file mode 100644 index 0000000..a53a577 --- /dev/null +++ b/Swift/QtUI/MUCSearch/MUCSearchRoomItem.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swift/QtUI/MUCSearch/MUCSearchRoomItem.h" + +#include "Swift/QtUI/MUCSearch/MUCSearchServiceItem.h" + +namespace Swift { +MUCSearchRoomItem::MUCSearchRoomItem(const QString& node, MUCSearchServiceItem* parent) : parent_(parent), node_(node) { + parent_->addRoom(this); +} + +MUCSearchServiceItem* MUCSearchRoomItem::getParent() { + return parent_; +} +QVariant MUCSearchRoomItem::data(int role) { + switch (role) { + case Qt::DisplayRole: return QVariant(node_); + default: return QVariant(); + } +} + +} diff --git a/Swift/QtUI/MUCSearch/MUCSearchRoomItem.h b/Swift/QtUI/MUCSearch/MUCSearchRoomItem.h new file mode 100644 index 0000000..920aaae --- /dev/null +++ b/Swift/QtUI/MUCSearch/MUCSearchRoomItem.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include "Swift/QtUI/MUCSearch/MUCSearchItem.h" + +namespace Swift { + class MUCSearchServiceItem; + class MUCSearchRoomItem : public MUCSearchItem { + public: + MUCSearchRoomItem(const QString& node, MUCSearchServiceItem* parent); + MUCSearchServiceItem* getParent(); + QVariant data(int role); + QString getNode() {return node_;} + private: + MUCSearchServiceItem* parent_; + QString node_; + }; +} diff --git a/Swift/QtUI/MUCSearch/MUCSearchServiceItem.h b/Swift/QtUI/MUCSearch/MUCSearchServiceItem.h new file mode 100644 index 0000000..860792f --- /dev/null +++ b/Swift/QtUI/MUCSearch/MUCSearchServiceItem.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <QList> +#include <QString> + +#include "Swift/QtUI/MUCSearch/MUCSearchRoomItem.h" + +namespace Swift { + class MUCSearchServiceItem : public MUCSearchItem { + public: + MUCSearchServiceItem(const QString& jidString) : jidString_(jidString) {} + void addRoom(MUCSearchRoomItem* room) {rooms_.push_back(room);} + int rowCount() {return rooms_.count();} + MUCSearchRoomItem* getItem(int i) {return rooms_[i];} + QVariant data(int role) { + switch (role) { + case Qt::DisplayRole: return QVariant(jidString_); + default: return QVariant(); + } + } + QString getHost() {return jidString_;} + private: + QList<MUCSearchRoomItem*> rooms_; + QString jidString_; + }; +} diff --git a/Swift/QtUI/MUCSearch/QtMUCSearchWindow.cpp b/Swift/QtUI/MUCSearch/QtMUCSearchWindow.cpp new file mode 100644 index 0000000..78e4fa5 --- /dev/null +++ b/Swift/QtUI/MUCSearch/QtMUCSearchWindow.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swift/QtUI/MUCSearch/QtMUCSearchWindow.h" + +#include <qdebug.h> + +#include "Swift/Controllers/UIEvents/UIEventStream.h" +#include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h" +#include "Swift/QtUI/MUCSearch/MUCSearchModel.h" +#include "Swift/QtUI/MUCSearch/MUCSearchDelegate.h" +#include "Swift/QtUI/QtSwiftUtil.h" + +namespace Swift { + +QtMUCSearchWindow::QtMUCSearchWindow(UIEventStream* eventStream) { + eventStream_ = eventStream; + setupUi(this); + showEmptyRooms_->hide(); + filterLabel_->hide(); + filter_->hide(); + model_ = new MUCSearchModel(); + delegate_ = new MUCSearchDelegate(); + results_->setModel(model_); + results_->setItemDelegate(delegate_); + results_->setHeaderHidden(true); +#ifdef SWIFT_PLATFORM_MACOSX + results_->setAlternatingRowColors(true); +#endif + connect(searchButton_, SIGNAL(clicked()), this, SLOT(handleSearch())); + connect(joinButton_, SIGNAL(clicked()), this, SLOT(handleJoin())); + connect(results_, SIGNAL(clicked(const QModelIndex&)), this, SLOT(handleSelected(const QModelIndex&))); + connect(results_, SIGNAL(activated(const QModelIndex&)), this, SLOT(handleActivated(const QModelIndex&))); +} + +QtMUCSearchWindow::~QtMUCSearchWindow() { + +} + +void QtMUCSearchWindow::handleActivated(const QModelIndex& index) { + if (!index.isValid()) { + return; + } + MUCSearchRoomItem* roomItem = dynamic_cast<MUCSearchRoomItem*>(static_cast<MUCSearchItem*>(index.internalPointer())); + if (roomItem) { + handleSelected(index); + handleJoin(); + } +} + +void QtMUCSearchWindow::handleSelected(const QModelIndex& current) { + if (!current.isValid()) { + return; + } + MUCSearchRoomItem* roomItem = dynamic_cast<MUCSearchRoomItem*>(static_cast<MUCSearchItem*>(current.internalPointer())); + if (roomItem) { + room_->setText(roomItem->getNode() + "@" + roomItem->getParent()->getHost()); + } + +} + +void QtMUCSearchWindow::handleSearch() { + if (service_->text().isEmpty()) { + return; + } + onAddService(JID(Q2PSTRING(service_->text()))); +} + +void QtMUCSearchWindow::handleJoin() { + if (room_->text().isEmpty()) { + handleSelected(results_->currentIndex()); + } + if (room_->text().isEmpty()) { + return; + } + boost::optional<String> maybeNick; + if (!nickName_->text().isEmpty()) { + maybeNick = Q2PSTRING(nickName_->text()); + } + eventStream_->send(boost::shared_ptr<UIEvent>(new JoinMUCUIEvent(JID(Q2PSTRING(room_->text())), maybeNick))); + hide(); +} + +void QtMUCSearchWindow::setNick(const String& nick) { + nickName_->setText(P2QSTRING(nick)); +} + +void QtMUCSearchWindow::setMUC(const String& nick) { + room_->setText(P2QSTRING(nick)); +} + +void QtMUCSearchWindow::show() { + QWidget::show(); + QWidget::activateWindow(); +} + +void QtMUCSearchWindow::clearList() { + model_->clear(); +} + +void QtMUCSearchWindow::addService(const MUCService& service) { + MUCSearchServiceItem* serviceItem = new MUCSearchServiceItem(P2QSTRING(service.getJID().toString())); + foreach (MUCService::MUCRoom room, service.getRooms()) { + new MUCSearchRoomItem(P2QSTRING(room.getNode()), serviceItem); + } + model_->addService(serviceItem); +} + +} diff --git a/Swift/QtUI/MUCSearch/QtMUCSearchWindow.h b/Swift/QtUI/MUCSearch/QtMUCSearchWindow.h new file mode 100644 index 0000000..7b556b0 --- /dev/null +++ b/Swift/QtUI/MUCSearch/QtMUCSearchWindow.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include "Swift/QtUI/MUCSearch/ui_QtMUCSearchWindow.h" + +#include "Swift/Controllers/UIInterfaces/MUCSearchWindow.h" + +namespace Swift { + class MUCSearchModel; + class MUCSearchDelegate; + class UIEventStream; + class QtMUCSearchWindow : public QWidget, public MUCSearchWindow, private Ui::QtMUCSearchWindow { + Q_OBJECT + public: + QtMUCSearchWindow(UIEventStream* eventStream); + virtual ~QtMUCSearchWindow(); + + virtual void setNick(const String& nick); + virtual void setMUC(const String& nick); + virtual void clearList(); + virtual void addService(const MUCService& service); + + virtual void show(); + private slots: + void handleSearch(); + void handleJoin(); + void handleSelected(const QModelIndex& current); + void handleActivated(const QModelIndex& index); + private: + MUCSearchModel* model_; + MUCSearchDelegate* delegate_; + UIEventStream* eventStream_; + }; +} diff --git a/Swift/QtUI/MUCSearch/QtMUCSearchWindow.ui b/Swift/QtUI/MUCSearch/QtMUCSearchWindow.ui new file mode 100644 index 0000000..d3d327e --- /dev/null +++ b/Swift/QtUI/MUCSearch/QtMUCSearchWindow.ui @@ -0,0 +1,185 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QtMUCSearchWindow</class> + <widget class="QWidget" name="QtMUCSearchWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>502</width> + <height>526</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string>Find Room</string> + </property> + <widget class="QTreeView" name="results_"> + <property name="geometry"> + <rect> + <x>10</x> + <y>30</y> + <width>256</width> + <height>441</height> + </rect> + </property> + </widget> + <widget class="QLabel" name="label"> + <property name="geometry"> + <rect> + <x>10</x> + <y>10</y> + <width>251</width> + <height>18</height> + </rect> + </property> + <property name="text"> + <string>Available Rooms</string> + </property> + </widget> + <widget class="QLineEdit" name="service_"> + <property name="geometry"> + <rect> + <x>280</x> + <y>140</y> + <width>211</width> + <height>26</height> + </rect> + </property> + <property name="text"> + <string/> + </property> + </widget> + <widget class="QLabel" name="label_2"> + <property name="geometry"> + <rect> + <x>280</x> + <y>120</y> + <width>201</width> + <height>18</height> + </rect> + </property> + <property name="text"> + <string>Search another service:</string> + </property> + </widget> + <widget class="QLabel" name="filterLabel_"> + <property name="geometry"> + <rect> + <x>280</x> + <y>260</y> + <width>211</width> + <height>20</height> + </rect> + </property> + <property name="text"> + <string>Only show rooms matching:</string> + </property> + </widget> + <widget class="QLineEdit" name="filter_"> + <property name="geometry"> + <rect> + <x>280</x> + <y>280</y> + <width>211</width> + <height>26</height> + </rect> + </property> + </widget> + <widget class="QLabel" name="label_4"> + <property name="geometry"> + <rect> + <x>280</x> + <y>410</y> + <width>211</width> + <height>18</height> + </rect> + </property> + <property name="text"> + <string>Join Room Directly:</string> + </property> + </widget> + <widget class="QLabel" name="label_5"> + <property name="geometry"> + <rect> + <x>280</x> + <y>10</y> + <width>211</width> + <height>18</height> + </rect> + </property> + <property name="text"> + <string>Your nickname:</string> + </property> + </widget> + <widget class="QLineEdit" name="nickName_"> + <property name="geometry"> + <rect> + <x>280</x> + <y>30</y> + <width>211</width> + <height>26</height> + </rect> + </property> + </widget> + <widget class="QLineEdit" name="room_"> + <property name="geometry"> + <rect> + <x>280</x> + <y>430</y> + <width>201</width> + <height>26</height> + </rect> + </property> + </widget> + <widget class="QPushButton" name="joinButton_"> + <property name="geometry"> + <rect> + <x>210</x> + <y>480</y> + <width>92</width> + <height>28</height> + </rect> + </property> + <property name="text"> + <string>Join Room</string> + </property> + </widget> + <widget class="QCheckBox" name="showEmptyRooms_"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="geometry"> + <rect> + <x>280</x> + <y>310</y> + <width>201</width> + <height>23</height> + </rect> + </property> + <property name="text"> + <string>Show Empty Rooms</string> + </property> + </widget> + <widget class="QPushButton" name="searchButton_"> + <property name="geometry"> + <rect> + <x>410</x> + <y>170</y> + <width>80</width> + <height>26</height> + </rect> + </property> + <property name="text"> + <string>Search</string> + </property> + </widget> + </widget> + <resources/> + <connections/> +</ui> diff --git a/Swift/QtUI/MUCSearch/QtMUCSearchWindowFactory.h b/Swift/QtUI/MUCSearch/QtMUCSearchWindowFactory.h new file mode 100644 index 0000000..9bcb53f --- /dev/null +++ b/Swift/QtUI/MUCSearch/QtMUCSearchWindowFactory.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include "Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h" +#include "Swift/QtUI/MUCSearch/QtMUCSearchWindow.h" + +namespace Swift { + class UIEventStream; + class QtMUCSearchWindowFactory : public MUCSearchWindowFactory { + public: + virtual ~QtMUCSearchWindowFactory() {}; + + MUCSearchWindow* createMUCSearchWindow(UIEventStream* eventStream) { + return new QtMUCSearchWindow(eventStream); + }; + }; +} diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp index fcfb693..7d987c6 100644 --- a/Swift/QtUI/QtMainWindow.cpp +++ b/Swift/QtUI/QtMainWindow.cpp @@ -25,6 +25,7 @@ #include "QtTabWidget.h" #include "Roster/QtTreeWidget.h" #include "Swift/Controllers/UIEvents/AddContactUIEvent.h" +#include "Swift/Controllers/UIEvents/RequestMUCSearchUIEvent.h" #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h" namespace Swift { @@ -138,6 +139,7 @@ void QtMainWindow::handleJoinMUCAction() { QtJoinMUCDialog* joinMUC = new QtJoinMUCDialog("jabber@conference.jabber.org", "SwiftUser", this); connect(joinMUC, SIGNAL(onJoinCommand(const JID&, const QString&)), SLOT(handleJoinMUCDialogComplete(const JID&, const QString&))); joinMUC->show(); + uiEventStream_->send(boost::shared_ptr<UIEvent>(new RequestMUCSearchUIEvent())); } void QtMainWindow::handleJoinMUCDialogComplete(const JID& muc, const QString& nick) { diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp index 534697b..3b286b4 100644 --- a/Swift/QtUI/QtSwift.cpp +++ b/Swift/QtUI/QtSwift.cpp @@ -16,6 +16,7 @@ #include "QtXMLConsoleWidgetFactory.h" #include "ChatList/QtChatListWindowFactory.h" #include "EventViewer/QtEventWindowFactory.h" +#include "MUCSearch/QtMUCSearchWindowFactory.h" #include <boost/bind.hpp> #include <QSplitter> @@ -71,11 +72,12 @@ QtSwift::QtSwift(po::variables_map options) : autoUpdater_(NULL) { eventWindowFactory_ = new QtEventWindowFactory(rosterWindowFactory_); xmlConsoleWidgetFactory_ = new QtXMLConsoleWidgetFactory(tabs_); chatListWindowFactory_ = new QtChatListWindowFactory(rosterWindowFactory_); + mucSearchWindowFactory_ = new QtMUCSearchWindowFactory(); soundPlayer_ = new QtSoundPlayer(); if (splitter_) { splitter_->show(); } - mainController_ = new MainController(chatWindowFactory_, rosterWindowFactory_, loginWindowFactory_, eventWindowFactory_, settings_, application_, systemTray_, soundPlayer_, xmlConsoleWidgetFactory_, chatListWindowFactory_, options.count("latency-debug") > 0); + mainController_ = new MainController(chatWindowFactory_, rosterWindowFactory_, loginWindowFactory_, eventWindowFactory_, settings_, application_, systemTray_, soundPlayer_, xmlConsoleWidgetFactory_, chatListWindowFactory_, mucSearchWindowFactory_, options.count("latency-debug") > 0); PlatformAutoUpdaterFactory autoUpdaterFactory; if (autoUpdaterFactory.isSupported()) { @@ -89,6 +91,7 @@ QtSwift::~QtSwift() { delete chatWindowFactory_; delete rosterWindowFactory_; delete loginWindowFactory_; + delete mucSearchWindowFactory_; delete mainController_; delete settings_; delete application_; diff --git a/Swift/QtUI/QtSwift.h b/Swift/QtUI/QtSwift.h index d0141b2..37dd4ce 100644 --- a/Swift/QtUI/QtSwift.h +++ b/Swift/QtUI/QtSwift.h @@ -33,6 +33,7 @@ namespace Swift { class QtSoundPlayer; class QtEventWindowFactory; class QtChatListWindowFactory; + class QtMUCSearchWindowFactory; class QtSwift : public QObject { Q_OBJECT @@ -56,6 +57,7 @@ namespace Swift { QtEventWindowFactory* eventWindowFactory_; Application* application_; AutoUpdater* autoUpdater_; + QtMUCSearchWindowFactory* mucSearchWindowFactory_; }; } diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index 4311623..4ccb301 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -95,6 +95,10 @@ sources = [ "ChatList/ChatListModel.cpp", "ChatList/ChatListDelegate.cpp", "ChatList/ChatListMUCItem.cpp", + "MUCSearch/QtMUCSearchWindow.cpp", + "MUCSearch/MUCSearchModel.cpp", + "MUCSearch/MUCSearchRoomItem.cpp", + "MUCSearch/MUCSearchDelegate.cpp", "ContextMenus/QtRosterContextMenu.cpp", "ContextMenus/QtContextMenu.cpp", "QtSubscriptionRequestWindow.cpp", @@ -116,6 +120,7 @@ else : swiftProgram = myenv.Program("swift", sources) myenv.Uic4("QtJoinMUCDialog.ui") +myenv.Uic4("MUCSearch/QtMUCSearchWindow.ui") myenv.Uic4("QtAddContactDialog.ui") myenv.Uic4("QtBookmarkDetailWindow.ui") myenv.Qrc("DefaultTheme.qrc") diff --git a/Swiften/Elements/DiscoItems.h b/Swiften/Elements/DiscoItems.h new file mode 100644 index 0000000..400947a --- /dev/null +++ b/Swiften/Elements/DiscoItems.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <vector> +#include <algorithm> + +#include "Swiften/Elements/Payload.h" +#include "Swiften/Base/String.h" +#include "Swiften/JID/JID.h" + +namespace Swift { + class DiscoItems : public Payload { + public: + class Item { + public: + Item(const String& name, const JID& jid, const String& node="") : name_(name), jid_(jid), node_(node) { + } + + const String& getName() const { + return name_; + } + + const String& getNode() const { + return node_; + } + + const JID& getJID() const { + return jid_; + } + + private: + String name_; + JID jid_; + String node_; + }; + + DiscoItems() { + } + + const String& getNode() const { + return node_; + } + + void setNode(const String& node) { + node_ = node; + } + + const std::vector<Item>& getItems() const { + return items_; + } + + void addItem(const Item& item) { + items_.push_back(item); + } + + private: + String node_; + std::vector<Item> items_; + }; +} diff --git a/Swiften/Parser/PayloadParsers/DiscoItemsParser.cpp b/Swiften/Parser/PayloadParsers/DiscoItemsParser.cpp new file mode 100644 index 0000000..0900354 --- /dev/null +++ b/Swiften/Parser/PayloadParsers/DiscoItemsParser.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swiften/Parser/PayloadParsers/DiscoItemsParser.h" + +namespace Swift { + +DiscoItemsParser::DiscoItemsParser() : level_(TopLevel) { +} + +void DiscoItemsParser::handleStartElement(const String& element, const String&, const AttributeMap& attributes) { + if (level_ == PayloadLevel) { + if (element == "item") { + getPayloadInternal()->addItem(DiscoItems::Item(attributes.getAttribute("name"), JID(attributes.getAttribute("jid")), attributes.getAttribute("node"))); + } + } + ++level_; +} + +void DiscoItemsParser::handleEndElement(const String&, const String&) { + --level_; +} + +void DiscoItemsParser::handleCharacterData(const String&) { +} + +} diff --git a/Swiften/Parser/PayloadParsers/DiscoItemsParser.h b/Swiften/Parser/PayloadParsers/DiscoItemsParser.h new file mode 100644 index 0000000..e3da34e --- /dev/null +++ b/Swiften/Parser/PayloadParsers/DiscoItemsParser.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include "Swiften/Elements/DiscoItems.h" +#include "Swiften/Parser/GenericPayloadParser.h" + +namespace Swift { + class DiscoItemsParser : public GenericPayloadParser<DiscoItems> { + public: + DiscoItemsParser(); + + virtual void handleStartElement(const String& element, const String&, const AttributeMap& attributes); + virtual void handleEndElement(const String& element, const String&); + virtual void handleCharacterData(const String& data); + + private: + enum Level { + TopLevel = 0, + PayloadLevel = 1 + }; + int level_; + }; +} + diff --git a/Swiften/Parser/PayloadParsers/DiscoItemsParserFactory.h b/Swiften/Parser/PayloadParsers/DiscoItemsParserFactory.h new file mode 100644 index 0000000..5b53031 --- /dev/null +++ b/Swiften/Parser/PayloadParsers/DiscoItemsParserFactory.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include "Swiften/Parser/GenericPayloadParserFactory.h" +#include "Swiften/Parser/PayloadParsers/DiscoItemsParser.h" + +namespace Swift { + class DiscoItemsParserFactory : public GenericPayloadParserFactory<DiscoItemsParser> { + public: + DiscoItemsParserFactory() : GenericPayloadParserFactory<DiscoItemsParser>("query", "http://jabber.org/protocol/disco#items") {} + }; +} + diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp index b0e4eb2..8c570cb 100644 --- a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp +++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp @@ -20,6 +20,7 @@ #include "Swiften/Parser/PayloadParsers/SoftwareVersionParserFactory.h" #include "Swiften/Parser/PayloadParsers/StorageParserFactory.h" #include "Swiften/Parser/PayloadParsers/DiscoInfoParserFactory.h" +#include "Swiften/Parser/PayloadParsers/DiscoItemsParserFactory.h" #include "Swiften/Parser/PayloadParsers/SecurityLabelParserFactory.h" #include "Swiften/Parser/PayloadParsers/SecurityLabelsCatalogParserFactory.h" #include "Swiften/Parser/PayloadParsers/VCardUpdateParserFactory.h" @@ -42,6 +43,7 @@ FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() { factories_.push_back(shared_ptr<PayloadParserFactory>(new StorageParserFactory())); factories_.push_back(shared_ptr<PayloadParserFactory>(new RosterParserFactory())); factories_.push_back(shared_ptr<PayloadParserFactory>(new DiscoInfoParserFactory())); + factories_.push_back(shared_ptr<PayloadParserFactory>(new DiscoItemsParserFactory())); factories_.push_back(shared_ptr<PayloadParserFactory>(new ResourceBindParserFactory())); factories_.push_back(shared_ptr<PayloadParserFactory>(new StartSessionParserFactory())); factories_.push_back(shared_ptr<PayloadParserFactory>(new SecurityLabelParserFactory())); diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript index 0d3aad2..77f50be 100644 --- a/Swiften/Parser/SConscript +++ b/Swiften/Parser/SConscript @@ -20,6 +20,7 @@ sources = [ "PayloadParsers/BodyParser.cpp", "PayloadParsers/ChatStateParser.cpp", "PayloadParsers/DiscoInfoParser.cpp", + "PayloadParsers/DiscoItemsParser.cpp", "PayloadParsers/ErrorParser.cpp", "PayloadParsers/FullPayloadParserFactoryCollection.cpp", "PayloadParsers/PriorityParser.cpp", diff --git a/Swiften/Queries/Requests/GetDiscoItemsRequest.h b/Swiften/Queries/Requests/GetDiscoItemsRequest.h new file mode 100644 index 0000000..453eab4 --- /dev/null +++ b/Swiften/Queries/Requests/GetDiscoItemsRequest.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include "Swiften/Queries/GenericRequest.h" +#include "Swiften/Elements/DiscoItems.h" + +namespace Swift { + class GetDiscoItemsRequest : public GenericRequest<DiscoItems> { + public: + GetDiscoItemsRequest(const JID& jid, IQRouter* router) : + GenericRequest<DiscoItems>(IQ::Get, jid, boost::shared_ptr<DiscoItems>(new DiscoItems()), router) { + } + }; +} diff --git a/Swiften/SConscript b/Swiften/SConscript index 257f056..4d25dc5 100644 --- a/Swiften/SConscript +++ b/Swiften/SConscript @@ -70,6 +70,7 @@ if env["SCONS_STAGE"] == "build" : "Serializer/PayloadSerializers/CapsInfoSerializer.cpp", "Serializer/PayloadSerializers/ChatStateSerializer.cpp", "Serializer/PayloadSerializers/DiscoInfoSerializer.cpp", + "Serializer/PayloadSerializers/DiscoItemsSerializer.cpp", "Serializer/PayloadSerializers/ErrorSerializer.cpp", "Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp", "Serializer/PayloadSerializers/MUCPayloadSerializer.cpp", diff --git a/Swiften/Serializer/PayloadSerializers/DiscoItemsSerializer.cpp b/Swiften/Serializer/PayloadSerializers/DiscoItemsSerializer.cpp new file mode 100644 index 0000000..056c515 --- /dev/null +++ b/Swiften/Serializer/PayloadSerializers/DiscoItemsSerializer.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swiften/Serializer/PayloadSerializers/DiscoItemsSerializer.h" + +#include <boost/shared_ptr.hpp> + +#include "Swiften/Base/foreach.h" +#include "Swiften/Serializer/XML/XMLElement.h" + +namespace Swift { + +DiscoItemsSerializer::DiscoItemsSerializer() : GenericPayloadSerializer<DiscoItems>() { +} + +String DiscoItemsSerializer::serializePayload(boost::shared_ptr<DiscoItems> discoItems) const { + XMLElement queryElement("query", "http://jabber.org/protocol/disco#items"); + if (!discoItems->getNode().isEmpty()) { + queryElement.setAttribute("node", discoItems->getNode()); + } + foreach(const DiscoItems::Item& item, discoItems->getItems()) { + boost::shared_ptr<XMLElement> itemElement(new XMLElement("item")); + itemElement->setAttribute("name", item.getName()); + itemElement->setAttribute("jid", item.getJID()); + if (!item.getNode().isEmpty()) { + itemElement->setAttribute("node", item.getNode()); + } + queryElement.addNode(itemElement); + } + return queryElement.serialize(); +} + +} diff --git a/Swiften/Serializer/PayloadSerializers/DiscoItemsSerializer.h b/Swiften/Serializer/PayloadSerializers/DiscoItemsSerializer.h new file mode 100644 index 0000000..8116e9b --- /dev/null +++ b/Swiften/Serializer/PayloadSerializers/DiscoItemsSerializer.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include "Swiften/Serializer/GenericPayloadSerializer.h" +#include "Swiften/Elements/DiscoItems.h" + +namespace Swift { + class DiscoItemsSerializer : public GenericPayloadSerializer<DiscoItems> { + public: + DiscoItemsSerializer(); + + virtual String serializePayload(boost::shared_ptr<DiscoItems>) const; + }; +} + diff --git a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp index 8dc9d86..caf1d9c 100644 --- a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp +++ b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp @@ -17,6 +17,7 @@ #include "Swiften/Serializer/PayloadSerializers/StatusSerializer.h" #include "Swiften/Serializer/PayloadSerializers/StatusShowSerializer.h" #include "Swiften/Serializer/PayloadSerializers/DiscoInfoSerializer.h" +#include "Swiften/Serializer/PayloadSerializers/DiscoItemsSerializer.h" #include "Swiften/Serializer/PayloadSerializers/CapsInfoSerializer.h" #include "Swiften/Serializer/PayloadSerializers/ResourceBindSerializer.h" #include "Swiften/Serializer/PayloadSerializers/StartSessionSerializer.h" @@ -42,6 +43,7 @@ FullPayloadSerializerCollection::FullPayloadSerializerCollection() { serializers_.push_back(new StatusSerializer()); serializers_.push_back(new StatusShowSerializer()); serializers_.push_back(new DiscoInfoSerializer()); + serializers_.push_back(new DiscoItemsSerializer()); serializers_.push_back(new CapsInfoSerializer()); serializers_.push_back(new ResourceBindSerializer()); serializers_.push_back(new StartSessionSerializer()); -- cgit v0.10.2-6-g49f6