#17 Tabs to spaces

master
Tomatix 4 years ago
parent fac1766fc5
commit 93e4840ec4
  1. 12
      CMakeLists.txt
  2. 60
      ICommand/CommandData.cpp
  3. 16
      ICommand/CommandData.h
  4. 18
      ICommand/Commands.h
  5. 20
      ICommand/External/kick.cpp
  6. 28
      ICommand/External/notice.cpp
  7. 30
      ICommand/External/privmsg.cpp
  8. 1116
      ICommand/ICommand.cpp
  9. 44
      ICommand/ICommand.h
  10. 6
      ICommand/ICommandPriv.cpp
  11. 12
      ICommand/ICommandPriv.h
  12. 26
      ICommand/Internal/me.cpp
  13. 552
      IConfig/ColorConfig.cpp
  14. 74
      IConfig/ColorConfig.h
  15. 178
      IConfig/IConfig.cpp
  16. 52
      IConfig/IConfig.h
  17. 100
      IConfig/IConfigLogging.cpp
  18. 30
      IConfig/IConfigLogging.h
  19. 154
      IConfig/IConfigOptions.cpp
  20. 66
      IConfig/IConfigOptions.h
  21. 128
      IConfig/IConfigServers.cpp
  22. 42
      IConfig/IConfigServers.h
  23. 458
      IConfig/ServerEditor.cpp
  24. 60
      IConfig/ServerEditor.h
  25. 60
      IConfig/ServerMgr.cpp
  26. 6
      IConfig/ServerMgr.h
  27. 256
      IConfig/ServerModel.cpp
  28. 16
      IConfig/ServerModel.h
  29. 108
      IRCClient/Commands.h
  30. 260
      IRCClient/DCC.cpp
  31. 80
      IRCClient/DCC.h
  32. 376
      IRCClient/IRCBase.cpp
  33. 208
      IRCClient/IRCBase.h
  34. 28
      IRCClient/IRCBasePriv.h
  35. 50
      IRCClient/IRCChannel.cpp
  36. 40
      IRCClient/IRCChannel.h
  37. 198
      IRCClient/IRCError.cpp
  38. 72
      IRCClient/IRCError.h
  39. 24
      IRCClient/IRCMember.cpp
  40. 20
      IRCClient/IRCMember.h
  41. 60
      IRCClient/IRCMemberEntry.cpp
  42. 18
      IRCClient/IRCMemberEntry.h
  43. 96
      IRCClient/IRCPrefix.cpp
  44. 58
      IRCClient/IRCPrefix.h
  45. 156
      IRCClient/Utilities.cpp
  46. 22
      IWin/IWin.cpp
  47. 60
      IWin/IWin.h
  48. 224
      IWin/IWinChannel.cpp
  49. 50
      IWin/IWinChannel.h
  50. 102
      IWin/IWinPrivate.cpp
  51. 22
      IWin/IWinPrivate.h
  52. 216
      IWin/IWinStatus.cpp
  53. 50
      IWin/IWinStatus.h
  54. 272
      IWin/NicklistController.cpp
  55. 34
      IWin/NicklistController.h
  56. 28
      IdealIRC/AboutIIRC.cpp
  57. 8
      IdealIRC/AboutIIRC.h
  58. 394
      IdealIRC/ButtonbarMgr.cpp
  59. 100
      IdealIRC/ButtonbarMgr.h
  60. 6
      IdealIRC/CMakeLists.txt
  61. 18
      IdealIRC/ConfigMgr.cpp
  62. 48
      IdealIRC/ConfigMgr.h
  63. 396
      IdealIRC/IRC.cpp
  64. 106
      IdealIRC/IRC.h
  65. 254
      IdealIRC/IdealIRC.cpp
  66. 56
      IdealIRC/IdealIRC.h
  67. 8
      IdealIRC/IniFile.h
  68. 76
      IdealIRC/InputHandler.cpp
  69. 12
      IdealIRC/InputHandler.h
  70. 342
      IdealIRC/MdiManager.cpp
  71. 94
      IdealIRC/MdiManager.h
  72. 42
      IdealIRC/ScriptEvent.h
  73. 10
      IdealIRC/main.cpp
  74. 174
      Script/Builtin/Builtin.cpp
  75. 254
      Script/Builtin/DialogUtils.cpp
  76. 64
      Script/Builtin/Error.cpp
  77. 204
      Script/Builtin/GeneralUtils.cpp
  78. 258
      Script/Builtin/ListUtils.cpp
  79. 86
      Script/Builtin/MapUtils.cpp
  80. 694
      Script/Builtin/Mathematics.cpp
  81. 308
      Script/Builtin/StringUtils.cpp
  82. 194
      Script/Dialog.cpp
  83. 106
      Script/Dialog.h
  84. 368
      Script/Manager.cpp
  85. 116
      Script/Manager.h
  86. 124
      Script/ManagerListModel.cpp
  87. 24
      Script/ManagerListModel.h
  88. 114
      Script/Menu.cpp
  89. 70
      Script/Menu.h
  90. 30
      Script/ParserOperator.h
  91. 124
      Script/ParserToken.cpp
  92. 42
      Script/ParserToken.h
  93. 1308
      Script/Script.cpp
  94. 78
      Script/Script.h
  95. 110
      Script/SymbolScope.cpp
  96. 70
      Script/SymbolScope.h
  97. 1720
      Script/Tokenizer.cpp
  98. 50
      Script/Tokenizer.h
  99. 260
      Script/Tokens.cpp
  100. 384
      Script/Tokens.h
  101. Some files were not shown because too many files have changed in this diff Show More

@ -33,18 +33,18 @@ option(BUILD_DEV_STUFF "Build additional development stuff" OFF)
set(IIRC_DEP_INCLUDE "" CACHE PATH "Additional include path (single path only)")
set(IIRC_DEP_LIBRARY "" CACHE PATH "Additional library path (single path only)")
if (NOT IIRC_DEP_INCLUDE STREQUAL "")
include_directories(${IIRC_DEP_INCLUDE})
message("Using additional include path: ${IIRC_DEP_INCLUDE}")
include_directories(${IIRC_DEP_INCLUDE})
message("Using additional include path: ${IIRC_DEP_INCLUDE}")
endif()
if (NOT IIRC_DEP_LIBRARY STREQUAL "")
link_directories(${IIRC_DEP_LIBRARY})
message("Using additional library path: ${IIRC_DEP_LIBRARY}")
link_directories(${IIRC_DEP_LIBRARY})
message("Using additional library path: ${IIRC_DEP_LIBRARY}")
endif()
# For GCC and Clang, basically.
if (UNIX)
# set(CMAKE_CXX_FLAGS "-Wall -Werror")
set(CMAKE_CXX_FLAGS "-pthread")
set(CMAKE_CXX_FLAGS "-pthread")
endif()
#
@ -85,5 +85,5 @@ add_subdirectory(Widgets)
add_subdirectory(IdealIRC) # This one builds the binary.
if (BUILD_DEV_STUFF)
add_subdirectory(IRCClientSandbox)
add_subdirectory(IRCClientSandbox)
endif()

@ -10,48 +10,48 @@
CommandData::CommandData(const QString& line, PredicateList argp, bool repeatLastPredicate)
{
for (int i = 0, ppos = 0; i < line.length(); ++i) {
QChar c = line[i];
if (c == ' ')
continue;
else {
if (ppos >= static_cast<int>(argp.size()) && m_l.at(ppos-1)) {
if (repeatLastPredicate)
ppos = argp.size()-1;
else
break;
}
for (int i = 0, ppos = 0; i < line.length(); ++i) {
QChar c = line[i];
if (c == ' ')
continue;
else {
if (ppos >= static_cast<int>(argp.size()) && m_l.at(ppos-1)) {
if (repeatLastPredicate)
ppos = argp.size()-1;
else
break;
}
const auto& result = argp[ppos](i, line);
m_l.push_back(result);
++ppos;
if (!result)
--i;
}
}
const auto& result = argp[ppos](i, line);
m_l.push_back(result);
++ppos;
if (!result)
--i;
}
}
while (m_l.size() < argp.size())
m_l.push_back(std::nullopt);
while (m_l.size() < argp.size())
m_l.push_back(std::nullopt);
}
int CommandData::size() const
{
return m_l.size();
return m_l.size();
}
const std::optional<std::string>& CommandData::operator[](int idx)
{
return m_l.at(idx);
return m_l.at(idx);
}
std::string CommandData::joinString(char sep) const
{
std::string ret;
for (const auto& item : m_l) {
if (!ret.empty())
ret += sep;
if (item.has_value())
ret += *item;
}
return ret;
std::string ret;
for (const auto& item : m_l) {
if (!ret.empty())
ret += sep;
if (item.has_value())
ret += *item;
}
return ret;
}

@ -15,22 +15,22 @@
#include <optional>
using PredicateList = std::vector< std::function<
std::optional<std::string>
(int& idx, const QString& line)
>>;
std::optional<std::string>
(int& idx, const QString& line)
>>;
class ICommand;
class CommandData
{
public:
CommandData(const QString& line, PredicateList argp, bool repeatLastPredicate = false);
int size() const;
const std::optional<std::string>& operator[](int idx);
std::string joinString(char sep) const;
CommandData(const QString& line, PredicateList argp, bool repeatLastPredicate = false);
int size() const;
const std::optional<std::string>& operator[](int idx);
std::string joinString(char sep) const;
private:
std::vector<std::optional<std::string>> m_l;
std::vector<std::optional<std::string>> m_l;
};
#endif // COMMANDDATA_H

@ -10,15 +10,15 @@
namespace Command::Internal {
constexpr auto* ACTION = "ACTION";
constexpr auto* CTCP = "CTCP";
constexpr auto* CTCPREPLY = "CTCPREPLY";
constexpr auto* ME = "ME";
constexpr auto* ECHO = "ECHO";
constexpr auto* QUERY = "QUERY";
constexpr auto* MUTERESP = "MUTERESP";
constexpr auto* UNMUTERESP = "UNMUTERESP";
constexpr auto* CLEAR = "CLEAR";
constexpr auto* MSG = "MSG";
constexpr auto* CTCP = "CTCP";
constexpr auto* CTCPREPLY = "CTCPREPLY";
constexpr auto* ME = "ME";
constexpr auto* ECHO = "ECHO";
constexpr auto* QUERY = "QUERY";
constexpr auto* MUTERESP = "MUTERESP";
constexpr auto* UNMUTERESP = "UNMUTERESP";
constexpr auto* CLEAR = "CLEAR";
constexpr auto* MSG = "MSG";
constexpr auto* QUOTE = "QUOTE";
constexpr auto* RAW = "RAW";
constexpr auto* SERVER = "SERVER";

@ -11,16 +11,16 @@ using namespace Command::IRC;
void ICommandPriv::cmd_kick(const std::string& channel, const std::string& nickname, const std::string& reason)
{
auto& mdi = MdiManager::instance();
std::string chan = channel;
auto& mdi = MdiManager::instance();
std::string chan = channel;
if (chan.empty()) {
if (mdi.currentWindow()->getType() != IWin::Type::Channel) {
mdi.currentWindow()->print(PrintType::ProgramInfo, "Not in a channel window");
return;
}
chan = mdi.currentWindow()->getButtonText().toStdString();
}
if (chan.empty()) {
if (mdi.currentWindow()->getType() != IWin::Type::Channel) {
mdi.currentWindow()->print(PrintType::ProgramInfo, "Not in a channel window");
return;
}
chan = mdi.currentWindow()->getButtonText().toStdString();
}
connection.command(KICK, { channel, nickname }, reason);
connection.command(KICK, { channel, nickname }, reason);
}

@ -11,21 +11,21 @@ using namespace Command::IRC;
void ICommandPriv::cmd_notice(const std::string& target, const std::string& message)
{
auto& mdi = MdiManager::instance();
auto& mdi = MdiManager::instance();
connection.command(NOTICE, { target }, message);
connection.command(NOTICE, { target }, message);
IWin* winTarget = mdi.findWindow(&status, target.c_str());
IWin* active = status.getActiveWindow();
IWin* winTarget = mdi.findWindow(&status, target.c_str());
IWin* active = status.getActiveWindow();
if (!target.empty() && !message.empty()) {
if (winTarget && winTarget != active)
winTarget->print(PrintType::OwnText, QStringLiteral("->%1<- %2")
.arg(target.c_str())
.arg(message.c_str()));
if (active && active != winTarget)
active->print(PrintType::OwnText, QStringLiteral("->%1<- %2")
.arg(target.c_str())
.arg(message.c_str()));
}
if (!target.empty() && !message.empty()) {
if (winTarget && winTarget != active)
winTarget->print(PrintType::OwnText, QStringLiteral("->%1<- %2")
.arg(target.c_str())
.arg(message.c_str()));
if (active && active != winTarget)
active->print(PrintType::OwnText, QStringLiteral("->%1<- %2")
.arg(target.c_str())
.arg(message.c_str()));
}
}

@ -11,22 +11,22 @@ using namespace Command::IRC;
void ICommandPriv::cmd_privmsg(const std::string& target, const std::string& message)
{
auto& mdi = MdiManager::instance();
auto& mdi = MdiManager::instance();
connection.command(PRIVMSG, { target }, message);
connection.command(PRIVMSG, { target }, message);
if (!target.empty() && !message.empty()) {
IWin* win = mdi.findWindow(&status, target.c_str());
if (win) {
const auto* myNick = connection.getNickname().c_str();
win->print(PrintType::OwnText, QStringLiteral("<%1> %2")
.arg(myNick)
.arg(message.c_str()));
}
else
status.print(PrintType::OwnText, QStringLiteral(">%1< %2")
.arg(target.c_str())
.arg(message.c_str()));
}
if (!target.empty() && !message.empty()) {
IWin* win = mdi.findWindow(&status, target.c_str());
if (win) {
const auto* myNick = connection.getNickname().c_str();
win->print(PrintType::OwnText, QStringLiteral("<%1> %2")
.arg(myNick)
.arg(message.c_str()));
}
else
status.print(PrintType::OwnText, QStringLiteral(">%1< %2")
.arg(target.c_str())
.arg(message.c_str()));
}
}

File diff suppressed because it is too large Load Diff

@ -20,34 +20,34 @@ struct ICommandPriv;
class ICommand : public QObject
{
Q_OBJECT
Q_OBJECT
friend struct ICommandPriv;
friend struct ICommandPriv;
public:
explicit ICommand(IRC& connection_);
~ICommand();
explicit ICommand(IRC& connection_);
~ICommand();
void parse(QString text);
IRC& getConnection();
void parse(QString text);
IRC& getConnection();
private:
bool tryParseIRC(QString cmdLine);
bool tryParseInternal(QString cmdLine);
void print_notEnoughParameters(const QString& command);
void print_invalidCommandSwitch(const QString& command);
void print_invalidWindowTypeForCommand(const QString& command);
void print_notConnectedToServer(const QString& command);
/* Predicates for parsing commands */
static std::optional<std::string> prd_AnyWord(int& idx, const QString& line);
static std::optional<std::string> prd_AnyWordToUpper(int& idx, const QString& line);
static std::optional<std::string> prd_OptionalChannel(int& idx, const QString& line);
static std::optional<std::string> prd_NotSwitch(int& idx, const QString& line);
static std::optional<std::string> prd_Switch(int& idx, const QString& line);
static std::optional<std::string> prd_Message(int& idx, const QString& line);
std::unique_ptr<ICommandPriv> mp;
bool tryParseIRC(QString cmdLine);
bool tryParseInternal(QString cmdLine);
void print_notEnoughParameters(const QString& command);
void print_invalidCommandSwitch(const QString& command);
void print_invalidWindowTypeForCommand(const QString& command);
void print_notConnectedToServer(const QString& command);
/* Predicates for parsing commands */
static std::optional<std::string> prd_AnyWord(int& idx, const QString& line);
static std::optional<std::string> prd_AnyWordToUpper(int& idx, const QString& line);
static std::optional<std::string> prd_OptionalChannel(int& idx, const QString& line);
static std::optional<std::string> prd_NotSwitch(int& idx, const QString& line);
static std::optional<std::string> prd_Switch(int& idx, const QString& line);
static std::optional<std::string> prd_Message(int& idx, const QString& line);
std::unique_ptr<ICommandPriv> mp;
};
#endif // ICOMMAND_H

@ -8,8 +8,8 @@
#include "ICommandPriv.h"
ICommandPriv::ICommandPriv(ICommand& super_, IRC& connection_, IWinStatus& status_)
: super(super_)
, connection(connection_)
, status(status_)
: super(super_)
, connection(connection_)
, status(status_)
{}

@ -19,19 +19,19 @@ constexpr char CTCPflag { 0x01 };
struct ICommandPriv
{
ICommandPriv(ICommand& super_, IRC& connection_, IWinStatus& status_);
ICommandPriv(ICommand& super_, IRC& connection_, IWinStatus& status_);
ICommand& super;
IRC& connection;
IWinStatus& status;
void cmd_kick(const std::string& channel, const std::string& nickname, const std::string& reason);
void cmd_privmsg(const std::string& target, const std::string& message);
void cmd_notice(const std::string& target, const std::string& message);
void cmd_kick(const std::string& channel, const std::string& nickname, const std::string& reason);
void cmd_privmsg(const std::string& target, const std::string& message);
void cmd_notice(const std::string& target, const std::string& message);
void cmd_ctcp(const std::string& target, const std::string& command, std::string message);
void cmd_ctcp(const std::string& target, const std::string& command, std::string message);
void cmd_ctcpreply(const std::string& target, const std::string& command, const std::string& message);
void cmd_me(const std::string& target, const std::string& message);
void cmd_me(const std::string& target, const std::string& message);
void cmd_echo(const std::string& arg1, const std::string& arg2, const std::string& text);
void cmd_server(bool ssl, bool newstatus, bool activate, const std::string& host, const std::string& port);
};

@ -19,19 +19,19 @@ void ICommandPriv::cmd_me(const std::string& target, const std::string& message)
IWin* cw = status.getActiveWindow();
if (cw->getType() == IWin::Type::Channel) {
auto channel = connection.getChannel( cw->getButtonText().toStdString() );
auto& member = channel->getMember(mynick)->get();
const std::string& mymodes = member.modes();
auto channel = connection.getChannel( cw->getButtonText().toStdString() );
auto& member = channel->getMember(mynick)->get();
const std::string& mymodes = member.modes();
if (!mymodes.empty()) {
std::string pf = connection.toMemberPrefix(mymodes);
if (!pf.empty())
mynick.insert(mynick.begin(), pf[0]);
}
}
if (!mymodes.empty()) {
std::string pf = connection.toMemberPrefix(mymodes);
if (!pf.empty())
mynick.insert(mynick.begin(), pf[0]);
}
}
if (!target.empty() && !message.empty())
cw->print(PrintType::Action, QStringLiteral("%1 %2")
.arg(mynick.c_str())
.arg(message.c_str()));
if (!target.empty() && !message.empty())
cw->print(PrintType::Action, QStringLiteral("%1 %2")
.arg(mynick.c_str())
.arg(message.c_str()));
}

