The complete source code of IdealIRC
http://www.idealirc.org/
257 lines
7.5 KiB
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);
|
|
}
|
|
|