1
0
forked from blue/squawk

Disabled context menu on items of not connected account, roster contacts group moving, bugfixes with roster contacts group moving ungrouping and copying

This commit is contained in:
Blue 2019-09-28 17:30:16 +03:00
parent e4d1e21ea0
commit 415d56ba69
20 changed files with 314 additions and 36 deletions

View File

@ -1153,3 +1153,50 @@ void Core::Account::addNewRoom(const QString& jid, const QString& nick, const QS
{"name", conf->getName()}
});
}
void Core::Account::addContactToGroupRequest(const QString& jid, const QString& groupName)
{
std::map<QString, Contact*>::const_iterator itr = contacts.find(jid);
if (itr == contacts.end()) {
qDebug() << "An attempt to add non existing contact" << jid << "of account" << name << "to the group" << groupName << ", skipping";
} else {
QXmppRosterManager& rm = client.rosterManager();
QXmppRosterIq::Item item = rm.getRosterEntry(jid);
QSet<QString> groups = item.groups();
if (groups.find(groupName) == groups.end()) { //TODO need to change it, I guess that sort of code is better in qxmpp lib
groups.insert(groupName);
item.setGroups(groups);
QXmppRosterIq iq;
iq.setType(QXmppIq::Set);
iq.addItem(item);
client.sendPacket(iq);
} else {
qDebug() << "An attempt to add contact" << jid << "of account" << name << "to the group" << groupName << "but it's already in that group, skipping";
}
}
}
void Core::Account::removeContactFromGroupRequest(const QString& jid, const QString& groupName)
{
std::map<QString, Contact*>::const_iterator itr = contacts.find(jid);
if (itr == contacts.end()) {
qDebug() << "An attempt to remove non existing contact" << jid << "of account" << name << "from the group" << groupName << ", skipping";
} else {
QXmppRosterManager& rm = client.rosterManager();
QXmppRosterIq::Item item = rm.getRosterEntry(jid);
QSet<QString> groups = item.groups();
QSet<QString>::const_iterator gItr = groups.find(groupName);
if (gItr != groups.end()) {
groups.erase(gItr);
item.setGroups(groups);
QXmppRosterIq iq;
iq.setType(QXmppIq::Set);
iq.addItem(item);
client.sendPacket(iq);
} else {
qDebug() << "An attempt to remove contact" << jid << "of account" << name << "from the group" << groupName << "but it's not in that group, skipping";
}
}
}

View File

@ -70,6 +70,8 @@ public:
void unsubscribeFromContact(const QString& jid, const QString& reason);
void removeContactRequest(const QString& jid);
void addContactRequest(const QString& jid, const QString& name, const QSet<QString>& groups);
void addContactToGroupRequest(const QString& jid, const QString& groupName);
void removeContactFromGroupRequest(const QString& jid, const QString& groupName);
void setRoomJoined(const QString& jid, bool joined);
void setRoomAutoJoin(const QString& jid, bool joined);

View File

@ -498,3 +498,23 @@ void Core::Squawk::downloadFileRequest(const QString& messageId, const QString&
{
network.downladFileRequest(messageId, url);
}
void Core::Squawk::addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName)
{
AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) {
qDebug() << "An attempt to add contact" << jid << "of existing account" << account << "to the group" << groupName << ", skipping";
return;
}
itr->second->addContactToGroupRequest(jid, groupName);
}
void Core::Squawk::removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName)
{
AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) {
qDebug() << "An attempt to add contact" << jid << "of existing account" << account << "to the group" << groupName << ", skipping";
return;
}
itr->second->removeContactFromGroupRequest(jid, groupName);
}

View File

@ -79,6 +79,8 @@ public slots:
void requestArchive(const QString& account, const QString& jid, int count, const QString& before);
void subscribeContact(const QString& account, const QString& jid, const QString& reason);
void unsubscribeContact(const QString& account, const QString& jid, const QString& reason);
void addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName);
void removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName);
void removeContactRequest(const QString& account, const QString& jid);
void addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet<QString>& groups);
void setRoomJoined(const QString& account, const QString& jid, bool joined);

