The complete source code of IdealIRC
http://www.idealirc.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
214 lines
5.6 KiB
214 lines
5.6 KiB
/*
|
|
* IdealIRC - Internet Relay Chat client
|
|
* Copyright (C) 2019 Tom-Andre Barstad
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include "NicklistController.h"
|
|
#include "ConfigMgr.h"
|
|
#include "IRC.h"
|
|
#include "IRCClient/IRCChannel.h"
|
|
#include "IRCClient/IRCMember.h"
|
|
#include <algorithm>
|
|
#include <QDebug>
|
|
|
|
NicklistController::NicklistController(QListWidget* listbox, IRC& server, const QString& channel, QObject* parent)
|
|
: QObject(parent)
|
|
, m_listbox(listbox)
|
|
, m_server(server)
|
|
, m_channel(channel)
|
|
{
|
|
ConfigMgr& conf = ConfigMgr::instance();
|
|
connect(&conf, &ConfigMgr::saved, this, &NicklistController::newConfiguration);
|
|
|
|
/* Precalculate m_prefixOrdered */
|
|
const auto& isupport = m_server.isupport();
|
|
const auto& prefix = isupport.at("PREFIX"); // PREFIX is always present
|
|
auto start = prefix.find_last_of(')');
|
|
if (start != std::string::npos) {
|
|
++start;
|
|
m_prefixOrdered = prefix.substr(start).c_str();
|
|
}
|
|
}
|
|
|
|
void NicklistController::reload()
|
|
{
|
|
m_listbox->clear();
|
|
m_items.clear();
|
|
|
|
auto chan = m_server.getChannel(m_channel.toStdString());
|
|
if (!chan)
|
|
return;
|
|
|
|
const auto& members = chan->members();
|
|
|
|
for (const auto& mem : members)
|
|
m_items << makeItemText(mem);
|
|
|
|
std::sort(m_items.begin(), m_items.end(),
|
|
[this](const QString& left, const QString& right) {
|
|
return lessThan(left, right);
|
|
});
|
|
|
|
m_listbox->addItems(m_items);
|
|
newConfiguration();
|
|
}
|
|
|
|
void NicklistController::clear()
|
|
{
|
|
m_listbox->clear();
|
|
}
|
|
|
|
bool NicklistController::lessThan(const QString& left, const QString& right)
|
|
{
|
|
auto li = m_prefixOrdered.indexOf(left[0]);
|
|
auto ri = m_prefixOrdered.indexOf(right[0]);
|
|
|
|
/* Both sides got a prefix */
|
|
if (li != -1 && ri != -1) {
|
|
if (left[0] == right[0])
|
|
return left.mid(1).compare(right.mid(1), Qt::CaseInsensitive) < 0;
|
|
else
|
|
return li < ri;
|
|
}
|
|
|
|
/* Neither side got a prefix */
|
|
else if (li == -1 && ri == -1)
|
|
return left.compare(right, Qt::CaseInsensitive) < 0;
|
|
|
|
/* One side got a prefix */
|
|
else
|
|
return ri == -1;
|
|
}
|
|
|
|
void NicklistController::addMember(const IRCMemberEntry& member)
|
|
{
|
|
QString newItem = makeItemText(member);
|
|
int pos = findNewItemPosition(newItem);
|
|
m_items.insert(pos, newItem);
|
|
m_listbox->insertItem(pos, newItem);
|
|
|
|
QListWidgetItem* item = m_listbox->item(pos);
|
|
updatePrefixColor(item, std::nullopt);
|
|
}
|
|
|
|
void NicklistController::updateMember(const QString& nickname, const IRCMemberEntry& member)
|
|
{
|
|
// Find position of the item in the list.
|
|
// TODO use this loop also to find new item position.
|
|
int currPos = -1;
|
|
for (int i = 0; i < m_items.count(); ++i) {
|
|
QString item = m_items[i];
|
|
if (m_prefixOrdered.indexOf(item[0]) > -1)
|
|
item = item.mid(1);
|
|
if (item == nickname) {
|
|
currPos = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// We don't have this nickname in this channel, and it is safe to ignore it silently.
|
|
// This is because when a nickname changes, that user might not be on all channels we're on.
|
|
if (currPos == -1) return;
|
|
|
|
QListWidgetItem* lbitem = m_listbox->item(currPos);
|
|
bool isSelected = lbitem->isSelected();
|
|
m_items.removeAt(currPos);
|
|
m_listbox->removeItemWidget(lbitem);
|
|
delete lbitem;
|
|
|
|
QString newItemText = makeItemText(member);
|
|
int newPos = findNewItemPosition(newItemText);
|
|
|
|
lbitem = new QListWidgetItem(newItemText);
|
|
m_listbox->insertItem(newPos, lbitem);
|
|
m_items.insert(newPos, newItemText);
|
|
lbitem->setSelected(isSelected);
|
|
updatePrefixColor(lbitem, std::nullopt);
|
|
}
|
|
|
|
void NicklistController::removeMember(const IRCMemberEntry& member)
|
|
{
|
|
QString itemText = makeItemText(member);
|
|
int pos = 0;
|
|
int delpos = -1;
|
|
while (pos < m_items.count()) {
|
|
const QString& item = m_items[pos];
|
|
if (item == itemText) {
|
|
delpos = pos;
|
|
break;
|
|
}
|
|
++pos;
|
|
}
|
|
|
|
if (delpos == -1) {
|
|
qWarning() << "Got request to delete" << itemText << "from" << m_channel << "but it was not found!";
|
|
return;
|
|
}
|
|
|
|
m_items.removeAt(delpos);
|
|
QListWidgetItem* lbitem = m_listbox->item(delpos);
|
|
m_listbox->removeItemWidget(lbitem);
|
|
delete lbitem;
|
|
}
|
|
|
|
QString NicklistController::makeItemText(const IRCMemberEntry& member)
|
|
{
|
|
QString item;
|
|
auto modes = member.modes();
|
|
if (!modes.empty()) {
|
|
auto prefixes = m_server.toMemberPrefix(modes).c_str();
|
|
item += prefixes[0];
|
|
}
|
|
item += member.member()->prefix().nickname().c_str();
|
|
return item;
|
|
}
|
|
|
|
int NicklistController::findNewItemPosition(const QString& text)
|
|
{
|
|
int pos = 0;
|
|
while (pos < m_items.count()) {
|
|
const QString& item = m_items[pos];
|
|
if (lessThan(text, item))
|
|
break;
|
|
|
|
++pos;
|
|
}
|
|
|
|
return pos;
|
|
}
|
|
|
|
void NicklistController::updatePrefixColor(QListWidgetItem* item, std::optional<QColor> color)
|
|
{
|
|
if (!color && m_prefixOrdered.indexOf(item->text()[0]) == -1)
|
|
return;
|
|
|
|
ConfigMgr& conf = ConfigMgr::instance();
|
|
if (!color) {
|
|
color = conf.prefixColor(item->text()[0]);
|
|
if (!color) return;
|
|
}
|
|
item->setForeground(*color);
|
|
}
|
|
|
|
void NicklistController::newConfiguration()
|
|
{
|
|
ConfigMgr& conf = ConfigMgr::instance();
|
|
for (int i = 0; i < m_listbox->count(); ++i) {
|
|
updatePrefixColor(m_listbox->item(i) , QColor(conf.color("ListboxForeground")));
|
|
updatePrefixColor(m_listbox->item(i) , std::nullopt);
|
|
}
|
|
}
|
|
|