The complete source code of IdealIRC http://www.idealirc.org/
 
 
 
 
idealirc/IWin/IWinChannel.cpp

257 lines
7.5 KiB

/*
* IdealIRC - Internet Relay Chat client
* Copyright (C) 2021 Tom-Andre Barstad.
* This software is licensed under the Software Attribution License.
* See LICENSE for more information.
*/
#include "IWinChannel.h"
#include "IWinStatus.h"
#include "MdiManager.h"
#include "ICommand/Commands.h"
#include "Script/Manager.h"
#include "IRCClient/IRCChannel.h"
#include "IRCClient/IRCMember.h"
#include "IRCClient/Utilities.h"
#include "IRCClient/Commands.h"
#include <ConfigMgr.h>
#include <QHeaderView>
IWinChannel::IWinChannel(IWinStatus* statusParent, const QString& channelName)
: IWin(IWin::Type::Channel, statusParent)
, status(statusParent)
, connection(statusParent->getConnection())
{
setButtonText(channelName);
splitter = new QSplitter();
v_layout = new QVBoxLayout();
view = new IIRCView();
listbox = new IListWidget();
input = new ILineEdit([this](int a, QString& b){ return tabComplete(a,b); });
v_layout->setMargin(0);
v_layout->setSpacing(2);
splitter->addWidget(view);
splitter->addWidget(listbox);
v_layout->addWidget(splitter);
v_layout->addWidget(input);
setLayout(v_layout);
setTabOrder(input, view);
setTabOrder(view, listbox);
splitter->setStretchFactor(0, 8);
splitter->setStretchFactor(1, 1);
view->setFocusPolicy(Qt::FocusPolicy::NoFocus);
listbox->setFocusPolicy(Qt::FocusPolicy::NoFocus);
listbox->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
listbox->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection);
nlControl = new NicklistController(listbox, connection, channelName, this);
connect(&connection, &IRC::memberListReloaded,
this, &IWinChannel::memberListReloaded);
connect(&connection, &IRC::memberListReset,
this, &IWinChannel::memberListReset);
connect(&connection, &IRC::memberListClearedForAll,
this, &IWinChannel::memberListCleared);
connect(&connection, &IRC::memberAdded,
this, &IWinChannel::memberListAdd);
connect(&connection, &IRC::memberChanged,
this, &IWinChannel::memberListUpdateMember);
connect(&connection, &IRC::memberRemoved,
this, &IWinChannel::memberListRemoveMember);
connect(input, &ILineEdit::newLine,
this, &IWinChannel::newLine);
connect(listbox, &IListWidget::itemDoubleClicked,
this, &IWinChannel::listboxDoubleclick);
connect(view, &IIRCView::customContextMenuRequested, [this,channelName](const QPoint& point){
menuSymbols.clear();
menuSymbols.set("channel", channelName.toStdString());
ScriptManager::instance()->contextMenuPopup(ScriptMenuType::Channel, mapToGlobal(point), menuSymbols);
});
connect(listbox, &IListWidget::customContextMenuRequested, [this,channelName](const QPoint& pos){
menuSymbols.clear();
menuSymbols.set("channel", channelName.toStdString());
ValueArray members;
auto items = listbox->selectedItems();
if (items.isEmpty()) return;
int idx = 0;
for (auto* item : items) {
QString name = item->text();
if (isUserModePrefix(name[0]))
name.remove(0, 1);
members.emplace(std::to_string(idx++), new ValueHolder(name.toStdString()));
}
menuSymbols.set("selected", std::move(members));
QPoint posAdj = mapToGlobal(pos);
posAdj.setX(posAdj.x() + view->width() + splitter->handleWidth());
ScriptManager::instance()->contextMenuPopup(ScriptMenuType::Memberlist, posAdj, menuSymbols);
});
}
bool IWinChannel::printWithCustomTime(const QDateTime& timestamp, PrintType ptype, const QString& text)
{
view->print(timestamp, ptype, text);
MdiManager::instance().highlight(this, HL_Activity);
ConfigMgr& conf = ConfigMgr::instance();
if (conf.logging("Channels") == "1") {
QString path = conf.logging("Path");
if (path.isEmpty())
return true;
path += "/" + getButtonText() + ".log";
QByteArray out;
out.append(IIRCView::formatType(ptype, text));
out.append("\n");
QFile f(path);
if (!f.open(QIODevice::WriteOnly | QIODevice::Append))
return true;
f.write(out);
f.close();
}
return true;
}
void IWinChannel::refreshWindowTitle()
{
auto chan = connection.getChannel(getButtonText().toStdString());
const auto& topic = chan->topic();
std::string modes = concatenateModes(chan->modes());
QString title = getButtonText();
if (!modes.empty())
title += QStringLiteral(" (+%1)").arg(QString::fromStdString(modes));
if (!topic.empty())
title += QStringLiteral(": %1").arg(QString::fromStdString(topic));
setWindowTitle(title);
}
void IWinChannel::resetNicklist()
{
nlControl->clear();
}
void IWinChannel::newLine(const QString& line)
{
InputHandler inHndl(connection);
inHndl.parse(*this, line);
}
void IWinChannel::listboxDoubleclick(QListWidgetItem* item)
{
if (!item)
return;
QString nickname = item->text();
if (isUserModePrefix(nickname[0]))
nickname = nickname.mid(1);
input->setFocus();
IWin* subwin = status->createPrivateWindow(nickname);
subwin->refreshWindowTitle();
}
bool IWinChannel::isUserModePrefix(QChar c)
{
const auto& isupport = connection.isupport();
const auto& prefix = isupport.at("PREFIX"); // Always present in the isupport map.
auto begin = std::find(prefix.begin(), prefix.end(), ')') + 1;
return std::find(begin, prefix.end(), c.toLatin1()) != prefix.end();
}
void IWinChannel::closeEvent(QCloseEvent* evt)
{
if (listbox->count() > 0)
connection.command(Command::IRC::PART, { getButtonText().toStdString() }, "");
IWin::closeEvent(evt);
}
int IWinChannel::tabComplete(int index, QString& pattern)
{
if (pattern.isEmpty() || !connection.isOnline())
return 0;
if (connection.isChannelSymbol( pattern[0].toLatin1() ))
return connection.channelAutoComplete(index, pattern);
auto chan = connection.getChannel(getButtonText().toStdString());
auto members = chan->members();
int searchIdx = 0;
bool changed = false;
Retry:
for (const auto& mem : members) {
const auto nickname = QString::fromStdString( mem.member()->prefix().nickname() );
if (pattern.length() > nickname.length())
continue;
if (nickname.left(pattern.length()).compare(pattern, Qt::CaseInsensitive) == 0) {
if (searchIdx == index) {
pattern = nickname;
changed = true;
}
++searchIdx;
}
}
if (!changed && searchIdx > 0) {
changed = false;
searchIdx = 0;
index = 0;
goto Retry;
}
return searchIdx;
}
void IWinChannel::memberListReloaded(const QString& channel)
{
if (channel != getButtonText()) return;
nlControl->reload();
}
void IWinChannel::memberListReset(const QString& channel)
{
if (channel != getButtonText()) return;
nlControl->clear();
}
void IWinChannel::memberListCleared()
{
nlControl->clear();
}
void IWinChannel::memberListAdd(const QString& channel, const IRCMemberEntry& member)
{
if (channel != getButtonText()) return;
nlControl->addMember(member);
}
void IWinChannel::memberListUpdateMember(const QString& nickname, const IRCMemberEntry& member)
{
nlControl->updateMember(nickname, member);
}
void IWinChannel::memberListRemoveMember(const QString& channel, const IRCMemberEntry& member)
{
if (channel != getButtonText()) return;
nlControl->removeMember(member);
}