@ -21,360 +21,360 @@ constexpr auto* Text_AddPrefix_Short = "Add";
}
ColorConfig::ColorConfig(QWidget* parent)
: QWidget(parent)
, colorDlg(this)
: QWidget(parent)
, colorDlg(this)
{
addPrefixContainer = new QWidget(this);
addPrefixLayout = new QHBoxLayout;
btnAddPrefix = new QToolButton;
btnAddPrefix->setText(Text_AddPrefix_Full);
edAddPrefix = new QLineEdit;
edAddPrefix->setMaxLength(1);
edAddPrefix->setAlignment(Qt::AlignmentFlag::AlignCenter);
edAddPrefix->hide();
addPrefixLayout->addWidget(edAddPrefix);
addPrefixLayout->addWidget(btnAddPrefix);
addPrefixContainer->setLayout(addPrefixLayout);
connect(btnAddPrefix, &QToolButton::clicked, this, &ColorConfig::addPrefixClicked);
connect(edAddPrefix, &QLineEdit::returnPressed, this, &ColorConfig::addPrefixClicked);
ConfigMgr& conf = ConfigMgr::instance();
palette = conf.color();
prefixColor = conf.prefixColor();
textTypeMap = {
// Config key, Descriptive text
{ "Action", "Action/role-play message" },
{ "CTCP", "CTCP message" },
{ "Highlight", "Highlighted message" },
{ "Invite", "Invite message" },
{ "Join", "Join message" },
{ "Kick", "Kick message" },
{ "Mode", "Mode message" },
{ "Nick", "Nick message" },
{ "Normal", "Normal message" },
{ "Notice", "Notice message" },
{ "OwnText", "Own message" },
{ "Part", "Part message" },
{ "ProgramInfo", "Program info" },
{ "Quit", "Quit message" },
{ "ServerInfo", "General server info" },
{ "Topic", "Topic message" },
{ "Wallops", "Wallops message" },
{ "Links", "Links / anchors" }
};
colorTypeMap = {
{ "TextviewBackground", "Text view background" },
{ "InputBackground", "Text input background" },
{ "InputForeground", "Text input" },
{ "ListboxBackground", "Member list background" },
{ "ListboxForeground", "Member list default" }
};
connect(&colorDlg, &QColorDialog::currentColorChanged,
this, &ColorConfig::colorSelected);
connect(&colorDlg, &QColorDialog::rejected, [this](){
if (colorDlgItem.left(8) == "lbprefix")
setPrefixColor(colorDlgItem[9], originalColor);
else
palette.insert(colorDlgItem, originalColor);
repaint();
});
addPrefixContainer = new QWidget(this);
addPrefixLayout = new QHBoxLayout;
btnAddPrefix = new QToolButton;
btnAddPrefix->setText(Text_AddPrefix_Full);
edAddPrefix = new QLineEdit;
edAddPrefix->setMaxLength(1);
edAddPrefix->setAlignment(Qt::AlignmentFlag::AlignCenter);
edAddPrefix->hide();
addPrefixLayout->addWidget(edAddPrefix);
addPrefixLayout->addWidget(btnAddPrefix);
addPrefixContainer->setLayout(addPrefixLayout);
connect(btnAddPrefix, &QToolButton::clicked, this, &ColorConfig::addPrefixClicked);
connect(edAddPrefix, &QLineEdit::returnPressed, this, &ColorConfig::addPrefixClicked);
ConfigMgr& conf = ConfigMgr::instance();
palette = conf.color();
prefixColor = conf.prefixColor();
textTypeMap = {
// Config key, Descriptive text
{ "Action", "Action/role-play message" },
{ "CTCP", "CTCP message" },
{ "Highlight", "Highlighted message" },
{ "Invite", "Invite message" },
{ "Join", "Join message" },
{ "Kick", "Kick message" },
{ "Mode", "Mode message" },
{ "Nick", "Nick message" },
{ "Normal", "Normal message" },
{ "Notice", "Notice message" },
{ "OwnText", "Own message" },
{ "Part", "Part message" },
{ "ProgramInfo", "Program info" },
{ "Quit", "Quit message" },
{ "ServerInfo", "General server info" },
{ "Topic", "Topic message" },
{ "Wallops", "Wallops message" },
{ "Links", "Links / anchors" }
};
colorTypeMap = {
{ "TextviewBackground", "Text view background" },
{ "InputBackground", "Text input background" },
{ "InputForeground", "Text input" },
{ "ListboxBackground", "Member list background" },
{ "ListboxForeground", "Member list default" }
};
connect(&colorDlg, &QColorDialog::currentColorChanged,
this, &ColorConfig::colorSelected);
connect(&colorDlg, &QColorDialog::rejected, [this](){
if (colorDlgItem.left(8) == "lbprefix")
setPrefixColor(colorDlgItem[9], originalColor);
else
palette.insert(colorDlgItem, originalColor);
repaint();
});
}
void ColorConfig::save()
{
ConfigMgr& conf = ConfigMgr::instance();
conf.setColorPalette(palette);
conf.setPrefixColorPalette(prefixColor);
m_isChanged = false;
ConfigMgr& conf = ConfigMgr::instance();
conf.setColorPalette(palette);
conf.setPrefixColorPalette(prefixColor);
m_isChanged = false;
}
bool ColorConfig::isChanged() const
{
return m_isChanged;
return m_isChanged;
}
void ColorConfig::reset()
{
ConfigMgr& conf = ConfigMgr::instance();
palette = conf.color();
prefixColor = conf.prefixColor();
m_isChanged = false;
repaint();
ConfigMgr& conf = ConfigMgr::instance();
palette = conf.color();
prefixColor = conf.prefixColor();
m_isChanged = false;
repaint();
}
void ColorConfig::paintEvent(QPaintEvent*)
{
QPainter paint(this);
int w = width();
int h = height();
QPainter paint(this);
int w = width();
int h = height();
QColor frame(palette.value("TextviewBackground"));
frame.setRed(frame.red() ^ 255);
frame.setGreen(frame.green() ^ 255);
frame.setBlue(frame.blue() ^ 255);
QColor frame(palette.value("TextviewBackground"));
frame.setRed(frame.red() ^ 255);
frame.setGreen(frame.green() ^ 255);
frame.setBlue(frame.blue() ^ 255);
paint.fillRect(0, 0, w, h, frame);
w -= 2;
h -= 2;
paint.fillRect(0, 0, w, h, frame);
w -= 2;
h -= 2;
const int listboxW = 150;
const int inputH = 24;
const int listboxW = 150;
const int inputH = 24;
/* Text-view background */
textViewBB = { 1, 1, w - listboxW, h - inputH };
paint.fillRect(textViewBB, QColor(palette.value("TextviewBackground")));
/* Text-view background */
textViewBB = { 1, 1, w - listboxW, h - inputH };
paint.fillRect(textViewBB, QColor(palette.value("TextviewBackground")));
/* Member list background */
listboxBB = { w - listboxW + 2, 1, listboxW - 1, h - inputH };
paint.fillRect(listboxBB, QColor(palette.value("ListboxBackground"))); // Listbox
/* Member list background */
listboxBB = { w - listboxW + 2, 1, listboxW - 1, h - inputH };
paint.fillRect(listboxBB, QColor(palette.value("ListboxBackground"))); // Listbox
/* Input background */
inputBB = { 1, h - inputH + 2, w, inputH - 1 };
paint.fillRect(inputBB, QColor(palette.value("InputBackground"))); // Input
/* Input background */
inputBB = { 1, h - inputH + 2, w, inputH - 1 };
paint.fillRect(inputBB, QColor(palette.value("InputBackground"))); // Input
ConfigMgr& conf = ConfigMgr::instance();
QFont font(conf.common("Font"));
paint.setFont(font);
ConfigMgr& conf = ConfigMgr::instance();
QFont font(conf.common("Font"));
paint.setFont(font);
createMessageBoxTexts(paint);
createMemberListTexts(paint, listboxW);
createInputBoxTexts(paint, inputH);
createMessageBoxTexts(paint);
createMemberListTexts(paint, listboxW);
createInputBoxTexts(paint, inputH);
}
void ColorConfig::mouseReleaseEvent(QMouseEvent* evt)
{
const int X = evt->x();
const int Y = evt->y();
/* Check click for text item types */
{
QHashIterator<QString,QRect> it(textTypeBB);
while (it.hasNext()) {
it.next();
const QString& key = it.key();
const QRect& val = it.value();
if (val.contains(X, Y)) {
chooseColorFor(key);
return;
}
}
}
/* Check click for listbox items */
{
QHashIterator<QString,QRect> it(listboxItemBB);
while (it.hasNext()) {
it.next();
const QString& key = it.key();
const QRect& val = it.value();
if (val.contains(X, Y)) {
chooseColorFor(key);
return;
}
}
}
/* Check click for delete listbox item */
{
QHashIterator<QChar,QRect> it(prefixDeleteBB);
while (it.hasNext()) {
it.next();
const QChar& key = it.key();
const QRect& val = it.value();
if (val.contains(X, Y)) {
askDeletePrefix(key);
return;
}
}
}
if (inputTextBB.contains(X, Y))
chooseColorFor("InputForeground");
else if (textViewBB.contains(X, Y))
chooseColorFor("TextviewBackground");
else if (listboxBB.contains(X, Y))
chooseColorFor("ListboxBackground");
else if (inputBB.contains(X, Y))
chooseColorFor("InputBackground");
const int X = evt->x();
const int Y = evt->y();
/* Check click for text item types */
{
QHashIterator<QString,QRect> it(textTypeBB);
while (it.hasNext()) {
it.next();
const QString& key = it.key();
const QRect& val = it.value();
if (val.contains(X, Y)) {
chooseColorFor(key);
return;
}
}
}
/* Check click for listbox items */
{
QHashIterator<QString,QRect> it(listboxItemBB);
while (it.hasNext()) {
it.next();
const QString& key = it.key();
const QRect& val = it.value();
if (val.contains(X, Y)) {
chooseColorFor(key);
return;
}
}
}
/* Check click for delete listbox item */
{
QHashIterator<QChar,QRect> it(prefixDeleteBB);
while (it.hasNext()) {
it.next();
const QChar& key = it.key();
const QRect& val = it.value();
if (val.contains(X, Y)) {
askDeletePrefix(key);
return;
}
}
}
if (inputTextBB.contains(X, Y))
chooseColorFor("InputForeground");
else if (textViewBB.contains(X, Y))
chooseColorFor("TextviewBackground");
else if (listboxBB.contains(X, Y))
chooseColorFor("ListboxBackground");
else if (inputBB.contains(X, Y))
chooseColorFor("InputBackground");
}
void ColorConfig::createMessageBoxTexts(QPainter& paint)
{
const int itemIncr = 19;
int itemTop = itemIncr;
QPen originalPen = paint.pen();
QFontMetrics fm(paint.fontMetrics());
const int itemIncr = 19;
int itemTop = itemIncr;
QPen originalPen = paint.pen();
QFontMetrics fm(paint.fontMetrics());
textTypeBB.clear();
QHashIterator<QString,QString> it(textTypeMap);
QStringList sorted;
textTypeBB.clear();
QHashIterator<QString,QString> it(textTypeMap);
QStringList sorted;
while (it.hasNext())
sorted << it.next().key();
while (it.hasNext())
sorted << it.next().key();
std::sort(sorted.begin(), sorted.end(),
[this](const QString& left, const QString& right) {
QString leftText = descriptiveColorText(left);
QString rightText = descriptiveColorText(right);
return leftText < rightText;
});
std::sort(sorted.begin(), sorted.end(),
[this](const QString& left, const QString& right) {
QString leftText = descriptiveColorText(left);
QString rightText = descriptiveColorText(right);
return leftText < rightText;
});
for (const QString& key : sorted) {
const QString val = descriptiveColorText(key);
for (const QString& key : sorted) {
const QString val = descriptiveColorText(key);
paint.setPen(QColor(palette.value(key)));
paint.drawText(4, itemTop, val);
paint.setPen(QColor(palette.value(key)));
paint.drawText(4, itemTop, val);
QRect itemRect(4, itemTop - fm.ascent(), fm.width(val), fm.height());
textTypeBB.insert(key, itemRect);
QRect itemRect(4, itemTop - fm.ascent(), fm.width(val), fm.height());
textTypeBB.insert(key, itemRect);
itemTop += itemIncr;
}
itemTop += itemIncr;
}
paint.setPen(originalPen);
paint.setPen(originalPen);
}
void ColorConfig::createMemberListTexts(QPainter& paint, int listboxW)
{
const int x = width() - listboxW + 4;
const int itemIncr = 20;
int itemTop = itemIncr;
QPen originalPen = paint.pen();
QFontMetrics fm(paint.fontMetrics());
listboxItemBB.clear();
prefixDeleteBB.clear();
paint.setPen(QColor(palette.value("ListboxForeground")));
paint.drawText(x, itemTop, "Default text");
listboxItemBB.insert("ListboxForeground", QRect(x, itemTop - fm.ascent(), fm.width("Default text"), fm.height()));
itemTop += itemIncr;
constexpr auto* DeleteLabel = "[X]";
for (const auto& item : prefixColor) {
QString itemText = QStringLiteral("%1member").arg(item.first);
int itemTextWidth = fm.width(itemText);
paint.setPen(item.second);
paint.drawText(x, itemTop, itemText);
paint.drawText(x + itemTextWidth + 10, itemTop, DeleteLabel);
listboxItemBB.insert(QStringLiteral("lbprefix %1").arg(item.first), QRect(x, itemTop - fm.ascent(), itemTextWidth, fm.height()));
prefixDeleteBB.insert(item.first, QRect(x + itemTextWidth + 10, itemTop - fm.ascent(), fm.width(DeleteLabel), fm.height()));
itemTop += itemIncr;
}
paint.setPen(originalPen);
addPrefixContainer->move(x, itemTop);
const int x = width() - listboxW + 4;
const int itemIncr = 20;
int itemTop = itemIncr;
QPen originalPen = paint.pen();
QFontMetrics fm(paint.fontMetrics());
listboxItemBB.clear();
prefixDeleteBB.clear();
paint.setPen(QColor(palette.value("ListboxForeground")));
paint.drawText(x, itemTop, "Default text");
listboxItemBB.insert("ListboxForeground", QRect(x, itemTop - fm.ascent(), fm.width("Default text"), fm.height()));
itemTop += itemIncr;
constexpr auto* DeleteLabel = "[X]";
for (const auto& item : prefixColor) {
QString itemText = QStringLiteral("%1member").arg(item.first);
int itemTextWidth = fm.width(itemText);
paint.setPen(item.second);
paint.drawText(x, itemTop, itemText);
paint.drawText(x + itemTextWidth + 10, itemTop, DeleteLabel);
listboxItemBB.insert(QStringLiteral("lbprefix %1").arg(item.first), QRect(x, itemTop - fm.ascent(), itemTextWidth, fm.height()));
prefixDeleteBB.insert(item.first, QRect(x + itemTextWidth + 10, itemTop - fm.ascent(), fm.width(DeleteLabel), fm.height()));
itemTop += itemIncr;
}
paint.setPen(originalPen);
addPrefixContainer->move(x, itemTop);
}
void ColorConfig::createInputBoxTexts(QPainter& paint, int inputH)
{
QFontMetrics fm(paint.fontMetrics());
const int fh = fm.height();
const int y = height() - inputH + fh;
QPen originalPen = paint.pen();
QFontMetrics fm(paint.fontMetrics());
const int fh = fm.height();
const int y = height() - inputH + fh;
QPen originalPen = paint.pen();
paint.setPen(QColor(palette.value("InputForeground")));
paint.drawText(4, y, "Input box text");
inputTextBB = {4, y - fm.ascent(), fm.width("Input box text"), fm.height()};
paint.setPen(QColor(palette.value("InputForeground")));
paint.drawText(4, y, "Input box text");
inputTextBB = {4, y - fm.ascent(), fm.width("Input box text"), fm.height()};
paint.setPen(originalPen);
paint.setPen(originalPen);
}
QString ColorConfig::descriptiveColorText(const QString& key) const
{
if (key.left(8) == "lbprefix")
return QStringLiteral("Prefix '%1'").arg(key[9]);
if (key.left(8) == "lbprefix")
return QStringLiteral("Prefix '%1'").arg(key[9]);
QString text = textTypeMap.value(key);
if (text.isEmpty())
text = colorTypeMap.value(key);
return text;
QString text = textTypeMap.value(key);
if (text.isEmpty())
text = colorTypeMap.value(key);
return text;
}
void ColorConfig::chooseColorFor(const QString& item)
{
colorDlgItem = item;
if (colorDlgItem.left(8) == "lbprefix")
colorDlg.setCurrentColor(*getPrefixColor(colorDlgItem[9]));
else
colorDlg.setCurrentColor(QColor(palette.value(item)));
originalColor = colorDlg.currentColor().name();
colorDlg.setWindowTitle(QStringLiteral("Choose color for: %1").arg(descriptiveColorText(item)));
if (!colorDlg.isVisible())
colorDlg.show();
colorDlgItem = item;
if (colorDlgItem.left(8) == "lbprefix")
colorDlg.setCurrentColor(*getPrefixColor(colorDlgItem[9]));
else
colorDlg.setCurrentColor(QColor(palette.value(item)));
originalColor = colorDlg.currentColor().name();
colorDlg.setWindowTitle(QStringLiteral("Choose color for: %1").arg(descriptiveColorText(item)));
if (!colorDlg.isVisible())
colorDlg.show();
}
void ColorConfig::colorSelected(const QColor& color)
{
if (colorDlgItem.left(8) == "lbprefix")
setPrefixColor(colorDlgItem[9], color);
else
palette.insert(colorDlgItem, color.name());
m_isChanged = true;
repaint();
if (colorDlgItem.left(8) == "lbprefix")
setPrefixColor(colorDlgItem[9], color);
else
palette.insert(colorDlgItem, color.name());
m_isChanged = true;
repaint();
}
std::optional<QColor> ColorConfig::getPrefixColor(QChar prefix)
{
for (const auto& item : prefixColor)
if (item.first == prefix)
return item.second;
return std::nullopt;
for (const auto& item : prefixColor)
if (item.first == prefix)
return item.second;
return std::nullopt;
}
void ColorConfig::setPrefixColor(QChar prefix, const QColor& color)
{
for (auto& item : prefixColor)
if (item.first == prefix)
item.second = color;
for (auto& item : prefixColor)
if (item.first == prefix)
item.second = color;
}
void ColorConfig::addPrefixClicked()
{
if (edAddPrefix->isVisible()) {
if (!edAddPrefix->text().isEmpty()) {
const QChar prefix = edAddPrefix->text()[0];
if (getPrefixColor(prefix)) {
QMessageBox::information(this, tr("Prefix exists"), tr("The given prefix '%1' is already defined.").arg(prefix));
return;
}
if (!prefix.isSpace() && !prefix.isLetter() && !prefix.isNumber()) {
prefixColor.push_back(std::make_pair(prefix, palette.value("ListboxForeground")));
m_isChanged = true;
repaint();
}
}
edAddPrefix->clear();
edAddPrefix->hide();
btnAddPrefix->setText(Text_AddPrefix_Full);
}
else {
edAddPrefix->show();
btnAddPrefix->setText(Text_AddPrefix_Short);
}
if (edAddPrefix->isVisible()) {
if (!edAddPrefix->text().isEmpty()) {
const QChar prefix = edAddPrefix->text()[0];
if (getPrefixColor(prefix)) {
QMessageBox::information(this, tr("Prefix exists"), tr("The given prefix '%1' is already defined.").arg(prefix));
return;
}
if (!prefix.isSpace() && !prefix.isLetter() && !prefix.isNumber()) {
prefixColor.push_back(std::make_pair(prefix, palette.value("ListboxForeground")));
m_isChanged = true;
repaint();
}
}
edAddPrefix->clear();
edAddPrefix->hide();
btnAddPrefix->setText(Text_AddPrefix_Full);
}
else {
edAddPrefix->show();
btnAddPrefix->setText(Text_AddPrefix_Short);
}
}
void ColorConfig::askDeletePrefix(QChar prefix)
{
const QString title = tr("Delete prefix");
const QString question = tr("Are you sure you want to delete the prefix '%1'?").arg(prefix);
if (QMessageBox::question(this, title, question) == QMessageBox::No)
return;
for (int i = 0; i < prefixColor.count(); ++i) {
QChar p = prefixColor[i].first;
if (p == prefix) {
prefixColor.removeAt(i);
break;
}
}
repaint();
const QString title = tr("Delete prefix");
const QString question = tr("Are you sure you want to delete the prefix '%1'?").arg(prefix);
if (QMessageBox::question(this, title, question) == QMessageBox::No)
return;
for (int i = 0; i < prefixColor.count(); ++i) {
QChar p = prefixColor[i].first;
if (p == prefix) {
prefixColor.removeAt(i);
break;
}
}
repaint();
}

@ -21,51 +21,51 @@
class ColorConfig : public QWidget
{
Q_OBJECT
Q_OBJECT
public:
explicit ColorConfig(QWidget* parent = nullptr);
void save();
bool isChanged() const;
void reset();
explicit ColorConfig(QWidget* parent = nullptr);
void save();
bool isChanged() const;
void reset();
private:
void paintEvent(QPaintEvent*);
void mouseReleaseEvent(QMouseEvent *evt);
void createMessageBoxTexts(QPainter& paint);
void createMemberListTexts(QPainter& paint, int listboxW);
void createInputBoxTexts(QPainter& paint, int inputH);
void recalculateBB();
QString descriptiveColorText(const QString& key) const;
void chooseColorFor(const QString& item);
void colorSelected(const QColor& color);
std::optional<QColor> getPrefixColor(QChar prefix);
void setPrefixColor(QChar prefix, const QColor& color);
void addPrefixClicked();
void askDeletePrefix(QChar prefix);
void paintEvent(QPaintEvent*);
void mouseReleaseEvent(QMouseEvent *evt);
void createMessageBoxTexts(QPainter& paint);
void createMemberListTexts(QPainter& paint, int listboxW);
void createInputBoxTexts(QPainter& paint, int inputH);
void recalculateBB();
QString descriptiveColorText(const QString& key) const;
void chooseColorFor(const QString& item);
void colorSelected(const QColor& color);
std::optional<QColor> getPrefixColor(QChar prefix);
void setPrefixColor(QChar prefix, const QColor& color);
void addPrefixClicked();
void askDeletePrefix(QChar prefix);
QWidget* addPrefixContainer;
QHBoxLayout* addPrefixLayout;
QToolButton* btnAddPrefix;
QLineEdit* edAddPrefix;
QHash<QString,QString> palette;
QWidget* addPrefixContainer;
QHBoxLayout* addPrefixLayout;
QToolButton* btnAddPrefix;
QLineEdit* edAddPrefix;
QHash<QString,QString> palette;
QHash<QString,QString> textTypeMap;
QHash<QString,QString> colorTypeMap;
QVector<std::pair<QChar,QColor>> prefixColor; // Use vector for displaying prefixes in insertion order.
QHash<QString,QString> textTypeMap;
QHash<QString,QString> colorTypeMap;
QVector<std::pair<QChar,QColor>> prefixColor; // Use vector for displaying prefixes in insertion order.
QHash<QString,QRect> textTypeBB;
QHash<QString,QRect> listboxItemBB;
QHash<QChar,QRect> prefixDeleteBB;
QRect textViewBB;
QRect inputBB;
QRect inputTextBB;
QRect listboxBB;
QHash<QString,QRect> textTypeBB;
QHash<QString,QRect> listboxItemBB;
QHash<QChar,QRect> prefixDeleteBB;
QRect textViewBB;
QRect inputBB;
QRect inputTextBB;
QRect listboxBB;
QColorDialog colorDlg;
QString colorDlgItem;
QString originalColor;
QColorDialog colorDlg;
QString colorDlgItem;
QString originalColor;
bool m_isChanged{ false };
bool m_isChanged{ false };
};
#endif // COLORCONFIG_H

