diff options
Diffstat (limited to 'Swift')
| -rw-r--r-- | Swift/Controllers/Roster/Roster.cpp | 9 | ||||
| -rw-r--r-- | Swift/Controllers/Roster/Roster.h | 6 | ||||
| -rw-r--r-- | Swift/QtUI/Roster/QtFilterWidget.cpp | 22 | ||||
| -rw-r--r-- | Swift/QtUI/Roster/QtFilterWidget.h | 3 |
4 files changed, 36 insertions, 4 deletions
diff --git a/Swift/Controllers/Roster/Roster.cpp b/Swift/Controllers/Roster/Roster.cpp index 44cf03d..4dbc453 100644 --- a/Swift/Controllers/Roster/Roster.cpp +++ b/Swift/Controllers/Roster/Roster.cpp @@ -1,247 +1,254 @@ /* * Copyright (c) 2010-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include <Swift/Controllers/Roster/Roster.h> #include <string> #include <iostream> #include <set> #include <deque> #include <boost/bind.hpp> #include <Swiften/Base/foreach.h> #include <Swiften/JID/JID.h> #include <Swift/Controllers/Roster/ContactRosterItem.h> #include <Swift/Controllers/Roster/RosterItem.h> #include <Swift/Controllers/Roster/GroupRosterItem.h> #include <Swift/Controllers/Roster/ItemOperations/RosterItemOperation.h> namespace Swift { Roster::Roster(bool sortByStatus, bool fullJIDMapping) : blockingSupported_(false) { sortByStatus_ = sortByStatus; fullJIDMapping_ = fullJIDMapping; root_ = new GroupRosterItem("Dummy-Root", NULL, sortByStatus_); root_->onChildrenChanged.connect(boost::bind(&Roster::handleChildrenChanged, this, root_)); } Roster::~Roster() { std::deque<RosterItem*> queue; queue.push_back(root_); while (!queue.empty()) { RosterItem* item = *queue.begin(); queue.pop_front(); GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(item); if (group) { queue.insert(queue.begin(), group->getChildren().begin(), group->getChildren().end()); } ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item); if (contact) { contact->onVCardRequested.disconnect(boost::bind(boost::ref(onVCardUpdateRequested), contact->getJID())); } delete item; } } GroupRosterItem* Roster::getRoot() { return root_; } GroupRosterItem* Roster::getGroup(const std::string& groupName) { foreach (RosterItem *item, root_->getChildren()) { GroupRosterItem *group = dynamic_cast<GroupRosterItem*>(item); if (group && group->getDisplayName() == groupName) { return group; } } GroupRosterItem* group = new GroupRosterItem(groupName, root_, sortByStatus_); root_->addChild(group); group->onChildrenChanged.connect(boost::bind(&Roster::handleChildrenChanged, this, group)); group->onDataChanged.connect(boost::bind(&Roster::handleDataChanged, this, group)); return group; } void Roster::setBlockingSupported(bool isSupported) { if (!blockingSupported_) { foreach(ItemMap::value_type i, itemMap_) { foreach(ContactRosterItem* item, i.second) { item->setBlockState(ContactRosterItem::IsUnblocked); } } } blockingSupported_ = isSupported; } void Roster::removeGroup(const std::string& group) { root_->removeGroupChild(group); } void Roster::handleDataChanged(RosterItem* item) { onDataChanged(item); } void Roster::handleChildrenChanged(GroupRosterItem* item) { onChildrenChanged(item); } void Roster::addContact(const JID& jid, const JID& displayJID, const std::string& name, const std::string& groupName, const boost::filesystem::path& avatarPath) { GroupRosterItem* group(getGroup(groupName)); ContactRosterItem *item = new ContactRosterItem(jid, displayJID, name, group); item->onVCardRequested.connect(boost::bind(boost::ref(onVCardUpdateRequested), jid)); item->setAvatarPath(avatarPath); if (blockingSupported_) { item->setBlockState(ContactRosterItem::IsUnblocked); } group->addChild(item); ItemMap::iterator i = itemMap_.insert(std::make_pair(fullJIDMapping_ ? jid : jid.toBare(), std::vector<ContactRosterItem*>())).first; if (!i->second.empty()) { foreach (const std::string& existingGroup, i->second[0]->getGroups()) { item->addGroup(existingGroup); } } i->second.push_back(item); item->onDataChanged.connect(boost::bind(&Roster::handleDataChanged, this, item)); filterContact(item, group); foreach (ContactRosterItem* item, i->second) { item->addGroup(groupName); } } struct JIDEqualsTo { JIDEqualsTo(const JID& jid) : jid(jid) {} bool operator()(ContactRosterItem* i) const { return jid == i->getJID(); } JID jid; }; void Roster::removeAll() { root_->removeAll(); itemMap_.clear(); onChildrenChanged(root_); onDataChanged(root_); } void Roster::removeContact(const JID& jid) { ItemMap::iterator item = itemMap_.find(fullJIDMapping_ ? jid : jid.toBare()); if (item != itemMap_.end()) { std::vector<ContactRosterItem*>& items = item->second; items.erase(std::remove_if(items.begin(), items.end(), JIDEqualsTo(jid)), items.end()); if (items.empty()) { itemMap_.erase(item); } } //Causes the delete root_->removeChild(jid); } void Roster::removeContactFromGroup(const JID& jid, const std::string& groupName) { std::vector<RosterItem*> children = root_->getChildren(); std::vector<RosterItem*>::iterator it = children.begin(); ItemMap::iterator itemIt = itemMap_.find(fullJIDMapping_ ? jid : jid.toBare()); while (it != children.end()) { GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(*it); if (group && group->getDisplayName() == groupName) { ContactRosterItem* deleted = group->removeChild(jid); if (itemIt != itemMap_.end()) { std::vector<ContactRosterItem*>& items = itemIt->second; items.erase(std::remove(items.begin(), items.end(), deleted), items.end()); } } ++it; } if (itemIt != itemMap_.end()) { foreach (ContactRosterItem* item, itemIt->second) { item->removeGroup(groupName); } } } void Roster::applyOnItems(const RosterItemOperation& operation) { if (operation.requiresLookup()) { applyOnItem(operation, operation.lookupJID()); } else { applyOnAllItems(operation); } } void Roster::applyOnItem(const RosterItemOperation& operation, const JID& jid) { ItemMap::iterator i = itemMap_.find(fullJIDMapping_ ? jid : jid.toBare()); if (i == itemMap_.end()) { return; } foreach (ContactRosterItem* item, i->second) { operation(item); filterContact(item, item->getParent()); } } void Roster::applyOnAllItems(const RosterItemOperation& operation) { std::deque<RosterItem*> queue; queue.push_back(root_); while (!queue.empty()) { RosterItem* item = *queue.begin(); queue.pop_front(); operation(item); GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(item); if (group) { queue.insert(queue.begin(), group->getChildren().begin(), group->getChildren().end()); } } filterAll(); } -void Roster::removeFilter(RosterFilter *filter) { +void Roster::addFilter(RosterFilter* filter) { + filters_.push_back(filter); + filterAll(); + onFilterAdded(filter); +} + +void Roster::removeFilter(RosterFilter* filter) { for (unsigned int i = 0; i < filters_.size(); i++) { if (filters_[i] == filter) { filters_.erase(filters_.begin() + i); break; } } filterAll(); + onFilterRemoved(filter); } void Roster::filterContact(ContactRosterItem* contact, GroupRosterItem* group) { size_t oldDisplayedSize = group->getDisplayedChildren().size(); bool hide = true; foreach (RosterFilter *filter, filters_) { hide &= (*filter)(contact); } group->setDisplayed(contact, filters_.empty() || !hide); size_t newDisplayedSize = group->getDisplayedChildren().size(); if (oldDisplayedSize == 0 && newDisplayedSize > 0) { onGroupAdded(group); } } void Roster::filterGroup(GroupRosterItem* group) { foreach (RosterItem* child, group->getChildren()) { ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(child); if (contact) { filterContact(contact, group); } } } void Roster::filterAll() { std::deque<RosterItem*> queue; queue.push_back(root_); while (!queue.empty()) { RosterItem *item = *queue.begin(); queue.pop_front(); GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(item); if (group) { queue.insert(queue.begin(), group->getChildren().begin(), group->getChildren().end()); filterGroup(group); } } } } diff --git a/Swift/Controllers/Roster/Roster.h b/Swift/Controllers/Roster/Roster.h index b60eafa..9956cb9 100644 --- a/Swift/Controllers/Roster/Roster.h +++ b/Swift/Controllers/Roster/Roster.h @@ -1,67 +1,69 @@ /* * Copyright (c) 2010-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include <string> #include <vector> #include <map> #include <boost/shared_ptr.hpp> #include <Swiften/Base/boost_bsignals.h> #include <Swiften/JID/JID.h> #include <Swift/Controllers/Roster/ItemOperations/RosterItemOperation.h> #include <Swift/Controllers/Roster/RosterFilter.h> #include <Swift/Controllers/Roster/ContactRosterItem.h> namespace Swift { class RosterItem; class GroupRosterItem; class ContactRosterItem; class Roster { public: Roster(bool sortByStatus = true, bool fullJIDMapping = false); ~Roster(); void addContact(const JID& jid, const JID& displayJID, const std::string& name, const std::string& group, const boost::filesystem::path& avatarPath); void removeContact(const JID& jid); void removeContactFromGroup(const JID& jid, const std::string& group); void removeGroup(const std::string& group); void removeAll(); void applyOnItems(const RosterItemOperation& operation); void applyOnAllItems(const RosterItemOperation& operation); void applyOnItem(const RosterItemOperation& operation, const JID& jid); - void addFilter(RosterFilter *filter) {filters_.push_back(filter);filterAll();} - void removeFilter(RosterFilter *filter); + void addFilter(RosterFilter* filter); + void removeFilter(RosterFilter* filter); GroupRosterItem* getRoot(); std::vector<RosterFilter*> getFilters() {return filters_;} boost::signal<void (GroupRosterItem*)> onChildrenChanged; boost::signal<void (GroupRosterItem*)> onGroupAdded; boost::signal<void (RosterItem*)> onDataChanged; boost::signal<void (JID&)> onVCardUpdateRequested; + boost::signal<void (RosterFilter* filter)> onFilterAdded; + boost::signal<void (RosterFilter* filter)> onFilterRemoved; GroupRosterItem* getGroup(const std::string& groupName); void setBlockingSupported(bool isSupported); private: void handleDataChanged(RosterItem* item); void handleChildrenChanged(GroupRosterItem* item); void filterGroup(GroupRosterItem* item); void filterContact(ContactRosterItem* contact, GroupRosterItem* group); void filterAll(); GroupRosterItem* root_; std::vector<RosterFilter*> filters_; typedef std::map<JID, std::vector<ContactRosterItem*> > ItemMap; ItemMap itemMap_; bool fullJIDMapping_; bool sortByStatus_; bool blockingSupported_; }; } diff --git a/Swift/QtUI/Roster/QtFilterWidget.cpp b/Swift/QtUI/Roster/QtFilterWidget.cpp index a4c9449..64eb312 100644 --- a/Swift/QtUI/Roster/QtFilterWidget.cpp +++ b/Swift/QtUI/Roster/QtFilterWidget.cpp @@ -1,146 +1,166 @@ /* * Copyright (c) 2014 Kevin Smith and Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ /* * Copyright (c) 2013 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include <QEvent> #include <QKeyEvent> #include <QLayout> #include <QString> #include <QVBoxLayout> #include <Swift/Controllers/UIEvents/RequestChatUIEvent.h> #include <Swift/Controllers/UIEvents/UIEventStream.h> #include <Swift/QtUI/QtClosableLineEdit.h> #include <Swift/QtUI/QtSwiftUtil.h> #include <Swift/QtUI/Roster/QtFilterWidget.h> namespace Swift { QtFilterWidget::QtFilterWidget(QWidget* parent, QtTreeWidget* treeView, UIEventStream* eventStream, QBoxLayout* layout) : QWidget(parent), treeView_(treeView), eventStream_(eventStream), fuzzyRosterFilter_(0), isModifierSinglePressed_(false) { int targetIndex = layout->indexOf(treeView); QVBoxLayout* vboxLayout = new QVBoxLayout(this); vboxLayout->setSpacing(0); vboxLayout->setContentsMargins(0,0,0,0); filterLineEdit_ = new QtClosableLineEdit(this); filterLineEdit_->hide(); vboxLayout->addWidget(filterLineEdit_); vboxLayout->addWidget(treeView); setLayout(vboxLayout); layout->insertWidget(targetIndex, this); filterLineEdit_->installEventFilter(this); treeView->installEventFilter(this); sourceModel_ = treeView_->model(); } QtFilterWidget::~QtFilterWidget() { } bool QtFilterWidget::eventFilter(QObject*, QEvent* event) { if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease || // InputMethodQuery got introduced in Qt 5. #if QT_VERSION >= 0x050000 event->type() == QEvent::InputMethodQuery || #endif event->type() == QEvent::InputMethod) { event->ignore(); QKeyEvent* keyEvent = dynamic_cast<QKeyEvent*>(event); if (keyEvent) { if (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down) { return false; } else if ((keyEvent->key() == Qt::Key_Left || keyEvent->key() == Qt::Key_Right) && filterLineEdit_->text().isEmpty()) { return false; } else if (keyEvent->key() == Qt::Key_Alt && event->type() == QEvent::KeyPress) { isModifierSinglePressed_ = true; } else if (keyEvent->key() == Qt::Key_Alt && event->type() == QEvent::KeyRelease && isModifierSinglePressed_) { QPoint itemOffset(2,2); QPoint contextMenuPosition = treeView_->visualRect(treeView_->currentIndex()).topLeft() + itemOffset;; QApplication::postEvent(treeView_, new QContextMenuEvent(QContextMenuEvent::Keyboard, contextMenuPosition, treeView_->mapToGlobal(contextMenuPosition))); return true; } else if (keyEvent->key() == Qt::Key_Return) { JID target = treeView_->selectedJID(); if (target.isValid()) { eventStream_->send(boost::shared_ptr<UIEvent>(new RequestChatUIEvent(target))); } filterLineEdit_->setText(""); updateRosterFilters(); } else if (keyEvent->key() == Qt::Key_Escape) { filterLineEdit_->setText(""); } else { isModifierSinglePressed_ = false; } } filterLineEdit_->event(event); - filterLineEdit_->setVisible(!filterLineEdit_->text().isEmpty()); if (event->type() == QEvent::KeyRelease) { updateRosterFilters(); } return true; } return false; } void QtFilterWidget::popAllFilters() { std::vector<RosterFilter*> filters = treeView_->getRoster()->getFilters(); foreach(RosterFilter* filter, filters) { filters_.push_back(filter); treeView_->getRoster()->removeFilter(filter); } + treeView_->getRoster()->onFilterAdded.connect(boost::bind(&QtFilterWidget::handleFilterAdded, this, _1)); + treeView_->getRoster()->onFilterRemoved.connect(boost::bind(&QtFilterWidget::handleFilterRemoved, this, _1)); } void QtFilterWidget::pushAllFilters() { + treeView_->getRoster()->onFilterAdded.disconnect(boost::bind(&QtFilterWidget::handleFilterAdded, this, _1)); + treeView_->getRoster()->onFilterRemoved.disconnect(boost::bind(&QtFilterWidget::handleFilterRemoved, this, _1)); foreach(RosterFilter* filter, filters_) { treeView_->getRoster()->addFilter(filter); } filters_.clear(); } void QtFilterWidget::updateRosterFilters() { if (fuzzyRosterFilter_) { if (filterLineEdit_->text().isEmpty()) { // remove currently installed search filter and put old filters back treeView_->getRoster()->removeFilter(fuzzyRosterFilter_); delete fuzzyRosterFilter_; fuzzyRosterFilter_ = NULL; pushAllFilters(); } else { // remove currently intsalled search filter and put new search filter in place updateSearchFilter(); } } else { if (!filterLineEdit_->text().isEmpty()) { // remove currently installed filters and put a search filter in place popAllFilters(); updateSearchFilter(); } } + filterLineEdit_->setVisible(!filterLineEdit_->text().isEmpty()); } void QtFilterWidget::updateSearchFilter() { if (fuzzyRosterFilter_) { treeView_->getRoster()->removeFilter(fuzzyRosterFilter_); delete fuzzyRosterFilter_; fuzzyRosterFilter_ = NULL; } fuzzyRosterFilter_ = new FuzzyRosterFilter(Q2PSTRING(filterLineEdit_->text())); treeView_->getRoster()->addFilter(fuzzyRosterFilter_); treeView_->setCurrentIndex(sourceModel_->index(0, 0, sourceModel_->index(0,0))); } +void QtFilterWidget::handleFilterAdded(RosterFilter* filter) { + if (filter != fuzzyRosterFilter_) { + filterLineEdit_->setText(""); + updateRosterFilters(); + } +} + +void QtFilterWidget::handleFilterRemoved(RosterFilter* filter) { + /* make sure we don't end up adding this one back in later */ + filters_.erase(std::remove(filters_.begin(), filters_.end(), filter), filters_.end()); + if (filter != fuzzyRosterFilter_) { + filterLineEdit_->setText(""); + updateRosterFilters(); + } +} + } diff --git a/Swift/QtUI/Roster/QtFilterWidget.h b/Swift/QtUI/Roster/QtFilterWidget.h index 3e17566..d0307ea 100644 --- a/Swift/QtUI/Roster/QtFilterWidget.h +++ b/Swift/QtUI/Roster/QtFilterWidget.h @@ -1,48 +1,51 @@ /* * Copyright (c) 2013 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include <vector> #include <QBoxLayout> #include <QWidget> #include <Swift/Controllers/Roster/RosterFilter.h> #include <Swift/Controllers/Roster/FuzzyRosterFilter.h> #include <Swift/QtUI/Roster/QtTreeWidget.h> namespace Swift { class UIEventStream; class QtClosableLineEdit; class QtFilterWidget : public QWidget { Q_OBJECT public: QtFilterWidget(QWidget* parent, QtTreeWidget* treeView, UIEventStream* eventStream, QBoxLayout* layout = 0); virtual ~QtFilterWidget(); protected: bool eventFilter(QObject*, QEvent* event); private: void popAllFilters(); void pushAllFilters(); void updateRosterFilters(); void updateSearchFilter(); + void handleFilterAdded(RosterFilter* filter); + void handleFilterRemoved(RosterFilter* filter); + private: QtClosableLineEdit* filterLineEdit_; QtTreeWidget* treeView_; UIEventStream* eventStream_; std::vector<RosterFilter*> filters_; QAbstractItemModel* sourceModel_; FuzzyRosterFilter* fuzzyRosterFilter_; bool isModifierSinglePressed_; }; } |
Swift