View File

@ -350,6 +350,6 @@ QIcon Shared::icon(const QString& name, bool big)
return QIcon::fromTheme(itr->second.first, QIcon(prefix + itr->second.second));
} else {
qDebug() << "Icon" << name << "not found";
throw 1;
return QIcon::fromTheme(name);
}
}

View File

@ -89,6 +89,8 @@ int main(int argc, char *argv[])
squawk, SLOT(addRoomRequest(const QString&, const QString&, const QString&, const QString&, bool)));
QObject::connect(&w, SIGNAL(fileLocalPathRequest(const QString&, const QString&)), squawk, SLOT(fileLocalPathRequest(const QString&, const QString&)));
QObject::connect(&w, SIGNAL(downloadFileRequest(const QString&, const QString&)), squawk, SLOT(downloadFileRequest(const QString&, const QString&)));
QObject::connect(&w, &Squawk::addContactToGroupRequest, squawk, &Core::Squawk::addContactToGroupRequest);
QObject::connect(&w, &Squawk::removeContactFromGroupRequest, squawk, &Core::Squawk::removeContactFromGroupRequest);
QObject::connect(squawk, SIGNAL(newAccount(const QMap<QString, QVariant>&)), &w, SLOT(newAccount(const QMap<QString, QVariant>&)));
QObject::connect(squawk, SIGNAL(addContact(const QString&, const QString&, const QString&, const QMap<QString, QVariant>&)),

View File

@ -32,6 +32,15 @@ Models::AbstractParticipant::AbstractParticipant(Models::Item::Type p_type, cons
}
}
Models::AbstractParticipant::AbstractParticipant(const Models::AbstractParticipant& other):
Item(other),
availability(other.availability),
lastActivity(other.lastActivity),
status(other.status)
{
}
Models::AbstractParticipant::~AbstractParticipant()
{
}

View File

@ -31,6 +31,7 @@ class AbstractParticipant : public Models::Item
Q_OBJECT
public:
explicit AbstractParticipant(Type p_type, const QMap<QString, QVariant> &data, Item *parentItem = 0);
AbstractParticipant(const AbstractParticipant& other);
~AbstractParticipant();
virtual int columnCount() const override;

View File

@ -323,3 +323,28 @@ bool Models::Contact::columnInvolvedInDisplay(int col)
{
return Item::columnInvolvedInDisplay(col) && col == 1;
}
Models::Contact * Models::Contact::copy() const
{
Contact* cnt = new Contact(*this);
return cnt;
}
Models::Contact::Contact(const Models::Contact& other):
Item(other),
jid(other.jid),
availability(other.availability),
state(other.state),
presences(),
messages(other.messages),
childMessages(0)
{
for (const Presence* pres : other.presences) {
Presence* pCopy = new Presence(*pres);
presences.insert(pCopy->getName(), pCopy);
Item::appendChild(pCopy);
connect(pCopy, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(refresh()));
}
refresh();
}

View File

@ -34,6 +34,7 @@ class Contact : public Item
public:
typedef std::deque<Shared::Message> Messages;
Contact(const QString& p_jid, const QMap<QString, QVariant> &data, Item *parentItem = 0);
Contact(const Contact& other);
~Contact();
QString getJid() const;
@ -59,6 +60,8 @@ public:
void getMessages(Messages& container) const;
QString getDisplayedName() const override;
Contact* copy() const;
protected:
void _removeChild(int index) override;
bool columnInvolvedInDisplay(int col) override;

View File

@ -103,3 +103,16 @@ unsigned int Models::Group::getOnlineContacts() const
return amount;
}
bool Models::Group::hasContact(const QString& jid) const
{
for (Models::Item* item : childItems) {
if (item->type == Item::contact) {
const Contact* cnt = static_cast<const Contact*>(item);
if (cnt->getJid() == jid) {
return true;
}
}
}
return false;
}