@ -11,75 +11,75 @@
#include <QMessageBox>
IConfig::IConfig(QWidget *parent) :
QDialog(parent),
ui(new Ui::IConfig)
QDialog(parent),
ui(new Ui::IConfig)
{
ui->setupUi(this);
ui->setupUi(this);
layout = new QHBoxLayout(this);
servers = new IConfigServers;
options = new IConfigOptions;
logging = new IConfigLogging;
layout = new QHBoxLayout(this);
servers = new IConfigServers;
options = new IConfigOptions;
logging = new IConfigLogging;
layout->addWidget(servers);
layout->addWidget(options);
layout->addWidget(logging);
ui->frame->setLayout(layout);
layout->addWidget(servers);
layout->addWidget(options);
layout->addWidget(logging);
ui->frame->setLayout(layout);
connect(ui->toolServers, &QAbstractButton::released, [this](){ showSubDialog(SubDialogType::Servers); });
connect(ui->toolOptions, &QAbstractButton::released, [this](){ showSubDialog(SubDialogType::Options); });
connect(ui->toolLogging, &QAbstractButton::released, [this](){ showSubDialog(SubDialogType::Logging); });
connect(ui->toolServers, &QAbstractButton::released, [this](){ showSubDialog(SubDialogType::Servers); });
connect(ui->toolOptions, &QAbstractButton::released, [this](){ showSubDialog(SubDialogType::Options); });
connect(ui->toolLogging, &QAbstractButton::released, [this](){ showSubDialog(SubDialogType::Logging); });
showSubDialog(SubDialogType::Servers);
showSubDialog(SubDialogType::Servers);
}
IConfig::~IConfig()
{
delete ui;
delete ui;
}
void IConfig::showDisconnectButton()
{
ui->btnDisconnect->show();
ui->btnDisconnect->show();
}
void IConfig::hideDisconnectButton()
{
ui->btnDisconnect->hide();
ui->btnDisconnect->hide();
}
void IConfig::showSubDialog(IConfig::SubDialogType dlg)
{
servers->hide();
options->hide();
logging->hide();
ui->toolServers->setChecked(false);
ui->toolOptions->setChecked(false);
ui->toolLogging->setChecked(false);
switch (dlg) {
case SubDialogType::Servers:
servers->show();
ui->toolServers->setChecked(true);
break;
case SubDialogType::Options:
options->show();
ui->toolOptions->setChecked(true);
break;
case SubDialogType::Logging:
logging->show();
ui->toolLogging->setChecked(true);
break;
}
servers->hide();
options->hide();
logging->hide();
ui->toolServers->setChecked(false);
ui->toolOptions->setChecked(false);
ui->toolLogging->setChecked(false);
switch (dlg) {
case SubDialogType::Servers:
servers->show();
ui->toolServers->setChecked(true);
break;
case SubDialogType::Options:
options->show();
ui->toolOptions->setChecked(true);
break;
case SubDialogType::Logging:
logging->show();
ui->toolLogging->setChecked(true);
break;
}
}
void IConfig::saveAll()
{
servers->save();
options->save();
logging->save();
servers->save();
options->save();
logging->save();
ConfigMgr::instance().save();
ConfigMgr::instance().save();
}
bool IConfig::askForSave()
@ -87,70 +87,70 @@ bool IConfig::askForSave()
bool serversChanged = servers->isChanged();
bool optionsChanged = options->isChanged();
bool loggingChanged = logging->isChanged();
if (serversChanged || optionsChanged || loggingChanged) {
auto btn = QMessageBox::question(this, tr("Changes made"), tr("Do you want to save the changes?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
if (btn == QMessageBox::Yes) {
saveAll();
}
else if (btn == QMessageBox::No) {
servers->reset();
options->reset();
logging->reset();
}
else if (btn == QMessageBox::Cancel) {
return false;
}
}
return true;
if (serversChanged || optionsChanged || loggingChanged) {
auto btn = QMessageBox::question(this, tr("Changes made"), tr("Do you want to save the changes?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
if (btn == QMessageBox::Yes) {
saveAll();
}
else if (btn == QMessageBox::No) {
servers->reset();
options->reset();
logging->reset();
}
else if (btn == QMessageBox::Cancel) {
return false;
}
}
return true;
}
void IConfig::on_btnSave_clicked()
{
saveAll();
saveAll();
}
void IConfig::on_btnSaveConnect_clicked()
{
saveAll();
ConfigMgr& conf = ConfigMgr::instance();
if (conf.connection("Realname").isEmpty()) {
QMessageBox::information(this, tr("Missing field"), tr("Real name must be filled."));
return;
}
if (conf.connection("Username").isEmpty()) {
QMessageBox::information(this, tr("Missing field"), tr("Username/email must be filled."));
return;
}
if (conf.connection("Nickname").isEmpty()) {
QMessageBox::information(this, tr("Missing field"), tr("Nickname must be filled"));
return;
}
if (!servers->connectToNewStatus())
emit disconnectFromServer();
emit connectToServer(servers->connectToNewStatus());
servers->unsetConnectToNewStatus();
close();
saveAll();
ConfigMgr& conf = ConfigMgr::instance();
if (conf.connection("Realname").isEmpty()) {
QMessageBox::information(this, tr("Missing field"), tr("Real name must be filled."));
return;
}
if (conf.connection("Username").isEmpty()) {
QMessageBox::information(this, tr("Missing field"), tr("Username/email must be filled."));
return;
}
if (conf.connection("Nickname").isEmpty()) {
QMessageBox::information(this, tr("Missing field"), tr("Nickname must be filled"));
return;
}
if (!servers->connectToNewStatus())
emit disconnectFromServer();
emit connectToServer(servers->connectToNewStatus());
servers->unsetConnectToNewStatus();
close();
}
void IConfig::on_btnDisconnect_clicked()
{
ui->btnDisconnect->hide();
emit disconnectFromServer();
ui->btnDisconnect->hide();
emit disconnectFromServer();
}
void IConfig::on_btnClose_clicked()
{
servers->unsetConnectToNewStatus();
close();
servers->unsetConnectToNewStatus();
close();
}
void IConfig::closeEvent(QCloseEvent* evt)
{
if (askForSave())
evt->accept();
else
evt->ignore();
if (askForSave())
evt->accept();
else
evt->ignore();
}

@ -22,40 +22,40 @@ class IConfig;
class IConfig : public QDialog
{
Q_OBJECT
Q_OBJECT
public:
explicit IConfig(QWidget *parent = nullptr);
~IConfig();
void showDisconnectButton();
void hideDisconnectButton();
explicit IConfig(QWidget *parent = nullptr);
~IConfig();
void showDisconnectButton();
void hideDisconnectButton();
private slots:
void on_btnSave_clicked();
void on_btnSaveConnect_clicked();
void on_btnDisconnect_clicked();
void on_btnClose_clicked();
void on_btnSave_clicked();
void on_btnSaveConnect_clicked();
void on_btnDisconnect_clicked();
void on_btnClose_clicked();
private:
enum class SubDialogType {
Servers,
Options,
Logging
};
void closeEvent(QCloseEvent* evt);
void showSubDialog(SubDialogType dlg);
void saveAll();
bool askForSave();
Ui::IConfig* ui;
QHBoxLayout *layout;
IConfigServers* servers;
IConfigOptions* options;
IConfigLogging* logging;
enum class SubDialogType {
Servers,
Options,
Logging
};
void closeEvent(QCloseEvent* evt);
void showSubDialog(SubDialogType dlg);
void saveAll();
bool askForSave();
Ui::IConfig* ui;
QHBoxLayout *layout;
IConfigServers* servers;
IConfigOptions* options;
IConfigLogging* logging;
signals:
void connectToServer(bool newServer);
void disconnectFromServer();
void connectToServer(bool newServer);
void disconnectFromServer();
};
#endif // ICONFIG_H

@ -13,95 +13,95 @@
#include <QDebug>
IConfigLogging::IConfigLogging(QWidget *parent) :
QWidget(parent),
ui(new Ui::IConfigLogging)
QWidget(parent),
ui(new Ui::IConfigLogging)
{
ui->setupUi(this);
ui->splitter->setStretchFactor(0, 1);
ui->splitter->setStretchFactor(1, 4);
ui->setupUi(this);
ui->splitter->setStretchFactor(0, 1);
ui->splitter->setStretchFactor(1, 4);
ConfigMgr& conf = ConfigMgr::instance();
cf_Chnanels = conf.logging("Channels").toInt();
cf_Privates = conf.logging("Privates").toInt();
cf_Path = conf.logging("Path");
ConfigMgr& conf = ConfigMgr::instance();
cf_Chnanels = conf.logging("Channels").toInt();
cf_Privates = conf.logging("Privates").toInt();
cf_Path = conf.logging("Path");
ui->chkChannels->setChecked(cf_Chnanels);
ui->chkPrivates->setChecked(cf_Privates);
ui->edPath->setText(cf_Path);
ui->chkChannels->setChecked(cf_Chnanels);
ui->chkPrivates->setChecked(cf_Privates);
ui->edPath->setText(cf_Path);
}
IConfigLogging::~IConfigLogging()
{
delete ui;
delete ui;
}
bool IConfigLogging::isChanged() const
{
return cf_Chnanels != ui->chkChannels->isChecked()
|| cf_Privates != ui->chkPrivates->isChecked()
|| cf_Path != ui->edPath->text();
return cf_Chnanels != ui->chkChannels->isChecked()
|| cf_Privates != ui->chkPrivates->isChecked()
|| cf_Path != ui->edPath->text();
}
void IConfigLogging::save()
{
ConfigMgr& conf = ConfigMgr::instance();
conf.setLogging("Channels", QString::number(ui->chkChannels->isChecked()));
conf.setLogging("Privates", QString::number(ui->chkPrivates->isChecked()));
conf.setLogging("Path", ui->edPath->text());
ConfigMgr& conf = ConfigMgr::instance();
conf.setLogging("Channels", QString::number(ui->chkChannels->isChecked()));
conf.setLogging("Privates", QString::number(ui->chkPrivates->isChecked()));
conf.setLogging("Path", ui->edPath->text());
cf_Chnanels = conf.logging("Channels").toInt();
cf_Privates = conf.logging("Privates").toInt();
cf_Path = conf.logging("Path");
cf_Chnanels = conf.logging("Channels").toInt();
cf_Privates = conf.logging("Privates").toInt();
cf_Path = conf.logging("Path");
}
void IConfigLogging::reset()
{
ui->chkChannels->setChecked(cf_Chnanels);
ui->chkPrivates->setChecked(cf_Privates);
ui->edPath->setText(cf_Path);
ui->chkChannels->setChecked(cf_Chnanels);
ui->chkPrivates->setChecked(cf_Privates);
ui->edPath->setText(cf_Path);
}
void IConfigLogging::showEvent(QShowEvent* evt)
{
QWidget::showEvent(evt);
if (!ui->edPath->text().isEmpty())
loadFiles(ui->edPath->text());
QWidget::showEvent(evt);
if (!ui->edPath->text().isEmpty())
loadFiles(ui->edPath->text());
}
void IConfigLogging::on_btnBrowse_clicked()
{
QString dirstr = QFileDialog::getExistingDirectory(this, tr("Log directory"), LOCAL_PATH);
loadFiles(dirstr);
QString dirstr = QFileDialog::getExistingDirectory(this, tr("Log directory"), LOCAL_PATH);
loadFiles(dirstr);
}
void IConfigLogging::loadFiles(const QString& path)
{
ui->fileList->clear();
ui->edPath->setText(path);
if (path.isEmpty())
return;
QDir dir(path);
QStringList files = dir.entryList(QDir::Files, QDir::Name);
ui->fileList->addItems(files);
ui->fileList->clear();
ui->edPath->setText(path);
if (path.isEmpty())
return;
QDir dir(path);
QStringList files = dir.entryList(QDir::Files, QDir::Name);
ui->fileList->addItems(files);
}
void IConfigLogging::on_edPath_textChanged(const QString &arg1)
{
loadFiles(ui->edPath->text());
loadFiles(ui->edPath->text());
}
void IConfigLogging::on_fileList_itemClicked(QListWidgetItem *item)
{
if (!item)
return;
if (!item)
return;
QString filePath = ui->edPath->text() + "/" + item->text();
QFile f(filePath);
if (!f.open(QIODevice::ReadOnly)) {
qWarning() << "Unable to open log file" << filePath;
return;
}
QByteArray data = f.readAll();
f.close();
ui->logView->setPlainText(data);
QString filePath = ui->edPath->text() + "/" + item->text();
QFile f(filePath);
if (!f.open(QIODevice::ReadOnly)) {
qWarning() << "Unable to open log file" << filePath;
return;
}
QByteArray data = f.readAll();
f.close();
ui->logView->setPlainText(data);
}

@ -18,27 +18,27 @@ class IConfigLogging;
class IConfigLogging : public QWidget
{
Q_OBJECT
Q_OBJECT
public:
explicit IConfigLogging(QWidget *parent = nullptr);
~IConfigLogging();
bool isChanged() const;
void save();
void reset();
explicit IConfigLogging(QWidget *parent = nullptr);
~IConfigLogging();
bool isChanged() const;
void save();
void reset();
private slots:
void showEvent(QShowEvent* evt);
void on_btnBrowse_clicked();
void on_edPath_textChanged(const QString &arg1);
void on_fileList_itemClicked(QListWidgetItem *item);
void showEvent(QShowEvent* evt);
void on_btnBrowse_clicked();
void on_edPath_textChanged(const QString &arg1);
void on_fileList_itemClicked(QListWidgetItem *item);
private:
void loadFiles(const QString& path);
Ui::IConfigLogging *ui;
bool cf_Chnanels;
bool cf_Privates;
QString cf_Path;
void loadFiles(const QString& path);
Ui::IConfigLogging *ui;
bool cf_Chnanels;
bool cf_Privates;
QString cf_Path;
};
#endif // ICONFIGLOGGING_H

@ -13,46 +13,46 @@
#include <QFileDialog>
IConfigOptions::IConfigOptions(QWidget *parent) :
QWidget(parent),
ui(new Ui::IConfigOptions)
QWidget(parent),
ui(new Ui::IConfigOptions)
{
ui->setupUi(this);
layout = new QVBoxLayout(ui->tabColors);
colorCfg = new ColorConfig;
{
QLabel* bgcolorhintlabel = new QLabel("Click on a text type to change its color.\nClick anywhere on the backgrounds to change background color.");
QFont labelFont = font();
bgcolorhintlabel->setAlignment(Qt::AlignCenter);
labelFont.setPointSize(8);
bgcolorhintlabel->setFont(labelFont);
layout->addWidget(bgcolorhintlabel);
}
layout->addWidget(colorCfg);
colorCfg->setSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding);
ui->tabColors->setLayout(layout);
ui->setupUi(this);
layout = new QVBoxLayout(ui->tabColors);
colorCfg = new ColorConfig;
{
QLabel* bgcolorhintlabel = new QLabel("Click on a text type to change its color.\nClick anywhere on the backgrounds to change background color.");
QFont labelFont = font();
bgcolorhintlabel->setAlignment(Qt::AlignCenter);
labelFont.setPointSize(8);
bgcolorhintlabel->setFont(labelFont);
layout->addWidget(bgcolorhintlabel);
}
layout->addWidget(colorCfg);
colorCfg->setSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding);
ui->tabColors->setLayout(layout);
/* Background opacity is not yet determined */
ui->label_4->hide();
ui->hsImageOpacity->hide();
/* Background opacity is not yet determined */
ui->label_4->hide();
ui->hsImageOpacity->hide();
/* Background scaling is not yet determined */
ui->label_5->hide();
ui->edImageScaling->hide();
/* Background scaling is not yet determined */
ui->label_5->hide();
ui->edImageScaling->hide();
reload();
reset();
reload();
reset();
cf_Font = ui->edFont->currentFont();
cf_Font = ui->edFont->currentFont();
}
IConfigOptions::~IConfigOptions()
{
delete ui;
delete ui;
}
bool IConfigOptions::isChanged() const
{
bool changed = cf_ShowOptions != ui->chkShowOptions->isChecked()
bool changed = cf_ShowOptions != ui->chkShowOptions->isChecked()
|| cf_Reconnect != ui->chkReconnect->isChecked()
|| cf_ReconnectDelay != ui->edReconnectDelay->value()
|| cf_RejoinChannelsOnConnect != ui->chkRejoinConnect->isChecked()
@ -75,10 +75,10 @@ bool IConfigOptions::isChanged() const
|| cf_SSLExpired != ui->chkSSLExpired->isChecked()
|| cf_SSLCNMismatch != ui->chkSSLCNMismatch->isChecked();
if (changed)
return true;
else
return colorCfg->isChanged();
if (changed)
return true;
else
return colorCfg->isChanged();
}
void IConfigOptions::save()
@ -111,67 +111,67 @@ void IConfigOptions::save()
void IConfigOptions::reset()
{
ui->chkShowOptions->setChecked(cf_ShowOptions);
ui->chkReconnect->setChecked(cf_Reconnect);
ui->edReconnectDelay->setValue(cf_ReconnectDelay);
ui->chkRejoinConnect->setChecked(cf_RejoinChannelsOnConnect);
ui->chkWhoisActive->setChecked(cf_ShowWhoisActiveWindow);
ui->chkShowModeMsg->setChecked(cf_ShowModeInMessage);
ui->chkTrayNotify->setChecked(cf_TrayNotify);
ui->edTrayDelay->setValue(cf_TrayNotifyDelay);
ui->chkTimestamp->setChecked(cf_ShowTimestamp);
ui->edTimestamp->setText(cf_TimestampFormat);
ui->chkManualKeepalive->setChecked(cf_ManualKeepaliveEnabled);
ui->edManualKeepalive->setValue(cf_ManualKeepalive);
ui->edQuit->setText(cf_QuitMessage);
ui->edFont->setCurrentFont(cf_Font);
ui->edFontSize->setValue(cf_FontSize);
ui->chkEnableBgImage->setChecked(cf_BgImageEnabled);
ui->edImage->setText(cf_BgImagePath);
ui->hsImageOpacity->setValue(cf_BgImageOpacity);
ui->chkSSLSelfsigned->setChecked(cf_SSLSelfSigned);
ui->chkSSLExpired->setChecked(cf_SSLExpired);
ui->chkSSLCNMismatch->setChecked(cf_SSLCNMismatch);
ui->chkShowOptions->setChecked(cf_ShowOptions);
ui->chkReconnect->setChecked(cf_Reconnect);
ui->edReconnectDelay->setValue(cf_ReconnectDelay);
ui->chkRejoinConnect->setChecked(cf_RejoinChannelsOnConnect);
ui->chkWhoisActive->setChecked(cf_ShowWhoisActiveWindow);
ui->chkShowModeMsg->setChecked(cf_ShowModeInMessage);
ui->chkTrayNotify->setChecked(cf_TrayNotify);
ui->edTrayDelay->setValue(cf_TrayNotifyDelay);
ui->chkTimestamp->setChecked(cf_ShowTimestamp);
ui->edTimestamp->setText(cf_TimestampFormat);
ui->chkManualKeepalive->setChecked(cf_ManualKeepaliveEnabled);
ui->edManualKeepalive->setValue(cf_ManualKeepalive);
ui->edQuit->setText(cf_QuitMessage);
ui->edFont->setCurrentFont(cf_Font);
ui->edFontSize->setValue(cf_FontSize);
ui->chkEnableBgImage->setChecked(cf_BgImageEnabled);
ui->edImage->setText(cf_BgImagePath);
ui->hsImageOpacity->setValue(cf_BgImageOpacity);
ui->chkSSLSelfsigned->setChecked(cf_SSLSelfSigned);
ui->chkSSLExpired->setChecked(cf_SSLExpired);
ui->chkSSLCNMismatch->setChecked(cf_SSLCNMismatch);
colorCfg->reset();
colorCfg->reset();
}
void IConfigOptions::on_chkSSLExpired_toggled(bool checked)
{
if (checked)
QMessageBox::warning(this, tr("Expired SSL certificates"),
tr("Allowing expired certificates is dangerous!\nThis option will revert after next connection."));
if (checked)
QMessageBox::warning(this, tr("Expired SSL certificates"),
tr("Allowing expired certificates is dangerous!\nThis option will revert after next connection."));
}
void IConfigOptions::reload()
{
ConfigMgr& conf = ConfigMgr::instance();
cf_ShowOptions = conf.common("ShowOptions").toInt();
cf_Reconnect = conf.common("Reconnect").toInt();
cf_ReconnectDelay = conf.common("ReconnectDelay").toInt();
cf_RejoinChannelsOnConnect = conf.common("RejoinChannelsOnConnect").toInt();
cf_ShowWhoisActiveWindow = conf.common("ShowWhoisActiveWindow").toInt();
cf_ShowModeInMessage = conf.common("ShowModeInMessage").toInt();
cf_TrayNotify = conf.common("TrayNotify").toInt();
cf_TrayNotifyDelay = conf.common("TrayNotifyDelay").toInt();
cf_ShowTimestamp = conf.common("ShowTimestamp").toInt();
ConfigMgr& conf = ConfigMgr::instance();
cf_ShowOptions = conf.common("ShowOptions").toInt();
cf_Reconnect = conf.common("Reconnect").toInt();
cf_ReconnectDelay = conf.common("ReconnectDelay").toInt();
cf_RejoinChannelsOnConnect = conf.common("RejoinChannelsOnConnect").toInt();
cf_ShowWhoisActiveWindow = conf.common("ShowWhoisActiveWindow").toInt();
cf_ShowModeInMessage = conf.common("ShowModeInMessage").toInt();
cf_TrayNotify = conf.common("TrayNotify").toInt();
cf_TrayNotifyDelay = conf.common("TrayNotifyDelay").toInt();
cf_ShowTimestamp = conf.common("ShowTimestamp").toInt();
cf_TimestampFormat = conf.common("TimestampFormat");
cf_ManualKeepaliveEnabled = conf.common("ManualKeepaliveEnabled").toInt();
cf_ManualKeepalive = conf.common("ManualKeepalive").toInt();
cf_QuitMessage = conf.common("QuitMessage");
cf_Font = conf.common("Font");
cf_FontSize = conf.common("FontSize").toInt();
cf_BgImageEnabled = conf.common("BgImageEnabled").toInt();
cf_BgImagePath = conf.common("BgImagePath");
cf_BgImageOpacity = conf.common("BgImageOpacity").toInt();
// TODO background image scaling combo box from config
cf_SSLSelfSigned = conf.common("SSLSelfsigned").toInt();
cf_SSLExpired = conf.common("SSLExpired").toInt();
cf_SSLCNMismatch = conf.common("SSLCNMismatch").toInt();
cf_Font = conf.common("Font");
cf_FontSize = conf.common("FontSize").toInt();
cf_BgImageEnabled = conf.common("BgImageEnabled").toInt();
cf_BgImagePath = conf.common("BgImagePath");
cf_BgImageOpacity = conf.common("BgImageOpacity").toInt();
// TODO background image scaling combo box from config
cf_SSLSelfSigned = conf.common("SSLSelfsigned").toInt();
cf_SSLExpired = conf.common("SSLExpired").toInt();
cf_SSLCNMismatch = conf.common("SSLCNMismatch").toInt();
}
void IConfigOptions::on_btnImageBrowse_clicked()
{
QString path = QFileDialog::getOpenFileName(this, tr("Choose background image"), LOCAL_PATH, tr("Image Files (*.png *.jpg *.bmp)"));
ui->edImage->setText(path);
QString path = QFileDialog::getOpenFileName(this, tr("Choose background image"), LOCAL_PATH, tr("Image Files (*.png *.jpg *.bmp)"));
ui->edImage->setText(path);
}

@ -19,46 +19,46 @@ class IConfigOptions;
class IConfigOptions : public QWidget
{
Q_OBJECT
Q_OBJECT
public:
explicit IConfigOptions(QWidget *parent = nullptr);
~IConfigOptions();
bool isChanged() const;
void save();
void reset();
explicit IConfigOptions(QWidget *parent = nullptr);
~IConfigOptions();
bool isChanged() const;
void save();
void reset();
private slots:
void on_chkSSLExpired_toggled(bool checked);
void on_btnImageBrowse_clicked();
void on_chkSSLExpired_toggled(bool checked);
void on_btnImageBrowse_clicked();
private:
void reload();
Ui::IConfigOptions *ui;
QVBoxLayout *layout;
ColorConfig *colorCfg;
bool cf_ShowOptions;
bool cf_Reconnect;
int cf_ReconnectDelay;
bool cf_RejoinChannelsOnConnect;
bool cf_ShowWhoisActiveWindow;
bool cf_ShowModeInMessage;
bool cf_TrayNotify;
int cf_TrayNotifyDelay;
bool cf_ShowTimestamp;
QString cf_TimestampFormat;
void reload();
Ui::IConfigOptions *ui;
QVBoxLayout *layout;
ColorConfig *colorCfg;
bool cf_ShowOptions;
bool cf_Reconnect;
int cf_ReconnectDelay;
bool cf_RejoinChannelsOnConnect;
bool cf_ShowWhoisActiveWindow;
bool cf_ShowModeInMessage;
bool cf_TrayNotify;
int cf_TrayNotifyDelay;
bool cf_ShowTimestamp;
QString cf_TimestampFormat;
bool cf_ManualKeepaliveEnabled;
int cf_ManualKeepalive;
QString cf_QuitMessage;
QFont cf_Font;
int cf_FontSize;
bool cf_BgImageEnabled;
QString cf_BgImagePath;
int cf_BgImageOpacity;
// TODO background image scaling combo box
bool cf_SSLSelfSigned;
bool cf_SSLExpired;
bool cf_SSLCNMismatch;
int cf_ManualKeepalive;
QString cf_QuitMessage;
QFont cf_Font;
int cf_FontSize;
bool cf_BgImageEnabled;
QString cf_BgImagePath;
int cf_BgImageOpacity;
// TODO background image scaling combo box
bool cf_SSLSelfSigned;
bool cf_SSLExpired;
bool cf_SSLCNMismatch;
};
#endif // ICONFIGOPTIONS_H

@ -11,46 +11,46 @@
#include <QDebug>
IConfigServers::IConfigServers(QWidget *parent) :
QWidget(parent),
ui(new Ui::IConfigServers)
QWidget(parent),
ui(new Ui::IConfigServers)
{
ui->setupUi(this);
ConfigMgr& conf = ConfigMgr::instance();
cf_Realname = conf.connection("Realname");
cf_Username = conf.connection("Username");
cf_Nickname = conf.connection("Nickname");
cf_AltNickame = conf.connection("AltNickname");
cf_Server = conf.connection("Server");
cf_Password = conf.connection("Password");
cf_SSL = conf.connection("SSL").toInt();
ui->edRealName->setText(cf_Realname);
ui->edUsername->setText(cf_Username);
ui->edNickname->setText(cf_Nickname);
ui->edAltNickname->setText(cf_AltNickame);
ui->edServer->setText(cf_Server);
ui->edServerPassword->setText(cf_Password);
ui->chkSSL->setChecked(cf_SSL);
editor = new ServerEditor(smodel, this);
ui->servers->setModel(&smodel);
ui->setupUi(this);
ConfigMgr& conf = ConfigMgr::instance();
cf_Realname = conf.connection("Realname");
cf_Username = conf.connection("Username");
cf_Nickname = conf.connection("Nickname");
cf_AltNickame = conf.connection("AltNickname");
cf_Server = conf.connection("Server");
cf_Password = conf.connection("Password");
cf_SSL = conf.connection("SSL").toInt();
ui->edRealName->setText(cf_Realname);
ui->edUsername->setText(cf_Username);
ui->edNickname->setText(cf_Nickname);
ui->edAltNickname->setText(cf_AltNickame);
ui->edServer->setText(cf_Server);
ui->edServerPassword->setText(cf_Password);
ui->chkSSL->setChecked(cf_SSL);
editor = new ServerEditor(smodel, this);
ui->servers->setModel(&smodel);
connect(editor, &ServerEditor::saveClicked,
this, &IConfigServers::onServerEditorSaved);
QModelIndex sindex = smodel.indexFromHost(cf_Server);
if (sindex.isValid())
ui->servers->selectionModel()->setCurrentIndex(sindex, QItemSelectionModel::Select | QItemSelectionModel::Rows);
QModelIndex sindex = smodel.indexFromHost(cf_Server);
if (sindex.isValid())
ui->servers->selectionModel()->setCurrentIndex(sindex, QItemSelectionModel::Select | QItemSelectionModel::Rows);
}
IConfigServers::~IConfigServers()
{
delete ui;
delete ui;
}
bool IConfigServers::isChanged() const
{
return cf_Realname != ui->edRealName->text()
return cf_Realname != ui->edRealName->text()
|| cf_Username != ui->edUsername->text()
|| cf_Nickname != ui->edNickname->text()
|| cf_AltNickame != ui->edAltNickname->text()
@ -61,66 +61,66 @@ bool IConfigServers::isChanged() const
bool IConfigServers::connectToNewStatus() const
{
return ui->chkNewStatus->isChecked();
return ui->chkNewStatus->isChecked();
}
void IConfigServers::unsetConnectToNewStatus()
{
ui->chkNewStatus->setChecked(false);
ui->chkNewStatus->setChecked(false);
}
void IConfigServers::save()
{
ConfigMgr& conf = ConfigMgr::instance();
conf.setConnection("Realname", ui->edRealName->text());
conf.setConnection("Username", ui->edUsername->text());
conf.setConnection("Nickname", ui->edNickname->text());
conf.setConnection("AltNickname", ui->edAltNickname->text());
conf.setConnection("Server", ui->edServer->text());
conf.setConnection("Password", ui->edServerPassword->text());
conf.setConnection("SSL", QString::number(ui->chkSSL->isChecked()));
cf_Realname = conf.connection("Realname");
cf_Username = conf.connection("Username");
cf_Nickname = conf.connection("Nickname");
cf_AltNickame = conf.connection("AltNickname");
cf_Server = conf.connection("Server");
cf_Password = conf.connection("Password");
cf_SSL = conf.connection("SSL").toInt();
ConfigMgr& conf = ConfigMgr::instance();
conf.setConnection("Realname", ui->edRealName->text());
conf.setConnection("Username", ui->edUsername->text());
conf.setConnection("Nickname", ui->edNickname->text());
conf.setConnection("AltNickname", ui->edAltNickname->text());
conf.setConnection("Server", ui->edServer->text());
conf.setConnection("Password", ui->edServerPassword->text());
conf.setConnection("SSL", QString::number(ui->chkSSL->isChecked()));
cf_Realname = conf.connection("Realname");
cf_Username = conf.connection("Username");
cf_Nickname = conf.connection("Nickname");
cf_AltNickame = conf.connection("AltNickname");
cf_Server = conf.connection("Server");
cf_Password = conf.connection("Password");
cf_SSL = conf.connection("SSL").toInt();
}
void IConfigServers::reset()
{
ui->edRealName->setText(cf_Realname);
ui->edUsername->setText(cf_Username);
ui->edNickname->setText(cf_Nickname);
ui->edAltNickname->setText(cf_AltNickame);
ui->edServer->setText(cf_Server);
ui->edServerPassword->setText(cf_Password);
ui->edRealName->setText(cf_Realname);
ui->edUsername->setText(cf_Username);
ui->edNickname->setText(cf_Nickname);
ui->edAltNickname->setText(cf_AltNickame);
ui->edServer->setText(cf_Server);
ui->edServerPassword->setText(cf_Password);
}
void IConfigServers::on_btnShowPassword_toggled(bool checked)
{
ui->edServerPassword->setEchoMode(checked ? QLineEdit::Normal : QLineEdit::Password);
ui->edServerPassword->setEchoMode(checked ? QLineEdit::Normal : QLineEdit::Password);
}
void IConfigServers::on_btnEditServers_clicked()
{
editor->show();
editor->show();
}
void IConfigServers::on_servers_clicked(const QModelIndex &index)
{
auto spair = smodel.fromIndex(index);
QString details = smodel.details(spair.second, spair.first);
QString server;
QString password;
auto spair = smodel.fromIndex(index);
QString details = smodel.details(spair.second, spair.first);
QString server;
QString password;
if (details.contains('|')) {
password = details.split('|')[1];
details.remove("|"+password);
}
if (details.contains('|')) {
password = details.split('|')[1];
details.remove("|"+password);
}
bool ssl = details[0] == '$';
if (ssl)
@ -128,8 +128,8 @@ void IConfigServers::on_servers_clicked(const QModelIndex &index)
server = details;
ui->edServer->setText(server);
ui->edServerPassword->setText(password);
ui->edServer->setText(server);
ui->edServerPassword->setText(password);
ui->chkSSL->setChecked(ssl);
}

@ -18,34 +18,34 @@ class IConfigServers;
class IConfigServers : public QWidget
{
Q_OBJECT
Q_OBJECT
public:
explicit IConfigServers(QWidget *parent = nullptr);
~IConfigServers();
bool isChanged() const;
bool connectToNewStatus() const;
void unsetConnectToNewStatus();
void save();
void reset();
explicit IConfigServers(QWidget *parent = nullptr);
~IConfigServers();
bool isChanged() const;
bool connectToNewStatus() const;
void unsetConnectToNewStatus();
void save();
void reset();
private slots:
void on_btnShowPassword_toggled(bool checked);
void on_btnEditServers_clicked();
void on_servers_clicked(const QModelIndex &index);
void on_btnShowPassword_toggled(bool checked);
void on_btnEditServers_clicked();
void on_servers_clicked(const QModelIndex &index);
void onServerEditorSaved();
private:
Ui::IConfigServers *ui;
ServerModel smodel;
ServerEditor *editor;
QString cf_Realname;
QString cf_Username;
QString cf_Nickname;
QString cf_AltNickame;
QString cf_Server;
QString cf_Password;
bool cf_SSL;
Ui::IConfigServers *ui;
ServerModel smodel;
ServerEditor *editor;
QString cf_Realname;
QString cf_Username;
QString cf_Nickname;
QString cf_AltNickame;
QString cf_Server;
QString cf_Password;
bool cf_SSL;
};
#endif // ICONFIGSERVERS_H

@ -12,291 +12,291 @@
#include <QInputDialog>
ServerEditor::ServerEditor(ServerModel& model, QWidget* parent)
: QDialog(parent)
, ui(new Ui::ServerEditor)
, smodel(model)
: QDialog(parent)
, ui(new Ui::ServerEditor)
, smodel(model)
{
ui->setupUi(this);
addMenu = new QMenu(this);
addServerAction = addMenu->addAction(tr("Server"), this, &ServerEditor::on_addServerAction_triggered);
addNetworkAction = addMenu->addAction(tr("Network"), this, &ServerEditor::on_addNetworkAction_triggered);
ui->btnAdd->setMenu(addMenu);
ui->serverView->setModel(&smodel);
ui->serverView->header()->setSectionResizeMode(QHeaderView::Interactive);
QStringList networks = smodel.networkList();
ui->edNetwork->addItem("");
for (const QString& network : networks) {
if (network == "NONE")
continue;
ui->edNetwork->addItem(network);
}
ui->setupUi(this);
addMenu = new QMenu(this);
addServerAction = addMenu->addAction(tr("Server"), this, &ServerEditor::on_addServerAction_triggered);
addNetworkAction = addMenu->addAction(tr("Network"), this, &ServerEditor::on_addNetworkAction_triggered);
ui->btnAdd->setMenu(addMenu);
ui->serverView->setModel(&smodel);
ui->serverView->header()->setSectionResizeMode(QHeaderView::Interactive);
QStringList networks = smodel.networkList();
ui->edNetwork->addItem("");
for (const QString& network : networks) {
if (network == "NONE")
continue;
ui->edNetwork->addItem(network);
}
}
ServerEditor::~ServerEditor()
{
delete ui;
delete ui;
}
void ServerEditor::on_addServerAction_triggered()
{
QString network = "NONE";
if (editMode == EditMode::Network) {
QModelIndex current = ui->serverView->currentIndex();
auto spair = smodel.fromIndex(current);
network = spair.first;
}
else if (editMode == EditMode::Server) {
QModelIndex current = ui->serverView->currentIndex();
if (current.parent().isValid()) {
auto spair = smodel.fromIndex(current.parent());
network = spair.first;
}
}
QString name;
QString host;
for (int i = 1 ;; ++i) {
name = QStringLiteral("New server %1").arg(i);
host = QStringLiteral("host%1.name:6667").arg(i);
QString det = smodel.details(name);
if (det.isEmpty())
break;
}
QModelIndex idx = smodel.addServer(name, host, network);
selectItem(idx);
QString network = "NONE";
if (editMode == EditMode::Network) {
QModelIndex current = ui->serverView->currentIndex();
auto spair = smodel.fromIndex(current);
network = spair.first;
}
else if (editMode == EditMode::Server) {
QModelIndex current = ui->serverView->currentIndex();
if (current.parent().isValid()) {
auto spair = smodel.fromIndex(current.parent());
network = spair.first;
}
}
QString name;
QString host;
for (int i = 1 ;; ++i) {
name = QStringLiteral("New server %1").arg(i);
host = QStringLiteral("host%1.name:6667").arg(i);
QString det = smodel.details(name);
if (det.isEmpty())
break;
}
QModelIndex idx = smodel.addServer(name, host, network);
selectItem(idx);
}
void ServerEditor::on_addNetworkAction_triggered()
{
QString name;
QString host;
for (int i = 1 ;; ++i) {
name = QStringLiteral("New network %1").arg(i);
host = QStringLiteral("irc%1.host.name:6667").arg(i);
QString det = smodel.details("DEFAULT", name);
if (det.isEmpty())
break;
}
QModelIndex idx = smodel.addNetwork(name, host);
ui->edNetwork->addItem(name);
selectItem(idx);
QString name;
QString host;
for (int i = 1 ;; ++i) {
name = QStringLiteral("New network %1").arg(i);
host = QStringLiteral("irc%1.host.name:6667").arg(i);
QString det = smodel.details("DEFAULT", name);
if (det.isEmpty())
break;
}
QModelIndex idx = smodel.addNetwork(name, host);
ui->edNetwork->addItem(name);
selectItem(idx);
}
void ServerEditor::on_btnDel_clicked()
{
if (editMode == EditMode::Off)
return;
QModelIndex current = ui->serverView->currentIndex();
auto spair = smodel.fromIndex(current);
QString network = spair.first;
QString name = spair.second;
if (editMode == EditMode::Server) {
if (QMessageBox::question(this, tr("Delete server"), tr("Do you want to delete the server '%1'?").arg(name)) == QMessageBox::No)
return;
smodel.delServer(name, network);
}
if (editMode == EditMode::Network) {
name = spair.first;
auto answer = QMessageBox::question(this, tr("Delete network"), tr("Do you want to delete the network '%1'?\nHitting 'Yes' will keep the servers as orphans.").arg(name), QMessageBox::Yes | QMessageBox::YesAll | QMessageBox::No);
if (answer == QMessageBox::No)
return;
bool keepServers = answer != QMessageBox::YesAll;
smodel.delNetwork(name, keepServers);
}
ui->serverView->clearSelection();
disableAll();
if (editMode == EditMode::Off)
return;
QModelIndex current = ui->serverView->currentIndex();
auto spair = smodel.fromIndex(current);
QString network = spair.first;
QString name = spair.second;
if (editMode == EditMode::Server) {
if (QMessageBox::question(this, tr("Delete server"), tr("Do you want to delete the server '%1'?").arg(name)) == QMessageBox::No)
return;
smodel.delServer(name, network);
}
if (editMode == EditMode::Network) {
name = spair.first;
auto answer = QMessageBox::question(this, tr("Delete network"), tr("Do you want to delete the network '%1'?\nHitting 'Yes' will keep the servers as orphans.").arg(name), QMessageBox::Yes | QMessageBox::YesAll | QMessageBox::No);
if (answer == QMessageBox::No)
return;
bool keepServers = answer != QMessageBox::YesAll;
smodel.delNetwork(name, keepServers);
}
ui->serverView->clearSelection();
disableAll();
}
void ServerEditor::on_btnShowPassword_toggled(bool checked)
{
ui->edPassword->setEchoMode(checked ? QLineEdit::Normal : QLineEdit::Password);
ui->edPassword->setEchoMode(checked ? QLineEdit::Normal : QLineEdit::Password);
}
void ServerEditor::on_btnSave_clicked()
{
if (editMode == EditMode::Off)
return;
if (editMode == EditMode::Off)
return;
if (ui->edName->text().isEmpty()) {
QMessageBox::information(this, tr("Field missing"), tr("No name is specified."));
return;
}
if (ui->edName->text().isEmpty()) {
QMessageBox::information(this, tr("Field missing"), tr("No name is specified."));
return;
}
if (ui->edHostname->text().isEmpty()) {
QMessageBox::information(this, tr("Field missing"), tr("No hostname is specified."));
return;
}
if (ui->edHostname->text().isEmpty()) {
QMessageBox::information(this, tr("Field missing"), tr("No hostname is specified."));
return;
}
if (editMode == EditMode::Server)
saveServerEdit();
if (editMode == EditMode::Server)
saveServerEdit();
else if (editMode == EditMode::Network)
saveNetworkEdit();
else if (editMode == EditMode::Network)
saveNetworkEdit();
emit saveClicked();
}
void ServerEditor::enableForServer()
{
enableAll();
ui->lbNetwork->show();
ui->edNetwork->show();
editMode = EditMode::Server;
enableAll();
ui->lbNetwork->show();
ui->edNetwork->show();
editMode = EditMode::Server;
}
void ServerEditor::enableForNetwork()
{
enableAll();
ui->lbNetwork->hide();
ui->edNetwork->hide();
editMode = EditMode::Network;
enableAll();
ui->lbNetwork->hide();
ui->edNetwork->hide();
editMode = EditMode::Network;
}
void ServerEditor::enableAll()
{
ui->edName->setEnabled(true);
ui->edHostname->setEnabled(true);
ui->edPort->setEnabled(true);
ui->edPassword->setEnabled(true);
ui->btnShowPassword->setEnabled(true);
ui->btnShowPassword->setChecked(false);
ui->edNetwork->setEnabled(true);
ui->btnSave->setEnabled(true);
ui->edName->setEnabled(true);
ui->edHostname->setEnabled(true);
ui->edPort->setEnabled(true);
ui->edPassword->setEnabled(true);
ui->btnShowPassword->setEnabled(true);
ui->btnShowPassword->setChecked(false);
ui->edNetwork->setEnabled(true);
ui->btnSave->setEnabled(true);
}
void ServerEditor::disableAll()
{
ui->edName->clear();
ui->edHostname->clear();
ui->edPort->setValue(6667);
ui->edPassword->clear();
ui->btnShowPassword->setChecked(false);
ui->edNetwork->clearEditText();
ui->edName->setEnabled(false);
ui->edHostname->setEnabled(false);
ui->edPort->setEnabled(false);
ui->edPassword->setEnabled(false);
ui->btnShowPassword->setEnabled(false);
ui->edNetwork->setEnabled(false);
ui->btnSave->setEnabled(false);
ui->lbNetwork->show();
ui->edNetwork->show();
editMode = EditMode::Off;
ui->edName->clear();
ui->edHostname->clear();
ui->edPort->setValue(6667);
ui->edPassword->clear();
ui->btnShowPassword->setChecked(false);
ui->edNetwork->clearEditText();
ui->edName->setEnabled(false);
ui->edHostname->setEnabled(false);
ui->edPort->setEnabled(false);
ui->edPassword->setEnabled(false);
ui->btnShowPassword->setEnabled(false);
ui->edNetwork->setEnabled(false);
ui->btnSave->setEnabled(false);
ui->lbNetwork->show();
ui->edNetwork->show();
editMode = EditMode::Off;
}
void ServerEditor::saveNetworkEdit()
{
qDebug() << "Save network";
QModelIndex current = ui->serverView->currentIndex();
auto spair = smodel.fromIndex(current);
QString network = spair.first;
/* Network name changed */
if (network != ui->edName->text()) {
QString newNetwork = ui->edName->text();
if (ui->edNetwork->findText(newNetwork) > -1) {
QMessageBox::information(this, tr("Duplicate network name"), tr("The specified network name already exist."));
return;
}
smodel.renameNetwork(network, newNetwork);
int networkIdx = ui->edNetwork->findText(network);
ui->edNetwork->setItemText(networkIdx, newNetwork);
network = newNetwork;
}
QString details = ui->edHostname->text() + ":" + QString::number(ui->edPort->value());
QString password = ui->edPassword->text();
if (!password.isEmpty())
details += "|" + password;
smodel.setNetworkServer(network, details);
qDebug() << "Save network";
QModelIndex current = ui->serverView->currentIndex();
auto spair = smodel.fromIndex(current);
QString network = spair.first;
/* Network name changed */
if (network != ui->edName->text()) {
QString newNetwork = ui->edName->text();
if (ui->edNetwork->findText(newNetwork) > -1) {
QMessageBox::information(this, tr("Duplicate network name"), tr("The specified network name already exist."));
return;
}
smodel.renameNetwork(network, newNetwork);
int networkIdx = ui->edNetwork->findText(network);
ui->edNetwork->setItemText(networkIdx, newNetwork);
network = newNetwork;
}
QString details = ui->edHostname->text() + ":" + QString::number(ui->edPort->value());
QString password = ui->edPassword->text();
if (!password.isEmpty())
details += "|" + password;
smodel.setNetworkServer(network, details);
}
void ServerEditor::saveServerEdit()
{
qDebug() << "Save server";
QModelIndex current = ui->serverView->currentIndex();
auto spair = smodel.fromIndex(current);
qDebug() << "Save server";
QModelIndex current = ui->serverView->currentIndex();
auto spair = smodel.fromIndex(current);
QString name = ui->edName->text();
QString host = ui->edHostname->text();
QString port = QString::number(ui->edPort->value());
QString password = ui->edPassword->text();
QString network = ui->edNetwork->currentText();
QString name = ui->edName->text();
QString host = ui->edHostname->text();
QString port = QString::number(ui->edPort->value());
QString password = ui->edPassword->text();
QString network = ui->edNetwork->currentText();
if (ui->chkSSL->isChecked())
host.prepend('$');
if (ui->edNetwork->currentIndex() == 0)
network = "NONE";
if (ui->edNetwork->currentIndex() == 0)
network = "NONE";
if (spair.first != network) {
/* New network */
if (network != "NONE" && ui->edNetwork->findText(network) == -1) {
bool ok = false;
QString hostname = QInputDialog::getText(this, tr("New network"), tr("Set hostname:port for the new network %1").arg(network), QLineEdit::Normal, "", &ok);
if (!ok || hostname.isEmpty())
return;
smodel.addNetwork(network, hostname);
ui->edNetwork->addItem(network);
}
QString details = smodel.details(spair.second, spair.first);
smodel.delServer(spair.second, spair.first);
QModelIndex idx = smodel.addServer(spair.second, details, network);
selectItem(idx);
}
smodel.setServer(spair.second, host + ":" + port, password, network);
if (spair.second != name) {
QString details = smodel.details(spair.second, network);
smodel.delServer(spair.second, network);
smodel.resetModel();
QModelIndex idx = smodel.addServer(name, details, network);
selectItem(idx);
}
/* New network */
if (network != "NONE" && ui->edNetwork->findText(network) == -1) {
bool ok = false;
QString hostname = QInputDialog::getText(this, tr("New network"), tr("Set hostname:port for the new network %1").arg(network), QLineEdit::Normal, "", &ok);
if (!ok || hostname.isEmpty())
return;
smodel.addNetwork(network, hostname);
ui->edNetwork->addItem(network);
}
QString details = smodel.details(spair.second, spair.first);
smodel.delServer(spair.second, spair.first);
QModelIndex idx = smodel.addServer(spair.second, details, network);
selectItem(idx);
}
smodel.setServer(spair.second, host + ":" + port, password, network);
if (spair.second != name) {
QString details = smodel.details(spair.second, network);
smodel.delServer(spair.second, network);
smodel.resetModel();
QModelIndex idx = smodel.addServer(name, details, network);
selectItem(idx);
}
}
void ServerEditor::selectItem(const QModelIndex& index)
{
ui->serverView->clearSelection();
ui->serverView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select | QItemSelectionModel::Rows);
on_serverView_clicked(index);
ui->serverView->clearSelection();
ui->serverView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select | QItemSelectionModel::Rows);
on_serverView_clicked(index);
}
void ServerEditor::on_serverView_clicked(const QModelIndex &index)
{
if (!index.isValid()) {
disableAll();
return;
}
auto spair = smodel.fromIndex(index);
if (spair.first.isEmpty()) {
disableAll();
return;
}
QString name;
if (spair.second == "DEFAULT") {
name = spair.first;
enableForNetwork();
}
else {
name = spair.second;
enableForServer();
}
QString details = smodel.details(spair.second, spair.first);
QString host, port, password;
if (!index.isValid()) {
disableAll();
return;
}
auto spair = smodel.fromIndex(index);
if (spair.first.isEmpty()) {
disableAll();
return;
}
QString name;
if (spair.second == "DEFAULT") {
name = spair.first;
enableForNetwork();
}
else {
name = spair.second;
enableForServer();
}
QString details = smodel.details(spair.second, spair.first);
QString host, port, password;
// Value format: $host:port|password
// Presence of '$' determines if it's an SSL server.
@ -306,25 +306,25 @@ void ServerEditor::on_serverView_clicked(const QModelIndex &index)
details = details.mid(1);
if (details.contains('|')) {
password = details.split('|')[1];
details = details.remove("|"+password);
}
password = details.split('|')[1];
details = details.remove("|"+password);
}
if (details.contains(':')) {
port = details.split(':')[1];
details = details.remove(":"+port);
}
port = details.split(':')[1];
details = details.remove(":"+port);
}
host = details;
ui->edName->setText(name);
ui->edHostname->setText(host);
ui->edPort->setValue(port.toInt());
ui->edPassword->setText(password);
ui->edName->setText(name);
ui->edHostname->setText(host);
ui->edPort->setValue(port.toInt());
ui->edPassword->setText(password);
ui->chkSSL->setChecked(ssl);
int networkUiIndex = ui->edNetwork->findText(spair.first);
if (networkUiIndex == -1)
networkUiIndex = 0;
ui->edNetwork->setCurrentIndex(networkUiIndex);
int networkUiIndex = ui->edNetwork->findText(spair.first);
if (networkUiIndex == -1)
networkUiIndex = 0;
ui->edNetwork->setCurrentIndex(networkUiIndex);
}

@ -18,45 +18,45 @@ class ServerEditor;
class ServerEditor : public QDialog
{
Q_OBJECT
Q_OBJECT
public:
explicit ServerEditor(ServerModel& model, QWidget *parent = nullptr);
~ServerEditor();
explicit ServerEditor(ServerModel& model, QWidget *parent = nullptr);
~ServerEditor();
signals:
void saveClicked();
private slots:
void on_addServerAction_triggered();
void on_addNetworkAction_triggered();
void on_btnDel_clicked();
void on_btnShowPassword_toggled(bool checked);
void on_btnSave_clicked();
void on_serverView_clicked(const QModelIndex &index);
void on_addServerAction_triggered();
void on_addNetworkAction_triggered();
void on_btnDel_clicked();
void on_btnShowPassword_toggled(bool checked);
void on_btnSave_clicked();
void on_serverView_clicked(const QModelIndex &index);
private:
enum class EditMode {
Off,
Server,
Network
} editMode = EditMode::Off;
void enableForServer();
void enableForNetwork();
void enableAll();
void disableAll();
void saveNetworkEdit();
void saveServerEdit();
void selectItem(const QModelIndex& index);
Ui::ServerEditor* ui;
QMenu* addMenu;
QAction* addServerAction;
QAction* addNetworkAction;
ServerModel& smodel;
enum class EditMode {
Off,
Server,
Network
} editMode = EditMode::Off;
void enableForServer();
void enableForNetwork();
void enableAll();
void disableAll();
void saveNetworkEdit();
void saveServerEdit();
void selectItem(const QModelIndex& index);
Ui::ServerEditor* ui;
QMenu* addMenu;
QAction* addServerAction;
QAction* addNetworkAction;
ServerModel& smodel;
};
#endif // SERVEREDITOR_H

@ -11,16 +11,16 @@
ServerMgr::ServerMgr(QObject *parent) :
QObject(parent),
ini( QString(LOCAL_PATH+"/servers.ini").toStdString() )
ini( QString(LOCAL_PATH+"/servers.ini").toStdString() )
{
}
QStringList ServerMgr::networkList()
{
int count = ini.count();
int count = ini.count();
QStringList r;
for (int i = 0; i < count; i++) {
for (int i = 0; i < count; i++) {
const auto name = QString::fromStdString( ini.section(i) );
r.push_back(name);
}
@ -32,12 +32,12 @@ QHash<QString,QString> ServerMgr::serverList(const QString& network)
{
const auto networkStr = network.toStdString();
int count = ini.count(networkStr);
int count = ini.count(networkStr);
QHash<QString,QString> r;
for (int i = 0; i < count; i++) {
const auto servername = QString::fromStdString( ini.item(networkStr, i) );
const auto serverdetails = QString::fromStdString( ini.read(networkStr, i) );
for (int i = 0; i < count; i++) {
const auto servername = QString::fromStdString( ini.item(networkStr, i) );
const auto serverdetails = QString::fromStdString( ini.read(networkStr, i) );
r.insert(servername, serverdetails);
}
@ -46,48 +46,48 @@ QHash<QString,QString> ServerMgr::serverList(const QString& network)
QString ServerMgr::defaultServer(const QString& network)
{
return QString::fromStdString( ini.read(network.toStdString(), "DEFAULT") );
return QString::fromStdString( ini.read(network.toStdString(), "DEFAULT") );
}
bool ServerMgr::addNetwork(const QString& name)
{
const auto nameStr = name.toStdString();
if (nameStr == "NONE" || ini.exist(nameStr))
return false;
if (nameStr == "NONE" || ini.exist(nameStr))
return false;
ini.write(nameStr, "DEFAULT", "server.name");
return true;
ini.write(nameStr, "DEFAULT", "server.name");
return true;
}
bool ServerMgr::renameNetwork(const QString& o_name, const QString& n_name)
{
if ((o_name == "NONE") || (n_name == "NONE"))
return false;
if ((o_name == "NONE") || (n_name == "NONE"))
return false;
auto err = ini.rename(o_name.toStdString(), n_name.toStdString());
return err == IniFile::Error::NoError;
auto err = ini.rename(o_name.toStdString(), n_name.toStdString());
return err == IniFile::Error::NoError;
}
void ServerMgr::delNetwork(const QString& name, bool keep_servers)
{
// If servers=true, we will keep the servers by moving them to the NONE section.
const auto nameStr = name.toStdString();
if (! ini.exist(nameStr))
const auto nameStr = name.toStdString();
if (! ini.exist(nameStr))
return;
if (keep_servers) {
int max = ini.count(nameStr);
for (int i = 0; i < max; i++) {
auto item = fmt::format("{}_{}", name.toStdString(), ini.item(nameStr, i));
auto value = ini.read(nameStr, i);
int max = ini.count(nameStr);
for (int i = 0; i < max; i++) {
auto item = fmt::format("{}_{}", name.toStdString(), ini.item(nameStr, i));
auto value = ini.read(nameStr, i);
ini.write("NONE", item, value);
ini.write("NONE", item, value);
}
}
ini.remove(nameStr);
ini.remove(nameStr);
}
bool ServerMgr::addServer(const QString& name, const QString& host, const QString& pw, const QString& network)
@ -105,29 +105,29 @@ bool ServerMgr::addServer(const QString& name, const QString& host, const QStrin
const auto networkStr = network.toStdString();
if (!ini.exist(networkStr) && networkStr != "NONE")
if (!ini.exist(networkStr) && networkStr != "NONE")
return false;
ini.write(networkStr, name.toStdString(), details.toStdString());
ini.write(networkStr, name.toStdString(), details.toStdString());
return true;
}
void ServerMgr::delServer(const QString& name, const QString& network)
{
ini.remove(network.toStdString(), name.toStdString());
ini.remove(network.toStdString(), name.toStdString());
}
bool ServerMgr::hasNetwork(const QString& name)
{
return ini.exist(name.toStdString());
return ini.exist(name.toStdString());
}
bool ServerMgr::hasServer(const QString& name, const QString& network)
{
return ini.exist(network.toStdString(), name.toStdString());
return ini.exist(network.toStdString(), name.toStdString());
}
QString ServerMgr::getServerDetails(const QString& name, const QString& network)
{
return QString::fromStdString( ini.read(network.toStdString(), name.toStdString()) );
return QString::fromStdString( ini.read(network.toStdString(), name.toStdString()) );
}

@ -24,7 +24,7 @@ class ServerMgr : public QObject
Q_OBJECT
public:
explicit ServerMgr(QObject *parent = nullptr);
explicit ServerMgr(QObject *parent = nullptr);
// All networks in a string list (Also counts in the NONE network)
QStringList networkList();
@ -36,9 +36,9 @@ public:
QString defaultServer(const QString& network);
// Add new network to servers.ini - returns false if network exist
bool addNetwork(const QString& name);
bool addNetwork(const QString& name);
// Rename a network - returns false if new network name already exist
// Rename a network - returns false if new network name already exist
bool renameNetwork(const QString& o_name, const QString& n_name);
// Delete network

@ -17,43 +17,43 @@ ServerModel::ServerModel(QObject *parent) :
QModelIndex ServerModel::indexFromHost(QString hostname)
{
return hostmap.value(hostname, QModelIndex());
return hostmap.value(hostname, QModelIndex());
}
QPair<QString, QString> ServerModel::fromIndex(const QModelIndex& index)
{
/* Locate netmap */
{
QHashIterator<QString,QModelIndex> it(netmap);
while (it.hasNext()) {
const QString& network = it.next().key();
const QModelIndex& iidx = it.value();
if (index == iidx || index == iidx.siblingAtColumn(1))
return { network, "DEFAULT" };
for (int i = 0 ;; ++i) {
QModelIndex cidx = iidx.child(i, 0);
if (!cidx.isValid())
break;
QString name = cidx.data().toString();
if (index == cidx || index == cidx.siblingAtColumn(1))
return { network, name };
}
}
}
/* Locate nonemap */
{
QHashIterator<QString,QModelIndex> it(hostmap);
while (it.hasNext()) {
it.next();
const QModelIndex& iidx = it.value();
if (index == iidx || index == iidx.siblingAtColumn(1))
return { "NONE", iidx.data().toString() };
}
}
return {};
/* Locate netmap */
{
QHashIterator<QString,QModelIndex> it(netmap);
while (it.hasNext()) {
const QString& network = it.next().key();
const QModelIndex& iidx = it.value();
if (index == iidx || index == iidx.siblingAtColumn(1))
return { network, "DEFAULT" };
for (int i = 0 ;; ++i) {
QModelIndex cidx = iidx.child(i, 0);
if (!cidx.isValid())
break;
QString name = cidx.data().toString();
if (index == cidx || index == cidx.siblingAtColumn(1))
return { network, name };
}
}
}
/* Locate nonemap */
{
QHashIterator<QString,QModelIndex> it(hostmap);
while (it.hasNext()) {
it.next();
const QModelIndex& iidx = it.value();
if (index == iidx || index == iidx.siblingAtColumn(1))
return { "NONE", iidx.data().toString() };
}
}
return {};
}
QModelIndex ServerModel::addNetwork(QString name, QString server)
@ -69,10 +69,10 @@ QModelIndex ServerModel::addNetwork(QString name, QString server)
hostmap.insert(server, pname->index());
netmap.insert(name, pname->index());
if (smgr.addNetwork(name))
smgr.addServer("DEFAULT", server, "", name);
if (smgr.addNetwork(name))
smgr.addServer("DEFAULT", server, "", name);
return pname->index();
return pname->index();
}
void ServerModel::setNetworkServer(QString name, QString server)
@ -83,7 +83,7 @@ void ServerModel::setNetworkServer(QString name, QString server)
QStandardItem *item = itemFromIndex(serverIndex);
item->setText(server);
smgr.addServer("DEFAULT", server, "", name);
smgr.addServer("DEFAULT", server, "", name);
}
void ServerModel::renameNetwork(QString name, QString newname)
@ -97,40 +97,40 @@ void ServerModel::renameNetwork(QString name, QString newname)
netmap.remove(name);
netmap.insert(newname, current);
smgr.renameNetwork(name, newname);
smgr.renameNetwork(name, newname);
}
void ServerModel::delNetwork(QString name, bool keepServers)
{
smgr.delNetwork(name, keepServers);
resetModel();
smgr.delNetwork(name, keepServers);
resetModel();
}
QModelIndex ServerModel::addServer(QString name, QString server, QString network)
{
QStandardItem *parent;
QStandardItem *parent;
if (network.length() == 0)
network = "NONE";
if (network.length() == 0)
network = "NONE";
if (network == "NONE")
parent = invisibleRootItem();
else
parent = itemFromIndex( netmap.value(network) );
if (network == "NONE")
parent = invisibleRootItem();
else
parent = itemFromIndex( netmap.value(network) );
QStandardItem *sname = new QStandardItem(QIcon(":/options/gfx/server.png"), name);
QStandardItem *shost = new QStandardItem(server);
QList<QStandardItem*> list;
list << sname << shost;
QStandardItem *sname = new QStandardItem(QIcon(":/options/gfx/server.png"), name);
QStandardItem *shost = new QStandardItem(server);
QList<QStandardItem*> list;
list << sname << shost;
parent->appendRow(list);
hostmap.insert(server, indexFromItem(sname));
if (network == "NONE")
nonemap.insert(name, indexFromItem(sname));
parent->appendRow(list);
hostmap.insert(server, indexFromItem(sname));
if (network == "NONE")
nonemap.insert(name, indexFromItem(sname));
smgr.addServer(name, server, "", network);
smgr.addServer(name, server, "", network);
return indexFromItem(sname);
return indexFromItem(sname);
}
void ServerModel::setServer(QString name, QString server, QString password, QString network)
@ -178,7 +178,7 @@ void ServerModel::setServer(QString name, QString server, QString password, QStr
serverItem->setText(host);
hostmap.insert(host, nameIndex);
smgr.addServer(name, server, password, network);
smgr.addServer(name, server, password, network);
}
void ServerModel::renameServer(QString name, QString newname, QString network)
@ -221,9 +221,9 @@ void ServerModel::renameServer(QString name, QString newname, QString network)
QStandardItem *item = itemFromIndex(serverIndex);
item->setText(newname);
QString details = smgr.getServerDetails(name, network);
smgr.delServer(name, network);
smgr.addServer(name, details, "", network);
QString details = smgr.getServerDetails(name, network);
smgr.delServer(name, network);
smgr.addServer(name, details, "", network);
}
void ServerModel::delServer(QString name, QString network)
@ -263,7 +263,7 @@ void ServerModel::delServer(QString name, QString network)
int row = current.row();
removeRow(row, current.parent());
smgr.delServer(name, network);
smgr.delServer(name, network);
}
void ServerModel::resetModel()
@ -279,90 +279,88 @@ void ServerModel::resetModel()
setHorizontalHeaderItem(0, i);
setHorizontalHeaderLabels(l);
hostmap.clear();
netmap.clear();
nonemap.clear();
hostmap.clear();
netmap.clear();
nonemap.clear();
QStringList netlist = smgr.networkList();
QStringList netlist = smgr.networkList();
if (netlist.contains("NONE")) { // "None" network is a section with servers not assigned to a network.
QHash<QString,QString> sl = smgr.serverList("NONE");
QHashIterator<QString,QString> i(sl);
while (i.hasNext()) {
i.next();
// Key: Server name
// Value: host:port|pass
QString name = i.key();
QString detail = i.value();
if (netlist.contains("NONE")) { // "None" network is a section with servers not assigned to a network.
QHash<QString,QString> sl = smgr.serverList("NONE");
QHashIterator<QString,QString> i(sl);
while (i.hasNext()) {
i.next();
// Key: Server name
// Value: host:port|pass
QString name = i.key();
QString detail = i.value();
QString host; // hostname with port, e.g. irc.network.org:6667
host = detail.split('|')[0];
QString host; // hostname with port, e.g. irc.network.org:6667
host = detail.split('|')[0];
if (host[0] == '$')
if (host[0] == '$')
host = host.mid(1);
QStandardItem *itemname = new QStandardItem(QIcon(":/options/gfx/server.png"), name);
QStandardItem *itemhost = new QStandardItem(host);
QList<QStandardItem*> list;
list << itemname << itemhost;
QStandardItem *itemname = new QStandardItem(QIcon(":/options/gfx/server.png"), name);
QStandardItem *itemhost = new QStandardItem(host);
QList<QStandardItem*> list;
list << itemname << itemhost;
root->appendRow(list);
hostmap.insert(host, indexFromItem(itemname));
nonemap.insert(name, indexFromItem(itemname));
}
}
for (int i = 0; i <= netlist.count()-1; ++i) {
root->appendRow(list);
hostmap.insert(host, indexFromItem(itemname));
nonemap.insert(name, indexFromItem(itemname));
}
}
for (int i = 0; i <= netlist.count()-1; ++i) {
if (netlist[i] == "NONE")
continue; // The "None" network already taken care of - ignore.
QString data = smgr.defaultServer(netlist[i]);
QString host = data.split('|')[0];
QStandardItem *pname = new QStandardItem(QIcon(":/options/gfx/network.png"), netlist[i]); // parent name
QStandardItem *phost = new QStandardItem(host); // parent host
QList<QStandardItem*> list;
list << pname << phost;
root->appendRow(list);
hostmap.insert(host, pname->index());
netmap.insert(netlist[i], pname->index());
QHash<QString,QString> sl = smgr.serverList(netlist[i]);
QHashIterator<QString,QString> sli(sl);
while (sli.hasNext()) {
sli.next();
// Key: Server name
// Value: host:port|pass
QString name = sli.key();
if (name == "DEFAULT")
continue; // The default value already taken care of, it's the address of parent item.
QString detail = sli.value();
QString host; // hostname with port, e.g. irc.network.org:6667
host = detail.split('|')[0];
if (host[0] == '$')
host = host.mid(1);
QStandardItem *itemname = new QStandardItem(QIcon(":/options/gfx/server.png"), name); // parent name
QStandardItem *itemhost = new QStandardItem(host); // parent host
QList<QStandardItem*> list;
list << itemname << itemhost;
pname->appendRow(list);
hostmap.insert(host, indexFromItem(itemname));
}
}
QString data = smgr.defaultServer(netlist[i]);
QString host = data.split('|')[0];
QStandardItem *pname = new QStandardItem(QIcon(":/options/gfx/network.png"), netlist[i]); // parent name
QStandardItem *phost = new QStandardItem(host); // parent host
QList<QStandardItem*> list;
list << pname << phost;
root->appendRow(list);
hostmap.insert(host, pname->index());
netmap.insert(netlist[i], pname->index());
QHash<QString,QString> sl = smgr.serverList(netlist[i]);
QHashIterator<QString,QString> sli(sl);
while (sli.hasNext()) {
sli.next();
// Key: Server name
// Value: host:port|pass
QString name = sli.key();
if (name == "DEFAULT")
continue; // The default value already taken care of, it's the address of parent item.
QString detail = sli.value();
QString host; // hostname with port, e.g. irc.network.org:6667
host = detail.split('|')[0];
if (host[0] == '$')
host = host.mid(1);
QStandardItem *itemname = new QStandardItem(QIcon(":/options/gfx/server.png"), name); // parent name
QStandardItem *itemhost = new QStandardItem(host); // parent host
QList<QStandardItem*> list;
list << itemname << itemhost;
pname->appendRow(list);
hostmap.insert(host, indexFromItem(itemname));
}
}
}
QStringList ServerModel::networkList()
{
return smgr.networkList();
return smgr.networkList();
}
QString ServerModel::details(QString name, QString network)
{
return smgr.getServerDetails(name, network);
return smgr.getServerDetails(name, network);
}

@ -23,21 +23,21 @@ class ServerModel : public QStandardItemModel
Q_OBJECT
public:
explicit ServerModel(QObject *parent = nullptr);
explicit ServerModel(QObject *parent = nullptr);
QModelIndex indexFromHost(QString hostname); // Hostname:Port
QPair<QString,QString> fromIndex(const QModelIndex& index);
QPair<QString,QString> fromIndex(const QModelIndex& index);
QModelIndex addNetwork(QString name, QString server);
QModelIndex addNetwork(QString name, QString server);
void setNetworkServer(QString name, QString server = "");
void renameNetwork(QString name, QString newname);
void delNetwork(QString name, bool keepServers);
QModelIndex addServer(QString name, QString server, QString network = "NONE");
void setServer(QString name, QString server, QString password, QString network = "NONE");
void delNetwork(QString name, bool keepServers);
QModelIndex addServer(QString name, QString server, QString network = "NONE");
void setServer(QString name, QString server, QString password, QString network = "NONE");
void renameServer(QString name, QString newname, QString network = "NONE");
void delServer(QString name, QString network = "NONE");
void resetModel();
QStringList networkList();
QString details(QString name, QString network = "NONE");
QStringList networkList();
QString details(QString name, QString network = "NONE");
private:
ServerMgr smgr;

@ -10,66 +10,66 @@
namespace Command {
namespace IRC {
constexpr auto* PASS = "PASS";
constexpr auto* NICK = "NICK";
constexpr auto* USER = "USER";
constexpr auto* OPER = "OPER";
constexpr auto* MODE = "MODE";
constexpr auto* QUIT = "QUIT";
constexpr auto* SQUIT = "SQUIT";
constexpr auto* JOIN = "JOIN";
constexpr auto* PART = "PART";
constexpr auto* TOPIC = "TOPIC";
constexpr auto* NAMES = "NAMES";
constexpr auto* LIST = "LIST";
constexpr auto* INVITE = "INVITE";
constexpr auto* KICK = "KICK";
constexpr auto* PRIVMSG = "PRIVMSG";
constexpr auto* NOTICE = "NOTICE";
constexpr auto* MOTD = "MOTD";
constexpr auto* LUSERS = "LUSERS";
constexpr auto* VERSION = "VERSION";
constexpr auto* STATS = "STATS";
constexpr auto* LINKS = "LINKS";
constexpr auto* TIME = "TIME";
constexpr auto* CONNECT = "CONNECT";
constexpr auto* TRACE = "TRACE";
constexpr auto* ADMIN = "ADMIN";
constexpr auto* INFO = "INFO";
constexpr auto* SERVLIST = "SERVLIST";
constexpr auto* SQUERY = "SQUERY";
constexpr auto* WHO = "WHO";
constexpr auto* WHOIS = "WHOIS";
constexpr auto* WHOWAS = "WHOWAS";
constexpr auto* KILL = "KILL";
constexpr auto* PING = "PING";
constexpr auto* PONG = "PONG";
constexpr auto* ERROR_ = "ERROR"; // Environment for msvc already defines ERROR as a macro of sorts...
constexpr auto* AWAY = "AWAY";
constexpr auto* REHASH = "REHASH";
constexpr auto* DIE = "DIE";
constexpr auto* RESTART = "RESTART";
constexpr auto* SUMMON = "SUMMON";
constexpr auto* USERS = "USERS";
constexpr auto* WALLOPS = "WALLOPS";
constexpr auto* USERHOST = "USERHOST";
constexpr auto* ISON = "ISON";
constexpr auto* PASS = "PASS";
constexpr auto* NICK = "NICK";
constexpr auto* USER = "USER";
constexpr auto* OPER = "OPER";
constexpr auto* MODE = "MODE";
constexpr auto* QUIT = "QUIT";
constexpr auto* SQUIT = "SQUIT";
constexpr auto* JOIN = "JOIN";
constexpr auto* PART = "PART";
constexpr auto* TOPIC = "TOPIC";
constexpr auto* NAMES = "NAMES";
constexpr auto* LIST = "LIST";
constexpr auto* INVITE = "INVITE";
constexpr auto* KICK = "KICK";
constexpr auto* PRIVMSG = "PRIVMSG";
constexpr auto* NOTICE = "NOTICE";
constexpr auto* MOTD = "MOTD";
constexpr auto* LUSERS = "LUSERS";
constexpr auto* VERSION = "VERSION";
constexpr auto* STATS = "STATS";
constexpr auto* LINKS = "LINKS";
constexpr auto* TIME = "TIME";
constexpr auto* CONNECT = "CONNECT";
constexpr auto* TRACE = "TRACE";
constexpr auto* ADMIN = "ADMIN";
constexpr auto* INFO = "INFO";
constexpr auto* SERVLIST = "SERVLIST";
constexpr auto* SQUERY = "SQUERY";
constexpr auto* WHO = "WHO";
constexpr auto* WHOIS = "WHOIS";
constexpr auto* WHOWAS = "WHOWAS";
constexpr auto* KILL = "KILL";
constexpr auto* PING = "PING";
constexpr auto* PONG = "PONG";
constexpr auto* ERROR_ = "ERROR"; // Environment for msvc already defines ERROR as a macro of sorts...
constexpr auto* AWAY = "AWAY";
constexpr auto* REHASH = "REHASH";
constexpr auto* DIE = "DIE";
constexpr auto* RESTART = "RESTART";
constexpr auto* SUMMON = "SUMMON";
constexpr auto* USERS = "USERS";
constexpr auto* WALLOPS = "WALLOPS";
constexpr auto* USERHOST = "USERHOST";
constexpr auto* ISON = "ISON";
}
namespace Extension {
constexpr auto* ACCOUNT = "ACCOUNT";
constexpr auto* ACCOUNT = "ACCOUNT";
}
namespace IRCv3 {
constexpr auto* CAP = "CAP";
constexpr auto* LS = "LS";
constexpr auto* LIST = "LIST";
constexpr auto* REQ = "REQ";
constexpr auto* ACK = "ACK";
constexpr auto* NAK = "NAK";
constexpr auto* END = "END";
constexpr auto* NEW = "NEW";
constexpr auto* DEL = "DEL";
constexpr auto* CAP = "CAP";
constexpr auto* LS = "LS";
constexpr auto* LIST = "LIST";
constexpr auto* REQ = "REQ";
constexpr auto* ACK = "ACK";
constexpr auto* NAK = "NAK";
constexpr auto* END = "END";
constexpr auto* NEW = "NEW";
constexpr auto* DEL = "DEL";
} // namespace IRCv3
} // namespace command

@ -14,201 +14,201 @@ using asio::ip::tcp;
struct DCCPriv
{
DCCPriv(DCC& super_, IRCBase& ircctx_, DCC::Direction dir_, asio::io_context& ioctx_, const std::string& ip_, const std::string& port_)
: super(super_)
, ircctx(ircctx_)
, direction(dir_)
, ioctx(ioctx_)
, ip(ip_)
, port(port_)
, resolver(ioctx_)
, acceptor(ioctx_)
, endpoint(tcp::v4(), std::stoi(port_))
, socket(ioctx_)
{}
DCC& super;
IRCBase& ircctx;
bool pending{ true };
const DCC::Direction direction;
asio::io_context& ioctx;
std::string ip; // Only used when Direction=Target, and describes the Initiator's IP.
std::string port;
tcp::resolver resolver; // for outbound connection (we act as a Target)
tcp::acceptor acceptor; // for inbound connection (we act as an Initiator)
tcp::resolver::results_type resolverResults;
asio::ip::tcp::endpoint endpoint;
tcp::socket socket;
asio::streambuf readbuf;
DCC::CallbackRead cbRead;
DCC::CallbackConnected cbCon;
DCC::CallbackDisconnected cbDiscon;
void connected(const asio::error_code& ec)
{
super.onConnected();
asio::streambuf::mutable_buffers_type mutableBuf{ readbuf.prepare(16384) };
socket.async_receive(mutableBuf,
[this](const asio::error_code& ec, std::size_t rl){
readbuf.commit(rl);
read(ec);
});
}
void disconnected(const asio::error_code& ec)
{
super.onDisconnected(SystemErrorToIRCError(ec));
}
void read(const asio::error_code& ec)
{
if (ec) {
disconnected(ec);
return;
}
std::istream is(&readbuf);
super.onRead(is);
asio::streambuf::mutable_buffers_type mutableBuf{ readbuf.prepare(16384) };
socket.async_receive(mutableBuf,
[this](const asio::error_code& ec, std::size_t rl){
readbuf.commit(rl);
read(ec);
});
}
void writeFinished(const asio::error_code& ec, std::size_t bytes_transferred)
{
std::cout << "DCC wrote " << bytes_transferred << " bytes." << std::endl;
if (!ec) return; // No errors.
std::cerr << fmt::format("'- DCC write error: {} ({})", SystemErrorToIRCError(ec), ec.message()) << std::endl;
}
DCCPriv(DCC& super_, IRCBase& ircctx_, DCC::Direction dir_, asio::io_context& ioctx_, const std::string& ip_, const std::string& port_)
: super(super_)
, ircctx(ircctx_)
, direction(dir_)
, ioctx(ioctx_)
, ip(ip_)
, port(port_)
, resolver(ioctx_)
, acceptor(ioctx_)
, endpoint(tcp::v4(), std::stoi(port_))
, socket(ioctx_)
{}
DCC& super;
IRCBase& ircctx;
bool pending{ true };
const DCC::Direction direction;
asio::io_context& ioctx;
std::string ip; // Only used when Direction=Target, and describes the Initiator's IP.
std::string port;
tcp::resolver resolver; // for outbound connection (we act as a Target)
tcp::acceptor acceptor; // for inbound connection (we act as an Initiator)
tcp::resolver::results_type resolverResults;
asio::ip::tcp::endpoint endpoint;
tcp::socket socket;
asio::streambuf readbuf;
DCC::CallbackRead cbRead;
DCC::CallbackConnected cbCon;
DCC::CallbackDisconnected cbDiscon;
void connected(const asio::error_code& ec)
{
super.onConnected();
asio::streambuf::mutable_buffers_type mutableBuf{ readbuf.prepare(16384) };
socket.async_receive(mutableBuf,
[this](const asio::error_code& ec, std::size_t rl){
readbuf.commit(rl);
read(ec);
});
}
void disconnected(const asio::error_code& ec)
{
super.onDisconnected(SystemErrorToIRCError(ec));
}
void read(const asio::error_code& ec)
{
if (ec) {
disconnected(ec);
return;
}
std::istream is(&readbuf);
super.onRead(is);
asio::streambuf::mutable_buffers_type mutableBuf{ readbuf.prepare(16384) };
socket.async_receive(mutableBuf,
[this](const asio::error_code& ec, std::size_t rl){
readbuf.commit(rl);
read(ec);
});
}
void writeFinished(const asio::error_code& ec, std::size_t bytes_transferred)
{
std::cout << "DCC wrote " << bytes_transferred << " bytes." << std::endl;
if (!ec) return; // No errors.
std::cerr << fmt::format("'- DCC write error: {} ({})", SystemErrorToIRCError(ec), ec.message()) << std::endl;
}
};
DCC::DCC(IRCBase& ircctx, asio::io_context& ioctx, const std::string& port)
: mp(std::make_unique<DCCPriv>(*this, ircctx, Direction::Initiator, ioctx, "", port))
: mp(std::make_unique<DCCPriv>(*this, ircctx, Direction::Initiator, ioctx, "", port))
{
mp->acceptor.open(mp->endpoint.protocol());
mp->acceptor.bind(mp->endpoint);
mp->acceptor.listen(1);
mp->acceptor.async_accept(mp->socket, [this](const asio::error_code& ec){
if (ec)
mp->disconnected(ec);
else
mp->connected(ec);
});
mp->acceptor.open(mp->endpoint.protocol());
mp->acceptor.bind(mp->endpoint);
mp->acceptor.listen(1);
mp->acceptor.async_accept(mp->socket, [this](const asio::error_code& ec){
if (ec)
mp->disconnected(ec);
else
mp->connected(ec);
});
}
DCC::DCC(IRCBase& ircctx, asio::io_context& ioctx, const std::string& ip, const std::string& port)
: mp(std::make_unique<DCCPriv>(*this, ircctx, Direction::Target, ioctx, ip, port))
: mp(std::make_unique<DCCPriv>(*this, ircctx, Direction::Target, ioctx, ip, port))
{}
DCC::DCC(DCC&& other) noexcept
: mp(std::move(other.mp))
: mp(std::move(other.mp))
{}
DCC::~DCC()
{
try {
if (mp->socket.is_open()) {
mp->socket.shutdown(asio::socket_base::shutdown_both);
mp->socket.close();
}
if (mp->direction == Direction::Initiator)
mp->acceptor.close();
}
catch(const asio::system_error& e) {
// Properly log e.what() or e.code() maybe?
std::cerr << fmt::format("~DCC() caught exception ({}): {}\n IP={}, Port={}, Direction={}",
e.code().value(), e.what(), mp->ip, mp->port, mp->direction) << std::endl;
}
try {
if (mp->socket.is_open()) {
mp->socket.shutdown(asio::socket_base::shutdown_both);
mp->socket.close();
}
if (mp->direction == Direction::Initiator)
mp->acceptor.close();
}
catch(const asio::system_error& e) {
// Properly log e.what() or e.code() maybe?
std::cerr << fmt::format("~DCC() caught exception ({}): {}\n IP={}, Port={}, Direction={}",
e.code().value(), e.what(), mp->ip, mp->port, mp->direction) << std::endl;
}
}
bool DCC::isPending() const
{
return mp->pending;
return mp->pending;
}
IRCError DCC::accept()
{
if (mp->direction == Direction::Initiator)
return IRCError::DCC_NotATarget; // Only usable if we are a Target...
if (mp->direction == Direction::Initiator)
return IRCError::DCC_NotATarget; // Only usable if we are a Target...
mp->resolverResults = mp->resolver.resolve(mp->ip, mp->port);
mp->resolverResults = mp->resolver.resolve(mp->ip, mp->port);
asio::async_connect(mp->socket, mp->resolverResults,
[this](const asio::error_code& ec, const tcp::endpoint&) {
if (ec)
mp->disconnected(ec);
else
mp->connected(ec);
});
asio::async_connect(mp->socket, mp->resolverResults,
[this](const asio::error_code& ec, const tcp::endpoint&) {
if (ec)
mp->disconnected(ec);
else
mp->connected(ec);
});
mp->pending = false;
return IRCError::NoError;
mp->pending = false;
return IRCError::NoError;
}
DCC::Direction DCC::direction() const
{
return mp->direction;
return mp->direction;
}
void DCC::write(const ByteString& data)
{
mp->socket.async_send(asio::buffer(data),
[this](const asio::error_code& ec, std::size_t bytes_transferred){
mp->writeFinished(ec, bytes_transferred);
});
mp->socket.async_send(asio::buffer(data),
[this](const asio::error_code& ec, std::size_t bytes_transferred){
mp->writeFinished(ec, bytes_transferred);
});
}
IRCBase& DCC::context()
{
return mp->ircctx;
return mp->ircctx;
}
const IRCBase& DCC::context() const
{
return mp->ircctx;
return mp->ircctx;
}
void DCC::callbackRead(DCC::CallbackRead&& cb)
{
mp->cbRead = std::move(cb);
mp->cbRead = std::move(cb);
}
void DCC::callbackConnected(DCC::CallbackConnected&& cb)
{
mp->cbCon = std::move(cb);
mp->cbCon = std::move(cb);
}
void DCC::callbackDisconnected(DCC::CallbackDisconnected&& cb)
{
mp->cbDiscon = std::move(cb);
mp->cbDiscon = std::move(cb);
}
void DCC::onRead(std::istream& is)
{
if (mp->cbRead)
mp->cbRead(is);
if (mp->cbRead)
mp->cbRead(is);
}
void DCC::onConnected()
{
if (mp->cbCon)
mp->cbCon();
if (mp->cbCon)
mp->cbCon();
}
void DCC::onDisconnected(IRCError e)
{
if (mp->cbDiscon)
mp->cbDiscon(e);
if (mp->cbDiscon)
mp->cbDiscon(e);
}

@ -20,61 +20,61 @@ class IRCBase;
struct DCCPriv;
class DCC
{
friend struct IRCBasePriv;
friend struct DCCPriv;
friend struct IRCBasePriv;
friend struct DCCPriv;
public:
using CallbackRead = std::function<void(std::istream&)>;
using CallbackConnected = std::function<void()>;
using CallbackDisconnected = std::function<void(IRCError e)>;
using ByteString = std::basic_string<std::byte>;
using CallbackRead = std::function<void(std::istream&)>;
using CallbackConnected = std::function<void()>;
using CallbackDisconnected = std::function<void(IRCError e)>;
using ByteString = std::basic_string<std::byte>;
/*
* Initiator acts as the server, and the target is the client
*/
enum class Direction {
Initiator,
Target
};
/*
* Initiator acts as the server, and the target is the client
*/
enum class Direction {
Initiator,
Target
};
~DCC();
DCC(DCC&& other) noexcept;
~DCC();
DCC(DCC&& other) noexcept;
//! Constructs an Initiator (server)
DCC(IRCBase& ircctx, asio::io_context& ioctx, const std::string& port);
//! Constructs an Initiator (server)
DCC(IRCBase& ircctx, asio::io_context& ioctx, const std::string& port);
//! Constructs a Target (client)
DCC(IRCBase& ircctx, asio::io_context& ioctx, const std::string& ip, const std::string& port);
//! Constructs a Target (client)
DCC(IRCBase& ircctx, asio::io_context& ioctx, const std::string& ip, const std::string& port);
[[nodiscard]] bool isPending() const;
IRCError accept();
[[nodiscard]] bool isPending() const;
IRCError accept();
[[nodiscard]] Direction direction() const;
[[nodiscard]] Direction direction() const;
void write(const ByteString& data);
void write(const ByteString& data);
IRCBase& context();
const IRCBase& context() const;
IRCBase& context();
const IRCBase& context() const;
/*
* Callbacks used by 'users' of this class.
*/
void callbackRead(CallbackRead&& cb);
void callbackConnected(CallbackConnected&& cb);
void callbackDisconnected(CallbackDisconnected&& cb);
/*
* Callbacks used by 'users' of this class.
*/
void callbackRead(CallbackRead&& cb);
void callbackConnected(CallbackConnected&& cb);
void callbackDisconnected(CallbackDisconnected&& cb);
protected:
/*
* Use these to read data in a custom derivative of DCC.
* If you re-implement any of these, make sure to forward the
* calls to this base class at the end of your function.
*/
virtual void onRead(std::istream& is);
virtual void onConnected();
virtual void onDisconnected(IRCError e);
/*
* Use these to read data in a custom derivative of DCC.
* If you re-implement any of these, make sure to forward the
* calls to this base class at the end of your function.
*/
virtual void onRead(std::istream& is);
virtual void onConnected();
virtual void onDisconnected(IRCError e);
private:
std::unique_ptr<DCCPriv> mp;
std::unique_ptr<DCCPriv> mp;
};
#endif // DCC_H

@ -20,125 +20,125 @@
#include <iostream>
IRCBase::IRCBase()
: mp(new IRCBasePriv(*this))
: mp(new IRCBasePriv(*this))
{}
IRCBase::IRCBase(IRCBase&& other) noexcept
: mp(std::move(other.mp))
: mp(std::move(other.mp))
{}
IRCBase::~IRCBase() = default;
bool IRCBase::poll()
{
asio::error_code ioctxerr;
bool ioDidSomething = false;
try {
ioDidSomething = mp->ioctx.poll_one(ioctxerr) > 0;
}
catch (const std::system_error& e) {
mp->lastError = SystemErrorToIRCError(e);
mp->disconnectHandler();
return false;
}
asio::error_code ioctxerr;
bool ioDidSomething = false;
try {
ioDidSomething = mp->ioctx.poll_one(ioctxerr) > 0;
}
catch (const std::system_error& e) {
mp->lastError = SystemErrorToIRCError(e);
mp->disconnectHandler();
return false;
}
if (ioctxerr.value() != 0)
std::cout << fmt::format("IOCTX err {}: {}", ioctxerr.value(), ioctxerr.message()) << std::endl;
return ioDidSomething;
if (ioctxerr.value() != 0)
std::cout << fmt::format("IOCTX err {}: {}", ioctxerr.value(), ioctxerr.message()) << std::endl;
return ioDidSomething;
}
const std::string& IRCBase::getHostname() const
{
return mp->hostname;
return mp->hostname;
}
IRCError IRCBase::setHostname(const std::string& hostname, bool SSL)
{
if (isOnline())
return IRCError::CannotChangeWhenConnected;
mp->useSSL = SSL;
mp->hostname = hostname;
return IRCError::NoError;
if (isOnline())
return IRCError::CannotChangeWhenConnected;
mp->useSSL = SSL;
mp->hostname = hostname;
return IRCError::NoError;
}
const std::string& IRCBase::getPort() const
{
return mp->port;
return mp->port;
}
IRCError IRCBase::setPort(const std::string& port)
{
if (isOnline())
return IRCError::CannotChangeWhenConnected;
mp->port = port;
return IRCError::NoError;
if (isOnline())
return IRCError::CannotChangeWhenConnected;
mp->port = port;
return IRCError::NoError;
}
const std::string& IRCBase::getRealname() const
{
return mp->realname;
return mp->realname;
}
IRCError IRCBase::setRealname(const std::string& realname)
{
if (isOnline())
return IRCError::CannotChangeWhenConnected;
mp->realname = realname;
return IRCError::NoError;
if (isOnline())
return IRCError::CannotChangeWhenConnected;
mp->realname = realname;
return IRCError::NoError;
}
const std::string& IRCBase::getIdent() const
{
return mp->ident;
return mp->ident;
}
IRCError IRCBase::setIdent(const std::string& ident)
{
if (isOnline())
return IRCError::CannotChangeWhenConnected;
mp->ident = ident;
return IRCError::NoError;
if (isOnline())
return IRCError::CannotChangeWhenConnected;
mp->ident = ident;
return IRCError::NoError;
}
const std::string& IRCBase::getNickname() const
{
return mp->nickname;
return mp->nickname;
}
void IRCBase::setNickname(const std::string& nickname)
{
if (mp->isConnected) {
if (mp->isConnected) {
if (!isOnline())
mp->nickname = nickname;
mp->write(Command::IRC::NICK, nickname);
}
else
mp->nickname = nickname;
else
mp->nickname = nickname;
}
const std::string& IRCBase::getPassword() const
{
return mp->password;
return mp->password;
}
void IRCBase::setPassword(const std::string& password)
{
mp->password = password;
mp->password = password;
}
void IRCBase::exceptSSL_SelfSigned(bool except)
{
mp->sslExcept.selfSigned = except;
mp->sslExcept.selfSigned = except;
}
void IRCBase::exceptSSL_CNMismatch(bool except)
{
mp->sslExcept.CNMismactch = except;
mp->sslExcept.CNMismactch = except;
}
void IRCBase::exceptSSL_Expired(bool except)
{
mp->sslExcept.expired = except;
mp->sslExcept.expired = except;
}
void IRCBase::command(const std::string& command, const std::vector<std::string>& args, const std::string& msg)
@ -146,10 +146,10 @@ void IRCBase::command(const std::string& command, const std::vector<std::string>
if (!mp->isConnected)
return;
if (msg.empty())
mp->writeNoMsg(command, args);
else
mp->write(command, args, msg);
if (msg.empty())
mp->writeNoMsg(command, args);
else
mp->write(command, args, msg);
}
void IRCBase::command(const std::string& command, const std::string& msg)
@ -157,7 +157,7 @@ void IRCBase::command(const std::string& command, const std::string& msg)
if (!mp->isConnected)
return;
mp->write(command, msg);
mp->write(command, msg);
}
void IRCBase::raw(const std::string& data)
@ -165,7 +165,7 @@ void IRCBase::raw(const std::string& data)
if (!mp->isConnected)
return;
mp->writeNoMsg(data, {});
mp->writeNoMsg(data, {});
}
void IRCBase::ctcpRequest(const std::string& target, const std::string& command, const std::string& message)
@ -180,54 +180,54 @@ void IRCBase::ctcpResponse(const std::string& target, const std::string& command
void IRCBase::setManualKeepalive(std::chrono::seconds freq)
{
mp->keepaliveFreq = freq;
if (mp->isOnline) {
if (freq > std::chrono::seconds(0))
mp->startKeepaliveTimer();
else
mp->stopKeepaliveTimer();
}
mp->keepaliveFreq = freq;
if (mp->isOnline) {
if (freq > std::chrono::seconds(0))
mp->startKeepaliveTimer();
else
mp->stopKeepaliveTimer();
}
}
std::chrono::milliseconds IRCBase::getManualKeepaliveFreq() const
{
return mp->keepaliveFreq;
return mp->keepaliveFreq;
}
asio::io_context& IRCBase::getIOCTX()
{
return mp->ioctx;
return mp->ioctx;
}
std::string IRCBase::toMemberPrefix(const std::string& modes) const
{ // PREFIX=(qaohv)~&@%+
const auto& prefixdef = mp->isupport.at("PREFIX"); // PREFIX is always present.
const auto& prefixdef = mp->isupport.at("PREFIX"); // PREFIX is always present.
const auto prefixmodes = prefixdef.substr(1, prefixdef.find_first_of(')') - 1);
const auto prefix = prefixdef.substr(prefixdef.find_first_of(')') + 1);
const auto prefixmodes = prefixdef.substr(1, prefixdef.find_first_of(')') - 1);
const auto prefix = prefixdef.substr(prefixdef.find_first_of(')') + 1);
std::string ret;
std::string ret;
for (const char m : modes) {
// Only accept a-zA-Z
if (!(m > 'a' && m < 'z' || m > 'A' && m < 'Z')) { // De Morgan pls halp
ret += ':';
continue;
}
for (const char m : modes) {
// Only accept a-zA-Z
if (!(m > 'a' && m < 'z' || m > 'A' && m < 'Z')) { // De Morgan pls halp
ret += ':';
continue;
}
const auto ctpos = prefixmodes.find_first_of(m);
if (ctpos == std::string::npos)
ret += ':';
else
ret += prefix[ctpos];
}
const auto ctpos = prefixmodes.find_first_of(m);
if (ctpos == std::string::npos)
ret += ':';
else
ret += prefix[ctpos];
}
return ret;
return ret;
}
bool IRCBase::isChannelSymbol(char c)
{
return mp->isChannelSymbol(c);
return mp->isChannelSymbol(c);
}
char IRCBase::channelModeGroup(char m) const
@ -237,180 +237,180 @@ char IRCBase::channelModeGroup(char m) const
IRCError IRCBase::tryConnect()
{
if (isOnline())
return IRCError::AlreadyConnected;
if (isOnline())
return IRCError::AlreadyConnected;
if (mp->hostname.empty())
return IRCError::HostNotSet;
if (mp->hostname.empty())
return IRCError::HostNotSet;
if (mp->port.empty())
return IRCError::PortNotSet;
if (mp->port.empty())
return IRCError::PortNotSet;
if (mp->ident.empty())
return IRCError::IdentNotSet;
if (mp->ident.empty())
return IRCError::IdentNotSet;
if (mp->nickname.empty())
return IRCError::NicknameNotSet;
if (mp->nickname.empty())
return IRCError::NicknameNotSet;
if (mp->realname.empty())
return IRCError::RealnameNotSet;
if (mp->realname.empty())
return IRCError::RealnameNotSet;
try {
try {
mp->sslExceptions.clear();
mp->ioctx.restart();
mp->endpoints = mp->resolver.resolve(mp->hostname, mp->port);
if (mp->endpoints.empty())
return IRCError::CannotResolveAddress;
if (mp->useSSL) {
mp->sslsock.emplace(mp->ioctx, mp->sslctx);
mp->sslsock->set_verify_mode(asio::ssl::verify_peer);
mp->sslsock->set_verify_callback([this](bool preverified, asio::ssl::verify_context& vc){
if (preverified)
return true;
X509* cert = X509_STORE_CTX_get_current_cert(vc.native_handle());
mp->lastError = mp->verify_X509(cert);
if (mp->lastError != IRCError::NoError) {
onConnectionError(mp->lastError);
return false;
}
else
return true;
});
asio::async_connect(mp->sslsock->lowest_layer(), mp->endpoints,
[this](const asio::error_code& ec, const tcp::endpoint&) {
std::cout << "ssl connect error: " << ec.value() << " " << ec.message() << std::endl;
mp->lastError = SystemErrorToIRCError(ec);
if (ec) {
onConnectionError(mp->lastError);
return;
}
mp->sslsock->async_handshake(asio::ssl::stream_base::client,
[this](asio::error_code ec) {
mp->lastError = SystemErrorToIRCError(ec);
if (ec) {
onConnectionError(mp->lastError);
return;
}
// TODO error codes!
std::cout << "ssl handshake error: " << ec.value() << " " << ec.message() << std::endl;
mp->connected(ec);
});
});
}
else {
mp->sock.emplace(mp->ioctx);
asio::async_connect(mp->sock.value(), mp->endpoints,
[this](const asio::error_code& ec, const tcp::endpoint&) {
mp->lastError = SystemErrorToIRCError(ec);
if (ec) {
onConnectionError(mp->lastError);
return;
}
mp->connected(ec);
}
);
}
}
catch (const std::system_error& e) {
return SystemErrorToIRCError(e);
}
return IRCError::NoError;
mp->ioctx.restart();
mp->endpoints = mp->resolver.resolve(mp->hostname, mp->port);
if (mp->endpoints.empty())
return IRCError::CannotResolveAddress;
if (mp->useSSL) {
mp->sslsock.emplace(mp->ioctx, mp->sslctx);
mp->sslsock->set_verify_mode(asio::ssl::verify_peer);
mp->sslsock->set_verify_callback([this](bool preverified, asio::ssl::verify_context& vc){
if (preverified)
return true;
X509* cert = X509_STORE_CTX_get_current_cert(vc.native_handle());
mp->lastError = mp->verify_X509(cert);
if (mp->lastError != IRCError::NoError) {
onConnectionError(mp->lastError);
return false;
}
else
return true;
});
asio::async_connect(mp->sslsock->lowest_layer(), mp->endpoints,
[this](const asio::error_code& ec, const tcp::endpoint&) {
std::cout << "ssl connect error: " << ec.value() << " " << ec.message() << std::endl;
mp->lastError = SystemErrorToIRCError(ec);
if (ec) {
onConnectionError(mp->lastError);
return;
}
mp->sslsock->async_handshake(asio::ssl::stream_base::client,
[this](asio::error_code ec) {
mp->lastError = SystemErrorToIRCError(ec);
if (ec) {
onConnectionError(mp->lastError);
return;
}
// TODO error codes!
std::cout << "ssl handshake error: " << ec.value() << " " << ec.message() << std::endl;
mp->connected(ec);
});
});
}
else {
mp->sock.emplace(mp->ioctx);
asio::async_connect(mp->sock.value(), mp->endpoints,
[this](const asio::error_code& ec, const tcp::endpoint&) {
mp->lastError = SystemErrorToIRCError(ec);
if (ec) {
onConnectionError(mp->lastError);
return;
}
mp->connected(ec);
}
);
}
}
catch (const std::system_error& e) {
return SystemErrorToIRCError(e);
}
return IRCError::NoError;
}
IRCError IRCBase::disconnectFromServer(const std::string& quitMessage)
{
if (!mp->isConnected)
return IRCError::NotConnected;
if (!mp->isConnected)
return IRCError::NotConnected;
if (mp->isOnline) {
mp->write(Command::IRC::QUIT, quitMessage);
// TODO Quit timeout; if we never get disconnected by server, we forcefully must do so.
}
else {
if (mp->useSSL)
mp->sslsock->lowest_layer().close();
else
mp->sock->close();
}
if (mp->isOnline) {
mp->write(Command::IRC::QUIT, quitMessage);
// TODO Quit timeout; if we never get disconnected by server, we forcefully must do so.
}
else {
if (mp->useSSL)
mp->sslsock->lowest_layer().close();
else
mp->sock->close();
}
if (mp->keepaliveFreq > std::chrono::seconds(0))
mp->stopKeepaliveTimer();
if (mp->keepaliveFreq > std::chrono::seconds(0))
mp->stopKeepaliveTimer();
return IRCError::NoError;
return IRCError::NoError;
}
IRCError IRCBase::lastErrorCode() const
{
return mp->lastError;
return mp->lastError;
}
bool IRCBase::isOnline() const
{
return mp->isOnline;
return mp->isOnline;
}
bool IRCBase::isConnected() const
{
return mp->isConnected;
return mp->isConnected;
}
bool IRCBase::isSSL() const
{
return mp->useSSL;
return mp->useSSL;
}
const std::vector<std::string>& IRCBase::clientV3Support()
{
return V3Support;
return V3Support;
}
const std::vector<std::string>& IRCBase::registeredV3Support() const
{
return mp->registeredV3support;
return mp->registeredV3support;
}
const std::vector<std::string>& IRCBase::serverV3Support() const
{
return mp->serverV3support;
return mp->serverV3support;
}
const std::unordered_map<std::string, std::string>& IRCBase::isupport() const
{
return mp->isupport;
return mp->isupport;
}
const std::vector<std::shared_ptr<IRCChannel>>& IRCBase::channels() const
{
return mp->channels;
return mp->channels;
}
std::shared_ptr<IRCChannel> IRCBase::getChannel(const std::string& name) const
{
for (auto chanp : mp->channels)
if (strEquals(chanp->name(), name))
return chanp;
return nullptr;
for (auto chanp : mp->channels)
if (strEquals(chanp->name(), name))
return chanp;
return nullptr;
}
std::shared_ptr<IRCMember> IRCBase::getMember(const std::string& nickname) const
{
for (auto memp : mp->allMembers)
if (strEquals(memp->prefix().toString(), nickname))
return memp;
return nullptr;
for (auto memp : mp->allMembers)
if (strEquals(memp->prefix().toString(), nickname))
return memp;
return nullptr;
}
std::pair<std::shared_ptr<DCC>, IRCError> IRCBase::initiateDCC(const std::string& /*port*/)
{
return std::pair<std::shared_ptr<DCC>, IRCError>();
return std::pair<std::shared_ptr<DCC>, IRCError>();
}
IRCError IRCBase::declineDCC(std::shared_ptr<DCC> /*dcc*/)
{
return IRCError::NetworkUnreachable;
return IRCError::NetworkUnreachable;
}

@ -25,61 +25,61 @@ class DCC;
struct IRCBasePriv;
class IRCBase
{
friend struct IRCBasePriv;
friend struct IRCBasePriv;
public:
IRCBase();
~IRCBase();
IRCBase();
~IRCBase();
IRCBase(IRCBase&& other) noexcept;
IRCBase(const IRCBase&) = delete;
IRCBase& operator=(const IRCBase&) = delete;
IRCBase(IRCBase&& other) noexcept;
IRCBase(const IRCBase&) = delete;
IRCBase& operator=(const IRCBase&) = delete;
bool poll();
bool poll();
[[nodiscard]] const std::string& getHostname() const;
IRCError setHostname(const std::string& hostname, bool SSL = false);
[[nodiscard]] const std::string& getHostname() const;
IRCError setHostname(const std::string& hostname, bool SSL = false);
[[nodiscard]] const std::string& getPort() const;
IRCError setPort(const std::string& port);
[[nodiscard]] const std::string& getPort() const;
IRCError setPort(const std::string& port);
[[nodiscard]] const std::string& getRealname() const;
IRCError setRealname(const std::string& realname);
[[nodiscard]] const std::string& getRealname() const;
IRCError setRealname(const std::string& realname);
[[nodiscard]] const std::string& getIdent() const;
IRCError setIdent(const std::string& ident);
[[nodiscard]] const std::string& getIdent() const;
IRCError setIdent(const std::string& ident);
[[nodiscard]] const std::string& getNickname() const;
void setNickname(const std::string& nickname);
[[nodiscard]] const std::string& getNickname() const;
void setNickname(const std::string& nickname);
[[nodiscard]] const std::string& getPassword() const;
void setPassword(const std::string& password);
[[nodiscard]] const std::string& getPassword() const;
void setPassword(const std::string& password);
void exceptSSL_SelfSigned(bool except);
void exceptSSL_CNMismatch(bool except);
void exceptSSL_Expired(bool except);
void exceptSSL_SelfSigned(bool except);
void exceptSSL_CNMismatch(bool except);
void exceptSSL_Expired(bool except);
void command(const std::string& command, const std::vector<std::string>& args, const std::string& msg = "");
void command(const std::string& command, const std::string& msg);
void raw(const std::string& data);
void command(const std::string& command, const std::vector<std::string>& args, const std::string& msg = "");
void command(const std::string& command, const std::string& msg);
void raw(const std::string& data);
void ctcpRequest(const std::string& target, const std::string& command, const std::string& message = "");
void ctcpRequest(const std::string& target, const std::string& command, const std::string& message = "");
void ctcpResponse(const std::string& target, const std::string& command, const std::string& message = "");
//! Set to >0 to enable, =0 to disable.
void setManualKeepalive(std::chrono::seconds freq = std::chrono::seconds(30));
//! Set to >0 to enable, =0 to disable.
void setManualKeepalive(std::chrono::seconds freq = std::chrono::seconds(30));
[[nodiscard]] std::chrono::milliseconds getManualKeepaliveFreq() const;
[[nodiscard]] std::chrono::milliseconds getManualKeepaliveFreq() const;
[[nodiscard]] asio::io_context& getIOCTX();
[[nodiscard]] asio::io_context& getIOCTX();
/**
* Takes a string of modes (letters like ohv), in their intended order
* and converts each letter to its corresponding display prefix (ie. @%+).
* For mode (letters) that isn't valid, a colon ':' is placed in-stead (since
* those can't naturally exist with the IRC protocol.)
*/
[[nodiscard]] std::string toMemberPrefix(const std::string& modes) const;
/**
* Takes a string of modes (letters like ohv), in their intended order
* and converts each letter to its corresponding display prefix (ie. @%+).
* For mode (letters) that isn't valid, a colon ':' is placed in-stead (since
* those can't naturally exist with the IRC protocol.)
*/
[[nodiscard]] std::string toMemberPrefix(const std::string& modes) const;
bool isChannelSymbol(char c);
@ -97,81 +97,81 @@ public:
*/
[[nodiscard]] char channelModeGroup(char m) const;
[[nodiscard]] IRCError tryConnect();
[[nodiscard]] IRCError disconnectFromServer(const std::string& quitMessage = "");
[[nodiscard]] IRCError lastErrorCode() const;
[[nodiscard]] IRCError tryConnect();
[[nodiscard]] IRCError disconnectFromServer(const std::string& quitMessage = "");
[[nodiscard]] IRCError lastErrorCode() const;
[[nodiscard]] bool isOnline() const;
[[nodiscard]] bool isConnected() const;
[[nodiscard]] bool isSSL() const;
[[nodiscard]] bool isOnline() const;
[[nodiscard]] bool isConnected() const;
[[nodiscard]] bool isSSL() const;
[[nodiscard]] static const std::vector<std::string>& clientV3Support();
[[nodiscard]] const std::vector<std::string>& registeredV3Support() const;
[[nodiscard]] const std::vector<std::string>& serverV3Support() const;
[[nodiscard]] const std::unordered_map<std::string,std::string>& isupport() const;
[[nodiscard]] static const std::vector<std::string>& clientV3Support();
[[nodiscard]] const std::vector<std::string>& registeredV3Support() const;
[[nodiscard]] const std::vector<std::string>& serverV3Support() const;
[[nodiscard]] const std::unordered_map<std::string,std::string>& isupport() const;
[[nodiscard]] const std::vector<std::shared_ptr<IRCChannel>>& channels() const;
[[nodiscard]] std::shared_ptr<IRCChannel> getChannel(const std::string& name) const;
[[nodiscard]] std::shared_ptr<IRCMember> getMember(const std::string& nickname) const;
[[nodiscard]] const std::vector<std::shared_ptr<IRCChannel>>& channels() const;
[[nodiscard]] std::shared_ptr<IRCChannel> getChannel(const std::string& name) const;
[[nodiscard]] std::shared_ptr<IRCMember> getMember(const std::string& nickname) const;
std::pair<std::shared_ptr<DCC>, IRCError> initiateDCC(const std::string& port); // Creates a DCC initiator (tcp server)
IRCError declineDCC(std::shared_ptr<DCC> dcc);
std::pair<std::shared_ptr<DCC>, IRCError> initiateDCC(const std::string& port); // Creates a DCC initiator (tcp server)
IRCError declineDCC(std::shared_ptr<DCC> dcc);
protected:
virtual void onConnected() {}
virtual void onConnectedWithSSLExceptions(const std::vector<IRCError>& codes) {}
virtual void onDisconnected() {}
virtual void onRegistered() {}
virtual void onConnectionError(IRCError e) {}
/*
* To correctly implement and get all data from a standard IRC server,
* the following virtuals must be implemented.
*/
virtual void onMsgNick(const IRCPrefix& sender, const std::string& newNickname, const std::vector<std::string>& channelsAffected) = 0;
virtual void onMsgMode(const IRCPrefix& sender, const std::string& target, const std::string& modes, const std::vector<std::string>& args) = 0;
virtual void onMsgQuit(const IRCPrefix& sender, const std::string& message, const std::vector<std::string>& channelsAffected) = 0;
virtual void onMsgJoin(const IRCPrefix& sender, const std::string& target) = 0;
virtual void onMsgPart(const IRCPrefix& sender, const std::string& target, const std::string& message) = 0;
virtual void onMsgTopic(const IRCPrefix& sender, const std::string& target, const std::string& topic) = 0;
virtual void onMsgInvite(const IRCPrefix& sender, const std::string& target) = 0;
virtual void onMsgKick(const IRCPrefix& sender, const std::string& target, const std::string& who, const std::string& reason) = 0;
virtual void onMsgPrivmsg(const IRCPrefix& sender, const std::string& target, const std::string& message) = 0;
virtual void onMsgNotice(const IRCPrefix& sender, const std::string& target, const std::string& message) = 0;
virtual void onMsgKill(const IRCPrefix& sender, const std::string& reason) = 0;
virtual void onMsgPing(const std::string& message) = 0;
virtual void onMsgPong(const std::string& message) = 0;
virtual void onMsgError(const std::string& message) = 0;
virtual void onMsgWallops(const IRCPrefix& sender, const std::string& message) = 0;
virtual void onMsgNumeric(const IRCPrefix& sender, const std::string& num, const std::vector<std::string>& args, const std::string& message) = 0;
/*
* CTCP/DCC isn't really a part of the IRC standard so these are optional.
*/
virtual void onMsgCTCPRequest(const IRCPrefix& sender, const std::string& target, const std::string& command, const std::string& message) {};
virtual void onMsgCTCPResponse(const IRCPrefix& sender, const std::string& target, const std::string& command, const std::string& message) {};
virtual void onMsgDCCRequest(std::shared_ptr<DCC> dcc, const IRCPrefix& sender, const std::string& target, const std::string& type, const std::string& message) {};
/*
* Optional IRCv3 extensions to implement.
*/
virtual void v3onMsgAway(const IRCPrefix& sender, const std::string& message, const std::vector<std::string>& channelsAffected) {};
virtual void v3onMsgAccountLogin(const IRCPrefix& sender, const std::string& useraccount) {}
virtual void v3onMsgAccountLogout(const IRCPrefix& sender) {}
/*
* Required IRCv3 extensions to implement.
*/
virtual void v3onMsgJoin(const IRCPrefix& sender, const std::string& channel, const std::string& useraccount, const std::string& realname) = 0;
/*
* Catch-all handler. If the message parser cannot match for any of the commands, it will turn up here.
* Optional to implement.
*/
virtual void onMsgUnhandled(const IRCPrefix& sender, const std::string& command, const std::vector<std::string>& args, const std::string& message) {}
virtual void onConnected() {}
virtual void onConnectedWithSSLExceptions(const std::vector<IRCError>& codes) {}
virtual void onDisconnected() {}
virtual void onRegistered() {}
virtual void onConnectionError(IRCError e) {}
/*
* To correctly implement and get all data from a standard IRC server,
* the following virtuals must be implemented.
*/
virtual void onMsgNick(const IRCPrefix& sender, const std::string& newNickname, const std::vector<std::string>& channelsAffected) = 0;
virtual void onMsgMode(const IRCPrefix& sender, const std::string& target, const std::string& modes, const std::vector<std::string>& args) = 0;
virtual void onMsgQuit(const IRCPrefix& sender, const std::string& message, const std::vector<std::string>& channelsAffected) = 0;
virtual void onMsgJoin(const IRCPrefix& sender, const std::string& target) = 0;
virtual void onMsgPart(const IRCPrefix& sender, const std::string& target, const std::string& message) = 0;
virtual void onMsgTopic(const IRCPrefix& sender, const std::string& target, const std::string& topic) = 0;
virtual void onMsgInvite(const IRCPrefix& sender, const std::string& target) = 0;
virtual void onMsgKick(const IRCPrefix& sender, const std::string& target, const std::string& who, const std::string& reason) = 0;
virtual void onMsgPrivmsg(const IRCPrefix& sender, const std::string& target, const std::string& message) = 0;
virtual void onMsgNotice(const IRCPrefix& sender, const std::string& target, const std::string& message) = 0;
virtual void onMsgKill(const IRCPrefix& sender, const std::string& reason) = 0;
virtual void onMsgPing(const std::string& message) = 0;
virtual void onMsgPong(const std::string& message) = 0;
virtual void onMsgError(const std::string& message) = 0;
virtual void onMsgWallops(const IRCPrefix& sender, const std::string& message) = 0;
virtual void onMsgNumeric(const IRCPrefix& sender, const std::string& num, const std::vector<std::string>& args, const std::string& message) = 0;
/*
* CTCP/DCC isn't really a part of the IRC standard so these are optional.
*/
virtual void onMsgCTCPRequest(const IRCPrefix& sender, const std::string& target, const std::string& command, const std::string& message) {};
virtual void onMsgCTCPResponse(const IRCPrefix& sender, const std::string& target, const std::string& command, const std::string& message) {};
virtual void onMsgDCCRequest(std::shared_ptr<DCC> dcc, const IRCPrefix& sender, const std::string& target, const std::string& type, const std::string& message) {};
/*
* Optional IRCv3 extensions to implement.
*/
virtual void v3onMsgAway(const IRCPrefix& sender, const std::string& message, const std::vector<std::string>& channelsAffected) {};
virtual void v3onMsgAccountLogin(const IRCPrefix& sender, const std::string& useraccount) {}
virtual void v3onMsgAccountLogout(const IRCPrefix& sender) {}
/*
* Required IRCv3 extensions to implement.
*/
virtual void v3onMsgJoin(const IRCPrefix& sender, const std::string& channel, const std::string& useraccount, const std::string& realname) = 0;
/*
* Catch-all handler. If the message parser cannot match for any of the commands, it will turn up here.
* Optional to implement.
*/
virtual void onMsgUnhandled(const IRCPrefix& sender, const std::string& command, const std::vector<std::string>& args, const std::string& message) {}
private:
std::unique_ptr<IRCBasePriv> mp;
std::unique_ptr<IRCBasePriv> mp;
};
#endif // IRCBASE_H

@ -28,20 +28,20 @@ constexpr bool DumpReadData = true;
constexpr bool DumpWriteData = true;
const std::vector<std::string> V3Support {
"account-notify",
"extended-join",
"away-notify",
"invite-notify",
"multi-prefix",
"userhost-in-names"/*,
"server-time",
"batch"
*/
/*
* TODO https://ircv3.net/irc/
* chghost
* sasl
*/
"account-notify",
"extended-join",
"away-notify",
"invite-notify",
"multi-prefix",
"userhost-in-names"/*,
"server-time",
"batch"
*/
/*
* TODO https://ircv3.net/irc/
* chghost
* sasl
*/
};
} // end anonymous namespace

@ -11,24 +11,24 @@
#include <algorithm>
IRCChannel::IRCChannel(const std::string& name, IRCBase& owner)
: m_name(name)
, m_owner(&owner)
: m_name(name)
, m_owner(&owner)
{}
const std::string& IRCChannel::name() const
{
return m_name;
return m_name;
}
std::optional<IRCMemberEntryRef> IRCChannel::getMember(const std::string& nickname)
{
auto it = std::find_if(m_members.begin(), m_members.end(),
[&nickname](const IRCMemberEntry& entry){
return entry.member()->prefix().nickname() == nickname;
});
auto it = std::find_if(m_members.begin(), m_members.end(),
[&nickname](const IRCMemberEntry& entry){
return entry.member()->prefix().nickname() == nickname;
});
return it == m_members.end() ? std::nullopt
: std::make_optional<IRCMemberEntryRef>(*it);
return it == m_members.end() ? std::nullopt
: std::make_optional<IRCMemberEntryRef>(*it);
}
IRCMemberEntry& IRCChannel::addMember(std::shared_ptr<IRCMember> member)
@ -48,55 +48,55 @@ IRCMemberEntry& IRCChannel::addMember(std::shared_ptr<IRCMember> member)
void IRCChannel::delMember(std::shared_ptr<IRCMember> member)
{
auto newEnd = std::remove_if(m_members.begin(), m_members.end(),
auto newEnd = std::remove_if(m_members.begin(), m_members.end(),
[&member](const IRCMemberEntry& e){
return member->prefix() == e.member()->prefix();
return member->prefix() == e.member()->prefix();
});
m_members.erase(newEnd, m_members.end());
m_members.erase(newEnd, m_members.end());
}
const std::vector<IRCMemberEntry>& IRCChannel::members() const
{
return m_members;
return m_members;
}
void IRCChannel::setTopic(const std::string& topic)
{
m_topic = topic;
m_topic = topic;
}
const std::string& IRCChannel::topic() const
{
return m_topic;
return m_topic;
}
const std::unordered_map<char, std::string>& IRCChannel::modes() const
{
return m_modes;
return m_modes;
}
void IRCChannel::setMode(char m, const std::string& parameter)
{
/*
* Note that if a mode requiring an argument is already set,
* the server will always send an 'unset' before setting a new value.
* But let's use insert_or_assign anyway.
*/
m_modes.insert_or_assign(m, parameter);
/*
* Note that if a mode requiring an argument is already set,
* the server will always send an 'unset' before setting a new value.
* But let's use insert_or_assign anyway.
*/
m_modes.insert_or_assign(m, parameter);
}
void IRCChannel::delMode(char m)
{
m_modes.erase(m);
m_modes.erase(m);
}
bool IRCChannel::isPopulating() const
{
return m_populating;
return m_populating;
}
void IRCChannel::donePopulating()
{
m_populating = false;
m_populating = false;
}

@ -23,33 +23,33 @@ using IRCMemberEntryRef = std::reference_wrapper<IRCMemberEntry>;
class IRCChannel
{
public:
IRCChannel(const std::string& name, IRCBase& owner);
~IRCChannel() = default;
IRCChannel(const std::string& name, IRCBase& owner);
~IRCChannel() = default;
const std::string& name() const;
const std::string& name() const;
std::optional<IRCMemberEntryRef> getMember(const std::string& nickname);
IRCMemberEntry& addMember(std::shared_ptr<IRCMember> member);
void delMember(std::shared_ptr<IRCMember> member);
const std::vector<IRCMemberEntry>& members() const;
std::optional<IRCMemberEntryRef> getMember(const std::string& nickname);
IRCMemberEntry& addMember(std::shared_ptr<IRCMember> member);
void delMember(std::shared_ptr<IRCMember> member);
const std::vector<IRCMemberEntry>& members() const;
void setTopic(const std::string& topic);
const std::string& topic() const;
void setTopic(const std::string& topic);
const std::string& topic() const;
const std::unordered_map<char, std::string>& modes() const;
void setMode(char m, const std::string& parameter = "");
void delMode(char m);
const std::unordered_map<char, std::string>& modes() const;
void setMode(char m, const std::string& parameter = "");
void delMode(char m);
bool isPopulating() const;
void donePopulating();
bool isPopulating() const;
void donePopulating();
private:
std::string m_name;
std::vector<IRCMemberEntry> m_members;
std::string m_topic;
std::unordered_map<char, std::string> m_modes;
bool m_populating{ true };
IRCBase* m_owner; // TODO use reference?
std::string m_name;
std::vector<IRCMemberEntry> m_members;
std::string m_topic;
std::unordered_map<char, std::string> m_modes;
bool m_populating{ true };
IRCBase* m_owner; // TODO use reference?
};

@ -15,114 +15,114 @@ std::string lastAsioErrorMessage;
std::string IRCErrorToString(IRCError e)
{
switch (e) {
case IRCError::NoError:
return "No error";
case IRCError::NotConnected:
return "Not connected";
case IRCError::AlreadyConnected:
return "Already connected";
case IRCError::CannotResolveAddress:
return "Cannot resolve address";
case IRCError::BrokenPipe:
return "Broken pipe";
case IRCError::ConnectionAborted:
return "Connection aborted";
case IRCError::ConnectionRefused:
return "Connection refused";
case IRCError::ConnectionReset:
return "Connection reset";
case IRCError::HostUnreachable:
return "Host unreachable";
case IRCError::NetworkDown:
return "Network down";
case IRCError::NetworkReset:
return "Network reset";
case IRCError::NetworkUnreachable:
return "Network unreachable";
case IRCError::NoDescriptors:
return "No file descriptors left";
case IRCError::TimedOut:
return "Timed out";
case IRCError::EndOfFile:
return "EOF";
case IRCError::SSL_SelfSigned:
return "SSL: Self-signed certificate";
case IRCError::SSL_CN_Mismatch:
return "SSL: (CN) Hostname mismatch";
case IRCError::SSL_CN_Missing:
return "SSL: (CN) Hostname missing";
case IRCError::SSL_CN_WildcardIllegal:
return "SSL: (CN) Illegal wildcard usage";
case IRCError::SSL_NotYetValid:
return "SSL: Certificate not yet valid";
case IRCError::SSL_Expired:
return "SSL: Certificate expired";
case IRCError::CannotChangeWhenConnected:
return "Cannot change this setting when connected";
case IRCError::HostNotSet:
return "Hostname is not set";
case IRCError::PortNotSet:
return "Port number is not set";
case IRCError::IdentNotSet:
return "Ident/username is not set";
case IRCError::NicknameNotSet:
return "Nickname is not set";
case IRCError::RealnameNotSet:
return "Real name is not set";
case IRCError::DCC_NotATarget:
return "DCC: We are not a DCC target";
case IRCError::DCC_TimedOut:
return "DCC: Request timed out";
switch (e) {
case IRCError::NoError:
return "No error";
case IRCError::NotConnected:
return "Not connected";
case IRCError::AlreadyConnected:
return "Already connected";
case IRCError::CannotResolveAddress:
return "Cannot resolve address";
case IRCError::BrokenPipe:
return "Broken pipe";
case IRCError::ConnectionAborted:
return "Connection aborted";
case IRCError::ConnectionRefused:
return "Connection refused";
case IRCError::ConnectionReset:
return "Connection reset";
case IRCError::HostUnreachable:
return "Host unreachable";
case IRCError::NetworkDown:
return "Network down";
case IRCError::NetworkReset:
return "Network reset";
case IRCError::NetworkUnreachable:
return "Network unreachable";
case IRCError::NoDescriptors:
return "No file descriptors left";
case IRCError::TimedOut:
return "Timed out";
case IRCError::EndOfFile:
return "EOF";
case IRCError::SSL_SelfSigned:
return "SSL: Self-signed certificate";
case IRCError::SSL_CN_Mismatch:
return "SSL: (CN) Hostname mismatch";
case IRCError::SSL_CN_Missing:
return "SSL: (CN) Hostname missing";
case IRCError::SSL_CN_WildcardIllegal:
return "SSL: (CN) Illegal wildcard usage";
case IRCError::SSL_NotYetValid:
return "SSL: Certificate not yet valid";
case IRCError::SSL_Expired:
return "SSL: Certificate expired";
case IRCError::CannotChangeWhenConnected:
return "Cannot change this setting when connected";
case IRCError::HostNotSet:
return "Hostname is not set";
case IRCError::PortNotSet:
return "Port number is not set";
case IRCError::IdentNotSet:
return "Ident/username is not set";
case IRCError::NicknameNotSet:
return "Nickname is not set";
case IRCError::RealnameNotSet:
return "Real name is not set";
case IRCError::DCC_NotATarget:
return "DCC: We are not a DCC target";
case IRCError::DCC_TimedOut:
return "DCC: Request timed out";
case IRCError::UnhandledException:
return "Unhandled exception";
}
}
/*
* Compiler complains even if all cases are covered...
* I don't wanna use a 'default' so that my IDE and tools can pick up on missing enumerations.
* A return here would suffice.
*/
return fmt::format("Uncaught error code {}", e);
/*
* Compiler complains even if all cases are covered...
* I don't wanna use a 'default' so that my IDE and tools can pick up on missing enumerations.
* A return here would suffice.
*/
return fmt::format("Uncaught error code {}", e);
}
IRCError SystemErrorToIRCError(const std::system_error& e)
{
switch (e.code().value()) {
case 0:
return IRCError::NoError;
case asio::error::host_not_found:
return IRCError::CannotResolveAddress;
case asio::error::broken_pipe:
return IRCError::BrokenPipe;
case asio::error::connection_aborted:
return IRCError::ConnectionAborted;
case asio::error::connection_refused:
return IRCError::ConnectionRefused;
case asio::error::connection_reset:
return IRCError::ConnectionReset;
case asio::error::host_unreachable:
return IRCError::HostUnreachable;
case asio::error::network_down:
return IRCError::NetworkDown;
case asio::error::network_reset:
return IRCError::NetworkReset;
case asio::error::network_unreachable:
return IRCError::NetworkUnreachable;
case asio::error::no_descriptors:
return IRCError::NoDescriptors;
case asio::error::timed_out:
return IRCError::TimedOut;
case asio::error::eof:
return IRCError::EndOfFile;
switch (e.code().value()) {
case 0:
return IRCError::NoError;
case asio::error::host_not_found:
return IRCError::CannotResolveAddress;
case asio::error::broken_pipe:
return IRCError::BrokenPipe;
case asio::error::connection_aborted:
return IRCError::ConnectionAborted;
case asio::error::connection_refused:
return IRCError::ConnectionRefused;
case asio::error::connection_reset:
return IRCError::ConnectionReset;
case asio::error::host_unreachable:
return IRCError::HostUnreachable;
case asio::error::network_down:
return IRCError::NetworkDown;
case asio::error::network_reset:
return IRCError::NetworkReset;
case asio::error::network_unreachable:
return IRCError::NetworkUnreachable;
case asio::error::no_descriptors:
return IRCError::NoDescriptors;
case asio::error::timed_out:
return IRCError::TimedOut;
case asio::error::eof:
return IRCError::EndOfFile;
/*
* A 'default' here is needed since asio::error has a few errors I don't care about, or is able to care for...
*/
default:
lastAsioErrorCode = e.code().value();
lastAsioErrorMessage = e.code().message();
std::cerr << fmt::format("Unhandled exception {}: {}", lastAsioErrorCode, lastAsioErrorMessage) << std::endl;
return IRCError::UnhandledException;
}
default:
lastAsioErrorCode = e.code().value();
lastAsioErrorMessage = e.code().message();
std::cerr << fmt::format("Unhandled exception {}: {}", lastAsioErrorCode, lastAsioErrorMessage) << std::endl;
return IRCError::UnhandledException;
}
}

@ -16,42 +16,42 @@ extern std::string lastAsioErrorMessage;
enum class IRCError
{
NoError,
NotConnected,
AlreadyConnected,
CannotResolveAddress,
BrokenPipe,
ConnectionAborted,
ConnectionRefused,
ConnectionReset,
HostUnreachable,
NetworkDown,
NetworkReset,
NetworkUnreachable,
NoDescriptors,
TimedOut,
EndOfFile,
SSL_SelfSigned,
SSL_CN_Mismatch,
SSL_CN_Missing,
SSL_CN_WildcardIllegal,
SSL_NotYetValid,
SSL_Expired,
CannotChangeWhenConnected,
HostNotSet,
PortNotSet,
IdentNotSet,
NicknameNotSet,
RealnameNotSet,
DCC_NotATarget,
DCC_TimedOut,
UnhandledException
NoError,
NotConnected,
AlreadyConnected,
CannotResolveAddress,
BrokenPipe,
ConnectionAborted,
ConnectionRefused,
ConnectionReset,
HostUnreachable,
NetworkDown,
NetworkReset,
NetworkUnreachable,
NoDescriptors,
TimedOut,
EndOfFile,
SSL_SelfSigned,
SSL_CN_Mismatch,
SSL_CN_Missing,
SSL_CN_WildcardIllegal,
SSL_NotYetValid,
SSL_Expired,
CannotChangeWhenConnected,
HostNotSet,
PortNotSet,
IdentNotSet,
NicknameNotSet,
RealnameNotSet,
DCC_NotATarget,
DCC_TimedOut,
UnhandledException
};
std::string IRCErrorToString(IRCError e);

@ -10,51 +10,51 @@
#include <algorithm>
IRCMember::IRCMember(const IRCPrefix& prefix)
: m_prefix(prefix)
: m_prefix(prefix)
{}
IRCMember::IRCMember(const std::string& nickname)
: m_prefix(IRCPrefix::fromNickname(nickname))
: m_prefix(IRCPrefix::fromNickname(nickname))
{}
const IRCPrefix& IRCMember::prefix() const
{
return m_prefix;
return m_prefix;
}
void IRCMember::setPrefix(const IRCPrefix& prefix)
{
m_prefix = prefix;
m_prefix = prefix;
}
const std::vector<std::weak_ptr<IRCChannel>>& IRCMember::channels()
{
return m_channels;
return m_channels;
}
void IRCMember::addChannel(std::weak_ptr<IRCChannel> channel)
{
auto it = std::find_if(m_channels.begin(), m_channels.end(),
auto it = std::find_if(m_channels.begin(), m_channels.end(),
[channel](const std::weak_ptr<IRCChannel>& p){
return p.lock()->name() == channel.lock()->name();
});
});
if (it == m_channels.end())
m_channels.emplace_back(channel);
if (it == m_channels.end())
m_channels.emplace_back(channel);
}
void IRCMember::delChannel(std::weak_ptr<IRCChannel> channel)
{
auto it = std::find_if(m_channels.begin(), m_channels.end(),
auto it = std::find_if(m_channels.begin(), m_channels.end(),
[channel](const std::weak_ptr<IRCChannel>& p){
return p.lock()->name() == channel.lock()->name();
});
if (it != m_channels.end())
m_channels.erase(it);
m_channels.erase(it);
}
void IRCMember::setNickname(const std::string& nickname)
{
m_prefix.setNickname(nickname);
m_prefix.setNickname(nickname);
}

@ -18,20 +18,20 @@ class IRCChannel;
class IRCMember
{
public:
explicit IRCMember(const IRCPrefix& prefix);
explicit IRCMember(const std::string& nickname);
explicit IRCMember(const IRCPrefix& prefix);
explicit IRCMember(const std::string& nickname);
const IRCPrefix& prefix() const;
void setPrefix(const IRCPrefix& prefix);
const std::vector<std::weak_ptr<IRCChannel>>& channels();
void addChannel(std::weak_ptr<IRCChannel> channel);
void delChannel(std::weak_ptr<IRCChannel> channel);
const IRCPrefix& prefix() const;
void setPrefix(const IRCPrefix& prefix);
const std::vector<std::weak_ptr<IRCChannel>>& channels();
void addChannel(std::weak_ptr<IRCChannel> channel);
void delChannel(std::weak_ptr<IRCChannel> channel);
void setNickname(const std::string& nickname);
void setNickname(const std::string& nickname);
private:
IRCPrefix m_prefix;
std::vector<std::weak_ptr<IRCChannel>> m_channels;
IRCPrefix m_prefix;
std::vector<std::weak_ptr<IRCChannel>> m_channels;
};
#endif // IRCMEMBER_H

@ -10,54 +10,54 @@
#include <algorithm>
IRCMemberEntry::IRCMemberEntry(std::shared_ptr<IRCMember> member, IRCBase& owner)
: m_member(member)
, m_owner(&owner)
: m_member(member)
, m_owner(&owner)
{}
std::shared_ptr<IRCMember> IRCMemberEntry::member() const
{
return m_member;
return m_member;
}
const std::string& IRCMemberEntry::modes() const
{
return m_modes;
return m_modes;
}
void IRCMemberEntry::addMode(char m)
{
// Note: "PREFIX" is always present.
// Value example: (ohv)@%+
// Value is ordered with most significant first.
const std::string& prefix = m_owner->isupport().find("PREFIX")->second;
// Note: "PREFIX" is always present.
// Value example: (ohv)@%+
// Value is ordered with most significant first.
const std::string& prefix = m_owner->isupport().find("PREFIX")->second;
// Just interested in the mode letters (ie. ohv)
const std::string validModes(prefix.begin() + 1, std::find(prefix.begin(), prefix.end(), ')'));
auto order = [validModes](const char m) -> int {
auto ret = validModes.find(m);
return ret != std::string::npos ? static_cast<int>(ret)
: -1;
};
// Just interested in the mode letters (ie. ohv)
const std::string validModes(prefix.begin() + 1, std::find(prefix.begin(), prefix.end(), ')'));
auto order = [validModes](const char m) -> int {
auto ret = validModes.find(m);
return ret != std::string::npos ? static_cast<int>(ret)
: -1;
};
auto mOrd = order(m);
if (mOrd < 0)
return;
auto mOrd = order(m);
if (mOrd < 0)
return;
auto it = m_modes.begin();
for (auto& mode : m_modes) {
if (mode == m)
return;
else if (order(mode) >= mOrd)
break;
else
++it;
}
m_modes.insert(it, m);
auto it = m_modes.begin();
for (auto& mode : m_modes) {
if (mode == m)
return;
else if (order(mode) >= mOrd)
break;
else
++it;
}
m_modes.insert(it, m);
}
void IRCMemberEntry::delMode(char m)
{
auto pos = m_modes.find_first_of(m);
auto pos = m_modes.find_first_of(m);
/*
* Out-of-bounds can happen due to an IRC protocol bug. For example, where you join a channel which has
@ -66,6 +66,6 @@ void IRCMemberEntry::delMode(char m)
* IRCv3 implements a fix for this which this client support, but not all servers do.
* When a member then removes the unknown mode, we get this situation below;
*/
if (pos != std::string::npos)
if (pos != std::string::npos)
m_modes.erase(pos, 1);
}

@ -17,19 +17,19 @@ class IRCBase;
class IRCMemberEntry
{
public:
IRCMemberEntry(std::shared_ptr<IRCMember> member, IRCBase& owner);
~IRCMemberEntry() = default;
IRCMemberEntry(std::shared_ptr<IRCMember> member, IRCBase& owner);
~IRCMemberEntry() = default;
[[nodiscard]] std::shared_ptr<IRCMember> member() const;
[[nodiscard]] std::shared_ptr<IRCMember> member() const;
[[nodiscard]] const std::string& modes() const;
void addMode(char m);
void delMode(char m);
[[nodiscard]] const std::string& modes() const;
void addMode(char m);
void delMode(char m);
private:
std::shared_ptr<IRCMember> m_member;
std::string m_modes;
IRCBase* m_owner; // can't be reference since we store IRCMemberEntry in a vector, requiring assignment operator.
std::shared_ptr<IRCMember> m_member;
std::string m_modes;
IRCBase* m_owner; // can't be reference since we store IRCMemberEntry in a vector, requiring assignment operator.
};
#endif // IRCMEMBERENTRY_H

@ -11,107 +11,107 @@
IRCPrefix::IRCPrefix(const std::string& prefix)
{
auto atIt = std::find(prefix.begin(), prefix.end(), '@');
if (atIt != prefix.end()) {
// nickname!user@host
m_type = Type::user;
auto exclmIt = std::find(prefix.begin(), prefix.end(), '!');
m_nickname = std::string(prefix.begin(), exclmIt);
m_user = std::string(exclmIt + 1, atIt);
m_host = std::string(atIt + 1, prefix.end());
}
else {
m_type = Type::server;
m_servername = prefix;
}
auto atIt = std::find(prefix.begin(), prefix.end(), '@');
if (atIt != prefix.end()) {
// nickname!user@host
m_type = Type::user;
auto exclmIt = std::find(prefix.begin(), prefix.end(), '!');
m_nickname = std::string(prefix.begin(), exclmIt);
m_user = std::string(exclmIt + 1, atIt);
m_host = std::string(atIt + 1, prefix.end());
}
else {
m_type = Type::server;
m_servername = prefix;
}
}
IRCPrefix::IRCPrefix(const IRCPrefix& other)
: m_servername(other.m_servername)
, m_nickname(other.m_nickname)
, m_user(other.m_user)
, m_host(other.m_host)
, m_type(other.m_type)
: m_servername(other.m_servername)
, m_nickname(other.m_nickname)
, m_user(other.m_user)
, m_host(other.m_host)
, m_type(other.m_type)
{}
IRCPrefix::IRCPrefix(IRCPrefix&& other) noexcept
: m_servername(std::move(other.m_servername))
, m_nickname(std::move(other.m_nickname))
, m_user(std::move(other.m_user))
, m_host(std::move(other.m_host))
, m_type(std::move(other.m_type))
: m_servername(std::move(other.m_servername))
, m_nickname(std::move(other.m_nickname))
, m_user(std::move(other.m_user))
, m_host(std::move(other.m_host))
, m_type(std::move(other.m_type))
{}
IRCPrefix::IRCPrefix(IRCPrefix::Type t)
: m_type(t)
: m_type(t)
{}
IRCPrefix& IRCPrefix::operator=(const IRCPrefix& other)
{
m_servername = other.m_servername;
m_nickname = other.m_nickname;
m_user = other.m_user;
m_host = other.m_host;
m_type = other.m_type;
return *this;
m_servername = other.m_servername;
m_nickname = other.m_nickname;
m_user = other.m_user;
m_host = other.m_host;
m_type = other.m_type;
return *this;
}
const std::string& IRCPrefix::toString() const
{
if (m_type == Type::server)
return m_servername;
else
return m_nickname;
if (m_type == Type::server)
return m_servername;
else
return m_nickname;
}
const std::string& IRCPrefix::servername() const
{
return m_servername;
return m_servername;
}
const std::string& IRCPrefix::nickname() const
{
return m_nickname;
return m_nickname;
}
const std::string& IRCPrefix::user() const
{
return m_user;
return m_user;
}
const std::string& IRCPrefix::host() const
{
return m_host;
return m_host;
}
void IRCPrefix::setNickname(const std::string& nickname)
{
m_nickname = nickname;
m_nickname = nickname;
}
void IRCPrefix::setHost(const std::string& host)
{
m_host = host;
m_host = host;
}
std::string IRCPrefix::composite() const
{
if (m_type == Type::server)
return m_servername;
else
return fmt::format("{}!{}@{}", m_nickname, m_user, m_host);
if (m_type == Type::server)
return m_servername;
else
return fmt::format("{}!{}@{}", m_nickname, m_user, m_host);
}
IRCPrefix::Type IRCPrefix::type() const
{
return m_type;
return m_type;
}
IRCPrefix IRCPrefix::fromNickname(const std::string& nickname)
{
IRCPrefix p(Type::user);
p.m_nickname = nickname;
return p;
IRCPrefix p(Type::user);
p.m_nickname = nickname;
return p;
}
bool IRCPrefix::operator!=(const IRCPrefix& other)

@ -13,51 +13,51 @@
class IRCPrefix
{
public:
explicit IRCPrefix(const std::string& prefix);
IRCPrefix(const IRCPrefix& other);
IRCPrefix(IRCPrefix&& other) noexcept;
IRCPrefix() = delete;
~IRCPrefix() = default;
explicit IRCPrefix(const std::string& prefix);
IRCPrefix(const IRCPrefix& other);
IRCPrefix(IRCPrefix&& other) noexcept;
IRCPrefix() = delete;
~IRCPrefix() = default;
IRCPrefix& operator=(const IRCPrefix& other);
IRCPrefix& operator=(const IRCPrefix& other);
bool operator!=(const IRCPrefix& other);
bool operator==(const IRCPrefix& other);
[[nodiscard]] const std::string& toString() const; //!< Returns either a servername or nickname.
[[nodiscard]] const std::string& toString() const; //!< Returns either a servername or nickname.
[[nodiscard]] const std::string& servername() const;
[[nodiscard]] const std::string& nickname() const;
[[nodiscard]] const std::string& user() const;
[[nodiscard]] const std::string& host() const;
[[nodiscard]] const std::string& servername() const;
[[nodiscard]] const std::string& nickname() const;
[[nodiscard]] const std::string& user() const;
[[nodiscard]] const std::string& host() const;
void setNickname(const std::string& nickname);
void setHost(const std::string& host);
void setNickname(const std::string& nickname);
void setHost(const std::string& host);
[[nodiscard]] std::string composite() const;
[[nodiscard]] std::string composite() const;
enum class Type
{
server,
user
};
enum class Type
{
server,
user
};
[[nodiscard]] Type type() const;
[[nodiscard]] Type type() const;
[[nodiscard]] static IRCPrefix fromNickname(const std::string& nickname);
[[nodiscard]] static IRCPrefix fromNickname(const std::string& nickname);
private:
explicit IRCPrefix(Type t);
explicit IRCPrefix(Type t);
// Used for :irc.server.name prefix
std::string m_servername;
// Used for :irc.server.name prefix
std::string m_servername;
/* Used for :nickname!user@host prefix */
std::string m_nickname;
std::string m_user;
std::string m_host;
/* Used for :nickname!user@host prefix */
std::string m_nickname;
std::string m_user;
std::string m_host;
Type m_type;
Type m_type;
};
bool operator!=(const IRCPrefix& lhs, const IRCPrefix& rhs);

@ -12,21 +12,21 @@
std::pair<std::string,std::string> FormatCTCPLine(std::string line)
{
/* Clean away the CTCP byte flags */
if (line[0] == CTCPflag)
line.erase(line.begin());
/* Clean away the CTCP byte flags */
if (line[0] == CTCPflag)
line.erase(line.begin());
auto endIt = std::find(line.begin(), line.end(), CTCPflag);
if (endIt != line.end())
line.pop_back();
auto endIt = std::find(line.begin(), line.end(), CTCPflag);
if (endIt != line.end())
line.pop_back();
auto cmdIt = std::find(line.begin(), line.end(), ' ');
std::string cmd(line.begin(), cmdIt);
std::string ctcpMsg;
if (cmdIt != line.end())
ctcpMsg = std::string(cmdIt + 1, line.end());
auto cmdIt = std::find(line.begin(), line.end(), ' ');
std::string cmd(line.begin(), cmdIt);
std::string ctcpMsg;
if (cmdIt != line.end())
ctcpMsg = std::string(cmdIt + 1, line.end());
return std::make_pair(cmd, ctcpMsg);
return std::make_pair(cmd, ctcpMsg);
}
/*!
@ -35,80 +35,80 @@ std::pair<std::string,std::string> FormatCTCPLine(std::string line)
*/
bool singleWildcardMatch(const std::string& str, const std::string& wc)
{
if (str.empty() || wc.empty())
return false;
auto wcit = std::find(wc.begin(), wc.end(), '*');
if (wcit == wc.end())
return str == wc;
/*
* Since we only support a single wildcard we only need to match the left side and right side
* of the asterisk (wildcard) against the target string 'str'.
* Also if the "end" of left match is equal to the "begin" of right match, it's a mismatch.
*/
bool hasLeft, hasRight { false };
auto leftEnd = str.end();
auto rightBegin = str.end();
/* Left side */
if (wcit != wc.begin()) {
std::string wildls(wc.begin(), wcit);
auto strmatch = str.substr(0, wildls.length());
if (wildls != strmatch)
return false;
leftEnd = str.begin() + wildls.length();
hasLeft = true;
}
/* Right side */
if (wcit != wc.end() - 1) {
std::string wildrs(wcit + 1, wc.end());
auto mpos = str.find(wildrs);
if (mpos == std::string::npos)
return false;
auto strmatch = str.substr(mpos);
if (wildrs != strmatch)
return false;
rightBegin = str.begin() + mpos;
hasRight = true;
}
// Avoid matching "left*right" with "leftright"
return !(hasLeft && hasRight && leftEnd == rightBegin);
if (str.empty() || wc.empty())
return false;
auto wcit = std::find(wc.begin(), wc.end(), '*');
if (wcit == wc.end())
return str == wc;
/*
* Since we only support a single wildcard we only need to match the left side and right side
* of the asterisk (wildcard) against the target string 'str'.
* Also if the "end" of left match is equal to the "begin" of right match, it's a mismatch.
*/
bool hasLeft, hasRight { false };
auto leftEnd = str.end();
auto rightBegin = str.end();
/* Left side */
if (wcit != wc.begin()) {
std::string wildls(wc.begin(), wcit);
auto strmatch = str.substr(0, wildls.length());
if (wildls != strmatch)
return false;
leftEnd = str.begin() + wildls.length();
hasLeft = true;
}
/* Right side */
if (wcit != wc.end() - 1) {
std::string wildrs(wcit + 1, wc.end());
auto mpos = str.find(wildrs);
if (mpos == std::string::npos)
return false;
auto strmatch = str.substr(mpos);
if (wildrs != strmatch)
return false;
rightBegin = str.begin() + mpos;
hasRight = true;
}
// Avoid matching "left*right" with "leftright"
return !(hasLeft && hasRight && leftEnd == rightBegin);
}
std::string longIpToNormal(const std::string& longip)
{
try {
unsigned ip = std::stoul(longip);
unsigned a = (ip & 0xFF000000u) >> 24u;
unsigned b = (ip & 0x00FF0000u) >> 16u;
unsigned c = (ip & 0x0000FF00u) >> 8u;
unsigned d = ip & 0x000000FFu;
return fmt::format("{}.{}.{}.{}", a, b, c, d);
}
catch (...) {
return "0.0.0.0";
}
try {
unsigned ip = std::stoul(longip);
unsigned a = (ip & 0xFF000000u) >> 24u;
unsigned b = (ip & 0x00FF0000u) >> 16u;
unsigned c = (ip & 0x0000FF00u) >> 8u;
unsigned d = ip & 0x000000FFu;
return fmt::format("{}.{}.{}.{}", a, b, c, d);
}
catch (...) {
return "0.0.0.0";
}
}
std::string normalIpToLong(const std::string& normalip)
{
std::stringstream ss(normalip);
std::string p;
std::vector<unsigned> parts;
while (getline(ss, p, '.'))
parts.emplace_back(std::stoul(p));
uint32_t ret = parts[0];
for (int i = 1; i < 4; ++i) {
ret <<= 8u;
ret |= parts[i];
}
return std::to_string(ret);
std::stringstream ss(normalip);
std::string p;
std::vector<unsigned> parts;
while (getline(ss, p, '.'))
parts.emplace_back(std::stoul(p));
uint32_t ret = parts[0];
for (int i = 1; i < 4; ++i) {
ret <<= 8u;
ret |= parts[i];
}
return std::to_string(ret);
}
std::string concatenateModes(const std::unordered_map<char, std::string>& modes)

@ -12,40 +12,40 @@ const QString IWin::InvalidWindowTypeString { "Invalid" };
std::unordered_map<IWin::Type,QString> IWin::TypeString = {
{ IWin::Type::Undefined, "Undefined" },
{ IWin::Type::Status, "Status" },
{ IWin::Type::Channel, "Channel" },
{ IWin::Type::Private, "Private" },
{ IWin::Type::Custom, "Custom" }
{ IWin::Type::Status, "Status" },
{ IWin::Type::Channel, "Channel" },
{ IWin::Type::Private, "Private" },
{ IWin::Type::Custom, "Custom" }
};
IWin::Type IWin::getType() const
{
return m_type;
return m_type;
}
IWin* IWin::getStatusParent()
{
return (m_type == Type::Status) ? this : m_parent;
return (m_type == Type::Status) ? this : m_parent;
}
const QString& IWin::getButtonText() const
{
return m_buttonText;
return m_buttonText;
}
void IWin::setButtonText(const QString& text)
{
m_buttonText = text;
m_buttonText = text;
}
IWin::IWin(Type type, IWin* parentWindow)
: m_type(type)
, m_parent(parentWindow)
: m_type(type)
, m_parent(parentWindow)
{
}
void IWin::closeEvent(QCloseEvent*)
{
emit aboutToClose(this);
emit aboutToClose(this);
}

@ -16,44 +16,44 @@
class IWin : public QWidget
{
Q_OBJECT
Q_OBJECT
public:
enum class Type
{
Undefined,
Status,
Channel,
Private,
Custom
};
static const QString InvalidWindowTypeString;
static std::unordered_map<Type,QString> TypeString;
Type getType() const;
IWin* getParent() const { return m_parent; }
IWin* getStatusParent();
virtual bool print(PrintType ptype, const QString& text) = 0;
virtual void refreshWindowTitle() = 0;
virtual void clear() = 0;
const QString& getButtonText() const;
void setButtonText(const QString& text);
bool operator==(const IWin& other); // TODO implementation
enum class Type
{
Undefined,
Status,
Channel,
Private,
Custom
};
static const QString InvalidWindowTypeString;
static std::unordered_map<Type,QString> TypeString;
Type getType() const;
IWin* getParent() const { return m_parent; }
IWin* getStatusParent();
virtual bool print(PrintType ptype, const QString& text) = 0;
virtual void refreshWindowTitle() = 0;
virtual void clear() = 0;
const QString& getButtonText() const;
void setButtonText(const QString& text);
bool operator==(const IWin& other); // TODO implementation
protected:
explicit IWin(Type type, IWin* parentWindow = nullptr);
void closeEvent(QCloseEvent*) override;
explicit IWin(Type type, IWin* parentWindow = nullptr);
void closeEvent(QCloseEvent*) override;
private:
Type m_type;
QString m_buttonText;
IWin* m_parent{ nullptr };
bool m_firstShow{ true };
Type m_type;
QString m_buttonText;
IWin* m_parent{ nullptr };
bool m_firstShow{ true };
signals:
void aboutToClose(IWin* who);
void aboutToClose(IWin* who);
};
#endif // IWIN_H

@ -18,162 +18,162 @@
#include <QHeaderView>
IWinChannel::IWinChannel(IWinStatus* statusParent, const QString& channelName)
: IWin(IWin::Type::Channel, statusParent)
, status(statusParent)
, connection(statusParent->getConnection())
: IWin(IWin::Type::Channel, statusParent)
, status(statusParent)
, connection(statusParent->getConnection())
{
setButtonText(channelName);
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); });
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);
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);
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);
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);
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::memberListClearedForAll,
this, &IWinChannel::memberListCleared);
connect(&connection, &IRC::memberAdded,
this, &IWinChannel::memberListAdd);
connect(&connection, &IRC::memberAdded,
this, &IWinChannel::memberListAdd);
connect(&connection, &IRC::memberChanged,
this, &IWinChannel::memberListUpdateMember);
connect(&connection, &IRC::memberChanged,
this, &IWinChannel::memberListUpdateMember);
connect(&connection, &IRC::memberRemoved,
this, &IWinChannel::memberListRemoveMember);
connect(&connection, &IRC::memberRemoved,
this, &IWinChannel::memberListRemoveMember);
connect(input, &ILineEdit::newLine,
this, &IWinChannel::newLine);
connect(input, &ILineEdit::newLine,
this, &IWinChannel::newLine);
connect(listbox, &IListWidget::itemDoubleClicked,
this, &IWinChannel::listboxDoubleclick);
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(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());
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;
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));
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);
});
QPoint posAdj = mapToGlobal(pos);
posAdj.setX(posAdj.x() + view->width() + splitter->handleWidth());
ScriptManager::instance()->contextMenuPopup(ScriptMenuType::Memberlist, posAdj, menuSymbols);
});
}
bool IWinChannel::print(const PrintType ptype, const QString& text)
{
view->print(ptype, text);
view->print(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;
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());
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));
QString title = getButtonText();
if (!modes.empty())
title += QStringLiteral(" (+%1)").arg(QString::fromStdString(modes));
if (!topic.empty())
title += QStringLiteral(": %1").arg(QString::fromStdString(topic));
if (!topic.empty())
title += QStringLiteral(": %1").arg(QString::fromStdString(topic));
setWindowTitle(title);
setWindowTitle(title);
}
void IWinChannel::resetNicklist()
{
nlControl->clear();
nlControl->clear();
}
void IWinChannel::newLine(const QString& line)
{
InputHandler inHndl(connection);
inHndl.parse(*this, line);
InputHandler inHndl(connection);
inHndl.parse(*this, line);
}
void IWinChannel::listboxDoubleclick(QListWidgetItem* item)
{
if (!item)
return;
if (!item)
return;
QString nickname = item->text();
if (isUserModePrefix(nickname[0]))
nickname = nickname.mid(1);
QString nickname = item->text();
if (isUserModePrefix(nickname[0]))
nickname = nickname.mid(1);
input->setFocus();
IWin* subwin = status->createPrivateWindow(nickname);
subwin->refreshWindowTitle();
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