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.
 
 
 
 
idealirc/IWin/NicklistController.cpp

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);
}
}