View File

@ -37,6 +37,8 @@ public:
unsigned int getUnreadMessages() const;
unsigned int getOnlineContacts() const;
bool hasContact(const QString& jid) const;
protected:
void _removeChild(int index) override;
void setUnreadMessages(unsigned int amount);

View File

@ -34,6 +34,15 @@ Models::Item::Item(Type p_type, const QMap<QString, QVariant> &p_data, Item *p_p
}
}
Models::Item::Item(const Models::Item& other):
QObject(),
type(other.type),
name(other.name),
childItems(),
parent(nullptr)
{
}
Models::Item::~Item()
{
std::deque<Item*>::const_iterator itr = childItems.begin();
@ -225,6 +234,24 @@ QString Models::Item::getAccountName() const
return acc->getName();
}
Shared::Availability Models::Item::getAccountAvailability() const
{
const Account* acc = static_cast<const Account*>(getParentAccount());
if (acc == 0) {
return Shared::offline;
}
return acc->getAvailability();
}
Shared::ConnectionState Models::Item::getAccountConnectionState() const
{
const Account* acc = static_cast<const Account*>(getParentAccount());
if (acc == 0) {
return Shared::disconnected;
}
return acc->getState();
}
QString Models::Item::getDisplayedName() const
{
return name;

View File

@ -25,6 +25,8 @@
#include <deque>
#include "../../global.h"
namespace Models {
class Item : public QObject{
@ -41,6 +43,7 @@ class Item : public QObject{
};
explicit Item(Type p_type, const QMap<QString, QVariant> &data, Item *parentItem = 0);
Item(const Item& other);
~Item();
signals:
@ -71,6 +74,8 @@ class Item : public QObject{
QString getAccountName() const;
QString getAccountJid() const;
QString getAccountResource() const;
Shared::ConnectionState getAccountConnectionState() const;
Shared::Availability getAccountAvailability() const;
const Type type;

View File

@ -24,6 +24,13 @@ Models::Presence::Presence(const QMap<QString, QVariant>& data, Item* parentItem
{
}
Models::Presence::Presence(const Models::Presence& other):
AbstractParticipant(other),
messages(other.messages)
{
}
Models::Presence::~Presence()
{
}

View File

@ -32,6 +32,7 @@ class Presence : public Models::AbstractParticipant
public:
typedef std::deque<Shared::Message> Messages;
explicit Presence(const QMap<QString, QVariant> &data, Item *parentItem = 0);
Presence(const Presence& other);
~Presence();
int columnCount() const override;

View File

@ -383,50 +383,52 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons
{
Item* parent;
Account* acc;
Contact* contact;
Contact* sample = 0;
ElId id(account, jid);
{
std::map<QString, Account*>::iterator itr = accounts.find(account);
if (itr == accounts.end()) {
qDebug() << "An attempt to add a contact " << jid << " to non existing account " << account << ", skipping";
qDebug() << "An attempt to add a contact" << jid << "to non existing account" << account << ", skipping";
return;
}
acc = itr->second;
}
if (group == "") {
std::multimap<ElId, Contact*>::iterator itr = contacts.lower_bound(id);
std::multimap<ElId, Contact*>::iterator eItr = contacts.upper_bound(id);
while (itr != eItr) {
if (itr->second->parentItem() == acc) {
qDebug() << "An attempt to add a contact " << jid << " ungrouped to non the account " << account << " for the second time, skipping";
for (std::multimap<ElId, Contact*>::iterator itr = contacts.lower_bound(id), eItr = contacts.upper_bound(id); itr != eItr; ++itr) {
sample = itr->second; //need to find if this contact is already added somewhere
break; //so one iteration is enough
}
if (group == "") { //this means this contact is already added somewhere and there is no sense to add it ungrouped
if (sample != 0) {
qDebug() << "An attempt to add a contact" << jid << "to the ungrouped contact set of account" << account << "for the second time, skipping";
return;
}
itr++;
}
} else {
parent = acc;
}
} else {
std::map<ElId, Group*>::iterator itr = groups.find({account, group});
if (itr == groups.end()) {
qDebug() << "An attempt to add a contact " << jid << " to non existing group " << group << ", skipping";
return;
qDebug() << "An attempt to add a contact" << jid << "to non existing group" << group << ", adding group";
addGroup(account, group);
itr = groups.find({account, group});
}
parent = itr->second;
for (int i = 0; i < parent->childCount(); ++i) {
for (int i = 0; i < parent->childCount(); ++i) { //checking if the contact is already added to that group
Item* item = parent->child(i);
if (item->type == Item::contact) {
Contact* ca = static_cast<Contact*>(item);
if (ca->getJid() == jid) {
qDebug() << "An attempt to add a contact " << jid << " to the group " << group << " for the second time, skipping";
qDebug() << "An attempt to add a contact" << jid << "to the group" << group << "for the second time, skipping";
return;
}
}
}
for (int i = 0; i < acc->childCount(); ++i) {
for (int i = 0; i < acc->childCount(); ++i) { //checking if that contact is among ugrouped
Item* item = acc->child(i);
if (item->type == Item::contact) {
Contact* ca = static_cast<Contact*>(item);
@ -440,7 +442,12 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons
}
}
Contact* contact;
if (sample == 0) {
contact = new Contact(jid, data);
} else {
contact = sample->copy();
}
contacts.insert(std::make_pair(id, contact));
parent->appendChild(contact);
}
@ -548,15 +555,24 @@ void Models::Roster::removeContact(const QString& account, const QString& jid, c
qDebug() << "An attempt to remove contact " << jid << " from non existing group " << group << " of account " << account <<", skipping";
return;
}
Account* acc = accounts.find(account)->second; //I assume the account is found, otherwise there will be no groups with that ElId;
Group* gr = gItr->second;
Contact* cont = 0;
std::multimap<ElId, Contact*>::iterator cBeg = contacts.lower_bound(contactId);
std::multimap<ElId, Contact*>::iterator cEnd = contacts.upper_bound(contactId);
for (;cBeg != cEnd; ++cBeg) {
unsigned int entries(0);
unsigned int ungroupped(0);
for (std::multimap<ElId, Contact*>::iterator cBeg = contacts.lower_bound(contactId), cEnd = contacts.upper_bound(contactId); cBeg != cEnd; ++cBeg) {
++entries;
Contact* elem = cBeg->second;
if (elem->parentItem() == acc) {
++ungroupped;
}
}
if (ungroupped == 0 && entries == 1) {
for (std::multimap<ElId, Contact*>::iterator cBeg = contacts.lower_bound(contactId), cEnd = contacts.upper_bound(contactId); cBeg != cEnd; ++cBeg) {
if (cBeg->second->parentItem() == gr) {
cont = cBeg->second;
contacts.erase(cBeg);
break;
}
}
@ -566,12 +582,33 @@ void Models::Roster::removeContact(const QString& account, const QString& jid, c
return;
}
qDebug() << "An attempt to remove last instance of contact" << jid << "from the group" << group << ", contact will be moved to ungrouped contacts of" << account;
acc->appendChild(cont);
if (gr->childCount() == 0) {
removeGroup(account, group);
}
} else {
for (std::multimap<ElId, Contact*>::iterator cBeg = contacts.lower_bound(contactId), cEnd = contacts.upper_bound(contactId); cBeg != cEnd; ++cBeg) {
if (cBeg->second->parentItem() == gr) {
cont = cBeg->second;
contacts.erase(cBeg);
break;
}
}
if (cont == 0) {
qDebug() << "An attempt to remove contact" << jid << "of account" << account << "from group" << group <<", but there is no such contact in that group, skipping";
return;
}
gr->removeChild(cont->row());
cont->deleteLater();
if (gr->childCount() == 0) {
removeGroup(account, group);
}
}
}
void Models::Roster::onChildChanged(Models::Item* item, int row, int col)
@ -844,3 +881,27 @@ void Models::Roster::removeRoomParticipant(const QString& account, const QString
itr->second->removeParticipant(name);
}
}
std::deque<QString> Models::Roster::groupList(const QString& account) const
{
std::deque<QString> answer;
for (std::pair<ElId, Group*> pair : groups) {
if (pair.first.account == account) {
answer.push_back(pair.first.name);
}
}
return answer;
}
bool Models::Roster::groupHasContact(const QString& account, const QString& group, const QString& contact) const
{
ElId grId({account, group});
std::map<ElId, Group*>::const_iterator gItr = groups.find(grId);
if (gItr == groups.end()) {
return false;
} else {
const Group* gr = gItr->second;
return gr->hasContact(contact);
}
}

View File

@ -71,6 +71,9 @@ public:
QModelIndex parent ( const QModelIndex& child ) const override;
QModelIndex index ( int row, int column, const QModelIndex& parent ) const override;
std::deque<QString> groupList(const QString& account) const;
bool groupHasContact(const QString& account, const QString& group, const QString& contactJID) const;
Accounts* accountsModel;
private:

View File

@ -20,6 +20,7 @@
#include "ui_squawk.h"
#include <QDebug>
#include <QIcon>
#include <QInputDialog>
Squawk::Squawk(QWidget *parent) :
QMainWindow(parent),
@ -517,6 +518,7 @@ void Squawk::onRosterContextMenu(const QPoint& point)
contextMenu->clear();
bool hasMenu = false;
bool active = item->getAccountConnectionState() == Shared::connected;
switch (item->type) {
case Models::Item::account: {
Models::Account* acc = static_cast<Models::Account*>(item);
@ -525,17 +527,20 @@ void Squawk::onRosterContextMenu(const QPoint& point)
if (acc->getState() != Shared::disconnected) {
QAction* con = contextMenu->addAction(Shared::icon("network-disconnect"), "Disconnect");
con->setEnabled(active);
connect(con, &QAction::triggered, [this, name]() {
emit disconnectAccount(name);
});
} else {
QAction* con = contextMenu->addAction(Shared::icon("network-connect"), "Connect");
con->setEnabled(active);
connect(con, &QAction::triggered, [this, name]() {
emit connectAccount(name);
});
}
QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), "Remove");
remove->setEnabled(active);
connect(remove, &QAction::triggered, [this, name]() {
emit removeAccount(name);
});
@ -547,6 +552,7 @@ void Squawk::onRosterContextMenu(const QPoint& point)
hasMenu = true;
QAction* dialog = contextMenu->addAction(Shared::icon("mail-message"), "Open dialog");
dialog->setEnabled(active);
connect(dialog, &QAction::triggered, [this, index]() {
onRosterItemDoubleClicked(index);
});
@ -556,6 +562,7 @@ void Squawk::onRosterContextMenu(const QPoint& point)
case Shared::both:
case Shared::to: {
QAction* unsub = contextMenu->addAction(Shared::icon("news-unsubscribe"), "Unsubscribe");
unsub->setEnabled(active);
connect(unsub, &QAction::triggered, [this, cnt]() {
emit unsubscribeContact(cnt->getAccountName(), cnt->getJid(), "");
});
@ -565,13 +572,48 @@ void Squawk::onRosterContextMenu(const QPoint& point)
case Shared::unknown:
case Shared::none: {
QAction* sub = contextMenu->addAction(Shared::icon("news-subscribe"), "Subscribe");
sub->setEnabled(active);
connect(sub, &QAction::triggered, [this, cnt]() {
emit subscribeContact(cnt->getAccountName(), cnt->getJid(), "");
});
}
}
QMenu* groupsMenu = contextMenu->addMenu(Shared::icon("group"), "Groups");
QString accName = cnt->getAccountName();
QString cntJID = cnt->getJid();
std::deque<QString> groupList = rosterModel.groupList(accName);
for (QString groupName : groupList) {
QAction* gr = groupsMenu->addAction(groupName);
gr->setCheckable(true);
gr->setChecked(rosterModel.groupHasContact(accName, groupName, cntJID));
gr->setEnabled(active);
connect(gr, &QAction::toggled, [this, accName, groupName, cntJID](bool checked) {
if (checked) {
emit addContactToGroupRequest(accName, cntJID, groupName);
} else {
emit removeContactFromGroupRequest(accName, cntJID, groupName);
}
});
}
QAction* newGroup = groupsMenu->addAction(Shared::icon("resource-group-new"), "New group");
newGroup->setEnabled(active);
connect(newGroup, &QAction::triggered, [this, accName, cntJID]() {
QInputDialog* dialog = new QInputDialog(this);
connect(dialog, &QDialog::accepted, [this, dialog, accName, cntJID]() {
emit addContactToGroupRequest(accName, cntJID, dialog->textValue());
dialog->deleteLater();
});
connect(dialog, &QDialog::rejected, dialog, &QObject::deleteLater);
dialog->setInputMode(QInputDialog::TextInput);
dialog->setLabelText("New group name");
dialog->setWindowTitle("Add " + cntJID + " to a new group");
dialog->exec();
});
QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), "Remove");
remove->setEnabled(active);
connect(remove, &QAction::triggered, [this, cnt]() {
emit removeContactRequest(cnt->getAccountName(), cnt->getJid());
});
@ -583,6 +625,7 @@ void Squawk::onRosterContextMenu(const QPoint& point)
hasMenu = true;
QAction* dialog = contextMenu->addAction(Shared::icon("mail-message"), "Open conversation");
dialog->setEnabled(active);
connect(dialog, &QAction::triggered, [this, index]() {
onRosterItemDoubleClicked(index);
});
@ -591,6 +634,7 @@ void Squawk::onRosterContextMenu(const QPoint& point)
Models::Roster::ElId id(room->getAccountName(), room->getJid());
if (room->getAutoJoin()) {
QAction* unsub = contextMenu->addAction(Shared::icon("news-unsubscribe"), "Unsubscribe");
unsub->setEnabled(active);
connect(unsub, &QAction::triggered, [this, id]() {
emit setRoomAutoJoin(id.account, id.name, false);
if (conversations.find(id) == conversations.end()) { //to leave the room if it's not opened in a conversation window
@ -599,6 +643,7 @@ void Squawk::onRosterContextMenu(const QPoint& point)
});
} else {
QAction* unsub = contextMenu->addAction(Shared::icon("news-subscribe"), "Subscribe");
unsub->setEnabled(active);
connect(unsub, &QAction::triggered, [this, id]() {
emit setRoomAutoJoin(id.account, id.name, true);
if (conversations.find(id) == conversations.end()) { //to join the room if it's not already joined
@ -608,6 +653,7 @@ void Squawk::onRosterContextMenu(const QPoint& point)
}
QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), "Remove");
remove->setEnabled(active);
connect(remove, &QAction::triggered, [this, id]() {
emit removeRoomRequest(id.account, id.name);
});

View File

@ -62,6 +62,8 @@ signals:
void unsubscribeContact(const QString& account, const QString& jid, const QString& reason);
void removeContactRequest(const QString& account, const QString& jid);
void addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet<QString>& groups);
void addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName);
void removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName);
void setRoomJoined(const QString& account, const QString& jid, bool joined);
void setRoomAutoJoin(const QString& account, const QString& jid, bool joined);
void addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin);