#17 Tabs to spaces

master
Tomatix 3 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;
return std::find(begin, prefix.end(), c.toLatin1()) != prefix.end();
const auto& isupport = connection.isupport();
const auto& prefix = isupport.at("PREFIX"); // Always present in the isupport map.
auto begin = std::find(prefix.begin(), prefix.end(), ')') + 1;
return std::find(begin, prefix.end(), c.toLatin1()) != prefix.end();
}
void IWinChannel::closeEvent(QCloseEvent* evt)
@ -181,7 +181,7 @@ void IWinChannel::closeEvent(QCloseEvent* evt)
if (listbox->count() > 0)
connection.command(Command::IRC::PART, { getButtonText().toStdString() }, "");
IWin::closeEvent(evt);
IWin::closeEvent(evt);
}
int IWinChannel::tabComplete(int index, QString& pattern)
@ -224,8 +224,8 @@ Retry:
void IWinChannel::memberListReloaded(const QString& channel)
{
if (channel != getButtonText()) return;
nlControl->reload();
if (channel != getButtonText()) return;
nlControl->reload();
}
void IWinChannel::memberListReset(const QString& channel)
@ -236,22 +236,22 @@ void IWinChannel::memberListReset(const QString& channel)
void IWinChannel::memberListCleared()
{
nlControl->clear();
nlControl->clear();
}
void IWinChannel::memberListAdd(const QString& channel, const IRCMemberEntry& member)
{
if (channel != getButtonText()) return;
nlControl->addMember(member);
if (channel != getButtonText()) return;
nlControl->addMember(member);
}
void IWinChannel::memberListUpdateMember(const QString& nickname, const IRCMemberEntry& member)
{
nlControl->updateMember(nickname, member);
nlControl->updateMember(nickname, member);
}
void IWinChannel::memberListRemoveMember(const QString& channel, const IRCMemberEntry& member)
{
if (channel != getButtonText()) return;
nlControl->removeMember(member);
if (channel != getButtonText()) return;
nlControl->removeMember(member);
}

@ -24,44 +24,44 @@ class IWinStatus;
class IWinChannel : public IWin
{
Q_OBJECT
Q_OBJECT
public:
IWinChannel(IWinStatus* statusParent, const QString& channelName);
IWinChannel(IWinStatus* statusParent, const QString& channelName);
bool print(const PrintType ptype, const QString& text) override;
void refreshWindowTitle() override;
void clear() override { view->resetView(); }
void resetNicklist();
bool print(const PrintType ptype, const QString& text) override;
void refreshWindowTitle() override;
void clear() override { view->resetView(); }
void resetNicklist();
private:
void newLine(const QString& line);
void listboxDoubleclick(QListWidgetItem* item);
bool isUserModePrefix(QChar c); //! Returns true if the passed character is a prefix like @ + etc
void newLine(const QString& line);
void listboxDoubleclick(QListWidgetItem* item);
bool isUserModePrefix(QChar c); //! Returns true if the passed character is a prefix like @ + etc
void closeEvent(QCloseEvent* evt) override;
int tabComplete(int index, QString& pattern);
void closeEvent(QCloseEvent* evt) override;
int tabComplete(int index, QString& pattern);
QSplitter* splitter;
QVBoxLayout* v_layout;
IIRCView* view;
IListWidget* listbox;
ILineEdit* input;
QSplitter* splitter;
QVBoxLayout* v_layout;
IIRCView* view;
IListWidget* listbox;
ILineEdit* input;
NicklistController* nlControl;
NicklistController* nlControl;
IWinStatus* status;
IRC& connection;
IWinStatus* status;
IRC& connection;
SymbolScope menuSymbols;
SymbolScope menuSymbols;
private slots:
void memberListReloaded(const QString& channel);
void memberListReloaded(const QString& channel);
void memberListReset(const QString& channel);
void memberListCleared();
void memberListAdd(const QString& channel, const IRCMemberEntry& member);
void memberListUpdateMember(const QString& nickname, const IRCMemberEntry& member);
void memberListRemoveMember(const QString& channel, const IRCMemberEntry& member);
void memberListCleared();
void memberListAdd(const QString& channel, const IRCMemberEntry& member);
void memberListUpdateMember(const QString& nickname, const IRCMemberEntry& member);
void memberListRemoveMember(const QString& channel, const IRCMemberEntry& member);
};
#endif // IWINCHANNEL_H

@ -13,73 +13,73 @@
#include <ConfigMgr.h>
IWinPrivate::IWinPrivate(IWinStatus* statusParent, const QString& targetName)
: IWin(IWin::Type::Private, statusParent)
, status(statusParent)
, connection(statusParent->getConnection())
: IWin(IWin::Type::Private, statusParent)
, status(statusParent)
, connection(statusParent->getConnection())
{
setButtonText(targetName);
setButtonText(targetName);
layout = new QVBoxLayout();
view = new IIRCView();
input = new ILineEdit([this](int a, QString& b){ return tabComplete(a,b); });
layout = new QVBoxLayout();
view = new IIRCView();
input = new ILineEdit([this](int a, QString& b){ return tabComplete(a,b); });
layout->setMargin(0);
layout->setSpacing(2);
layout->addWidget(view);
layout->addWidget(input);
setLayout(layout);
setTabOrder(input, view);
layout->setMargin(0);
layout->setSpacing(2);
layout->addWidget(view);
layout->addWidget(input);
setLayout(layout);
setTabOrder(input, view);
view->setFocusPolicy(Qt::FocusPolicy::NoFocus);
view->setFocusPolicy(Qt::FocusPolicy::NoFocus);
connect(input, &ILineEdit::newLine,
this, &IWinPrivate::newLine);
connect(input, &ILineEdit::newLine,
this, &IWinPrivate::newLine);
connect(view, &IIRCView::customContextMenuRequested, [this](const QPoint& point){
menuSymbols.set("nickname", getButtonText().toStdString());
ScriptManager::instance()->contextMenuPopup(ScriptMenuType::Private, mapToGlobal(point), menuSymbols);
});
connect(view, &IIRCView::customContextMenuRequested, [this](const QPoint& point){
menuSymbols.set("nickname", getButtonText().toStdString());
ScriptManager::instance()->contextMenuPopup(ScriptMenuType::Private, mapToGlobal(point), menuSymbols);
});
}
bool IWinPrivate::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("Privates") == "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("Privates") == "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 IWinPrivate::refreshWindowTitle()
{
QString target = getButtonText();
auto ptr = connection.getMember(target.toStdString());
if (!ptr)
setWindowTitle(target);
else {
const auto& prefix = ptr->prefix();
if (prefix.host().empty())
setWindowTitle(target);
else
setWindowTitle(QStringLiteral("%1 (%2@%3)")
.arg(target)
.arg(prefix.user().c_str())
.arg(prefix.host().c_str()));
}
QString target = getButtonText();
auto ptr = connection.getMember(target.toStdString());
if (!ptr)
setWindowTitle(target);
else {
const auto& prefix = ptr->prefix();
if (prefix.host().empty())
setWindowTitle(target);
else
setWindowTitle(QStringLiteral("%1 (%2@%3)")
.arg(target)
.arg(prefix.user().c_str())
.arg(prefix.host().c_str()));
}
}
void IWinPrivate::newLine(const QString& line)

@ -19,26 +19,26 @@ class IRC;
class IWinPrivate : public IWin
{
Q_OBJECT
Q_OBJECT
public:
IWinPrivate(IWinStatus* statusParent, const QString& targetName);
bool print(const PrintType ptype, const QString& text) override;
void refreshWindowTitle() override;
IWinPrivate(IWinStatus* statusParent, const QString& targetName);
bool print(const PrintType ptype, const QString& text) override;
void refreshWindowTitle() override;
void clear() override { view->resetView(); }
private:
void newLine(const QString& newLine);
void newLine(const QString& newLine);
int tabComplete(int index, QString& pattern);
QVBoxLayout* layout;
IIRCView* view;
ILineEdit* input;
QVBoxLayout* layout;
IIRCView* view;
ILineEdit* input;
IWinStatus* status;
IRC& connection;
IWinStatus* status;
IRC& connection;
SymbolScope menuSymbols;
SymbolScope menuSymbols;
};
#endif // IWINPRIVATE_H

@ -16,202 +16,202 @@
int IWinStatus::StatusWindowCount = 0;
IWinStatus::IWinStatus(const QString& buttonText)
: IWin(Type::Status)
, connection(*this)
: IWin(Type::Status)
, connection(*this)
{
setButtonText(buttonText);
++StatusWindowCount;
setButtonText(buttonText);
++StatusWindowCount;
layout = new QVBoxLayout();
view = new IIRCView();
input = new ILineEdit([this](int a, QString& b){ return tabComplete(a,b); });
layout = new QVBoxLayout();
view = new IIRCView();
input = new ILineEdit([this](int a, QString& b){ return tabComplete(a,b); });
static int connectionIdCount{ 0 };
connect(view, &IIRCView::customContextMenuRequested, [this](const QPoint& point){
menuSymbols.set("cid", ++connectionIdCount);
ScriptManager::instance()->contextMenuPopup(ScriptMenuType::Status, mapToGlobal(point), menuSymbols);
});
static int connectionIdCount{ 0 };
connect(view, &IIRCView::customContextMenuRequested, [this](const QPoint& point){
menuSymbols.set("cid", ++connectionIdCount);
ScriptManager::instance()->contextMenuPopup(ScriptMenuType::Status, mapToGlobal(point), menuSymbols);
});
layout->setMargin(0);
layout->setSpacing(2);
layout->addWidget(view);
layout->addWidget(input);
setLayout(layout);
setTabOrder(input, view);
layout->setMargin(0);
layout->setSpacing(2);
layout->addWidget(view);
layout->addWidget(input);
setLayout(layout);
setTabOrder(input, view);
view->setFocusPolicy(Qt::FocusPolicy::NoFocus);
view->setFocusPolicy(Qt::FocusPolicy::NoFocus);
connect(input, &ILineEdit::newLine,
this, &IWinStatus::newLine);
connect(input, &ILineEdit::newLine,
this, &IWinStatus::newLine);
connect(&connection, &IRC::connected, [this] {
MdiManager::instance().connectionStateChange();
refreshWindowTitle();
});
connect(&connection, &IRC::connected, [this] {
MdiManager::instance().connectionStateChange();
refreshWindowTitle();
});
connect(&connection, &IRC::registered, [this] {
refreshWindowTitle();
});
connect(&connection, &IRC::registered, [this] {
refreshWindowTitle();
});
connect(&connection, &IRC::disconnected, [this] {
disconnectedFromServer();
MdiManager::instance().connectionStateChange();
refreshWindowTitle();
});
connect(&connection, &IRC::disconnected, [this] {
disconnectedFromServer();
MdiManager::instance().connectionStateChange();
refreshWindowTitle();
});
Qt_Eventloop_Hook();
Qt_Eventloop_Hook();
}
bool IWinStatus::print(const PrintType ptype, const QString& text)
{
view->print(ptype, text);
view->print(ptype, text);
MdiManager::instance().highlight(this, HL_Activity);
return true;
return true;
}
void IWinStatus::refreshWindowTitle()
{
const auto& isupport = connection.isupport();
const auto it = isupport.find("NETWORK");
const auto& isupport = connection.isupport();
const auto it = isupport.find("NETWORK");
QString title;
if (!connection.isOnline() && !connection.isConnected())
title = tr("Status (Offline)");
else if (!connection.isOnline() && connection.isConnected())
if (!connection.isOnline() && !connection.isConnected())
title = tr("Status (Offline)");
else if (!connection.isOnline() && connection.isConnected())
title = tr("Status (Registering)");
else if (it == isupport.end())
else if (it == isupport.end())
title = QStringLiteral("%1 (%2)")
.arg(QString::fromStdString( connection.getNickname() ))
.arg(QString::fromStdString( connection.getHostname() ));
else
title = QStringLiteral("%1 (%2)")
.arg(QString::fromStdString( connection.getNickname() ))
.arg(QString::fromStdString( it->second ));
else
title = QStringLiteral("%1 (%2)")
.arg(QString::fromStdString( connection.getNickname() ))
.arg(QString::fromStdString( it->second ));
setWindowTitle(title);
MdiManager::instance().renameWindowButton(this, title);
setWindowTitle(title);
MdiManager::instance().renameWindowButton(this, title);
}
void IWinStatus::printTo(const QString& target, const PrintType ptype, const QString& text)
{
MdiManager::instance().print(this, target, ptype, text);
MdiManager::instance().print(this, target, ptype, text);
}
void IWinStatus::printToTypes(const IWin::Type toType, const PrintType ptype, const QString& text)
{
MdiManager::instance().printToTypes(this, toType, ptype, text);
MdiManager::instance().printToTypes(this, toType, ptype, text);
}
void IWinStatus::printToActive(const PrintType ptype, const QString& text)
{
MdiManager::instance().printToActive(this, ptype, text);
MdiManager::instance().printToActive(this, ptype, text);
}
void IWinStatus::printToAll(const PrintType ptype, const QString& text)
{
MdiManager::instance().printToAll(this, ptype, text);
MdiManager::instance().printToAll(this, ptype, text);
}
IWinChannel* IWinStatus::createChannelWindow(const QString& name)
{
auto& mdiManager = MdiManager::instance();
IWin* subwinBase = mdiManager.findWindow(this, name);
if (!subwinBase)
subwinBase = mdiManager.createSubwindow(this, name, IWin::Type::Channel);
auto& mdiManager = MdiManager::instance();
IWin* subwinBase = mdiManager.findWindow(this, name);
if (!subwinBase)
subwinBase = mdiManager.createSubwindow(this, name, IWin::Type::Channel);
return dynamic_cast<IWinChannel*>(subwinBase);
return dynamic_cast<IWinChannel*>(subwinBase);
}
IWin* IWinStatus::createPrivateWindow(const QString& target, bool setFocus, Highlight buttonHighlight)
{
auto& mdiManager = MdiManager::instance();
IWin* subwinBase = mdiManager.findWindow(this, target);
if (!subwinBase)
subwinBase = mdiManager.createSubwindow(this, target, IWin::Type::Private, setFocus, buttonHighlight);
auto& mdiManager = MdiManager::instance();
IWin* subwinBase = mdiManager.findWindow(this, target);
if (!subwinBase)
subwinBase = mdiManager.createSubwindow(this, target, IWin::Type::Private, setFocus, buttonHighlight);
return subwinBase;
return subwinBase;
}
IWin* IWinStatus::getActiveWindow()
{
auto& mdiManager = MdiManager::instance();
if (mdiManager.currentWindow()->getStatusParent() != this)
return this;
return mdiManager.currentWindow();
auto& mdiManager = MdiManager::instance();
if (mdiManager.currentWindow()->getStatusParent() != this)
return this;
return mdiManager.currentWindow();
}
QList<IWin*> IWinStatus::subWindows() const
{
return MdiManager::instance().childrenOf(this);
return MdiManager::instance().childrenOf(this);
}
void IWinStatus::closeEvent(QCloseEvent* evt)
{
auto& mdiManager = MdiManager::instance();
auto& mdiManager = MdiManager::instance();
const auto& conf = ConfigMgr::instance();
if (StatusWindowCount == 1 ||
(connection.isConnected() && QMessageBox::question(this, tr("Close status window"), tr("This status window has a connection. Close?"))
== QMessageBox::No)) {
evt->ignore();
return;
}
if (StatusWindowCount == 1 ||
(connection.isConnected() && QMessageBox::question(this, tr("Close status window"), tr("This status window has a connection. Close?"))
== QMessageBox::No)) {
evt->ignore();
return;
}
if (connection.isConnected()) {
connect(&connection, &IRC::disconnected, [&] { closeAllChildren(); mdiManager.toMdiwin(this)->close(); });
if (connection.isConnected()) {
connect(&connection, &IRC::disconnected, [&] { closeAllChildren(); mdiManager.toMdiwin(this)->close(); });
const auto quitMsg = conf.common("QuitMessage").toStdString();
const auto e = connection.disconnectFromServer(quitMsg);
if (e != IRCError::NoError)
qWarning() << "disconnectFromServer returned:" << QString::fromStdString(IRCErrorToString(e));
evt->ignore();
return;
}
if (e != IRCError::NoError)
qWarning() << "disconnectFromServer returned:" << QString::fromStdString(IRCErrorToString(e));
evt->ignore();
return;
}
--StatusWindowCount;
--StatusWindowCount;
IWin::closeEvent(evt);
IWin::closeEvent(evt);
}
void IWinStatus::newLine(const QString& line)
{
InputHandler inHndl(connection);
inHndl.parse(*this, line);
InputHandler inHndl(connection);
inHndl.parse(*this, line);
}
void IWinStatus::closeAllChildren()
{
auto& mdiManager = MdiManager::instance();
QList<QMdiSubWindow*> subwinList = mdiManager.mdiChildrenOf(this);
for (QMdiSubWindow* subwin : subwinList)
subwin->close();
auto& mdiManager = MdiManager::instance();
QList<QMdiSubWindow*> subwinList = mdiManager.mdiChildrenOf(this);
for (QMdiSubWindow* subwin : subwinList)
subwin->close();
}
void IWinStatus::Qt_Eventloop_Hook()
{
const int nextInterval = connection.poll() ? 0 : 10;
QTimer::singleShot(nextInterval, this, std::bind(&IWinStatus::Qt_Eventloop_Hook, this));
const int nextInterval = connection.poll() ? 0 : 10;
QTimer::singleShot(nextInterval, this, std::bind(&IWinStatus::Qt_Eventloop_Hook, this));
}
void IWinStatus::disconnectedFromServer()
{
const auto printDisconnected = [this](IWin* sw){
sw->print(PrintType::ProgramInfo, tr("Disconnected"));
};
printDisconnected(this);
auto subwins = subWindows();
for (auto* sw : subwins) {
if (sw->getType() == IWin::Type::Channel) {
auto* chan = dynamic_cast<IWinChannel*>(sw);
chan->resetNicklist();
printDisconnected(chan);
}
else if (sw->getType() == IWin::Type::Private) {
printDisconnected(sw);
}
}
const auto printDisconnected = [this](IWin* sw){
sw->print(PrintType::ProgramInfo, tr("Disconnected"));
};
printDisconnected(this);
auto subwins = subWindows();
for (auto* sw : subwins) {
if (sw->getType() == IWin::Type::Channel) {
auto* chan = dynamic_cast<IWinChannel*>(sw);
chan->resetNicklist();
printDisconnected(chan);
}
else if (sw->getType() == IWin::Type::Private) {
printDisconnected(sw);
}
}
}
int IWinStatus::tabComplete(int index, QString& pattern)

@ -23,42 +23,42 @@ class IWinChannel;
class IWinStatus : public IWin
{
Q_OBJECT
Q_OBJECT
public:
explicit IWinStatus(const QString& buttonText);
explicit IWinStatus(const QString& buttonText);
bool print(const PrintType ptype, const QString& text) override;
void refreshWindowTitle() override;
bool print(const PrintType ptype, const QString& text) override;
void refreshWindowTitle() override;
void clear() override { view->resetView(); }
void printTo(const QString& target, const PrintType ptype, const QString& text);
void printToTypes(const IWin::Type toType, const PrintType ptype, const QString& text);
void printToActive(const PrintType ptype, const QString& text); // TODO test
void printToAll(const PrintType ptype, const QString& text);
IWinChannel* createChannelWindow(const QString& name);
IWin* createPrivateWindow(const QString& target, bool setFocus = true, Highlight buttonHighlight = HL_None);
IWin* getActiveWindow();
QList<IWin*> subWindows() const;
IRC& getConnection() { return connection; }
void printTo(const QString& target, const PrintType ptype, const QString& text);
void printToTypes(const IWin::Type toType, const PrintType ptype, const QString& text);
void printToActive(const PrintType ptype, const QString& text); // TODO test
void printToAll(const PrintType ptype, const QString& text);
IWinChannel* createChannelWindow(const QString& name);
IWin* createPrivateWindow(const QString& target, bool setFocus = true, Highlight buttonHighlight = HL_None);
IWin* getActiveWindow();
QList<IWin*> subWindows() const;
IRC& getConnection() { return connection; }
const IRC& getConnection() const { return connection; }
ILineEdit& getInputBox() { return *input; }
ILineEdit& getInputBox() { return *input; }
private:
void closeEvent(QCloseEvent* evt) override;
void newLine(const QString& line);
void closeAllChildren();
void Qt_Eventloop_Hook();
void disconnectedFromServer();
void closeEvent(QCloseEvent* evt) override;
void newLine(const QString& line);
void closeAllChildren();
void Qt_Eventloop_Hook();
void disconnectedFromServer();
int tabComplete(int index, QString& pattern);
static int StatusWindowCount;
QVBoxLayout* layout;
IIRCView* view;
ILineEdit* input;
static int StatusWindowCount;
QVBoxLayout* layout;
IIRCView* view;
ILineEdit* input;
IRC connection;
IRC connection;
SymbolScope menuSymbols;
SymbolScope menuSymbols;
};
#endif // IWINSTATUS_H

@ -14,189 +14,189 @@
#include <QDebug>
NicklistController::NicklistController(QListWidget* listbox, IRC& server, const QString& channel, QObject* parent)
: QObject(parent)
, m_listbox(listbox)
, m_server(server)
, m_channel(channel)
: QObject(parent)
, m_listbox(listbox)
, m_server(server)
, m_channel(channel)
{
ConfigMgr& conf = ConfigMgr::instance();
connect(&conf, &ConfigMgr::saved, this, &NicklistController::newConfiguration);
/* Precalculate m_prefixOrdered */
const auto& isupport = m_server.isupport();
const auto& prefix = isupport.at("PREFIX"); // PREFIX is always present
auto start = prefix.find_last_of(')');
if (start != std::string::npos) {
++start;
m_prefixOrdered = prefix.substr(start).c_str();
}
ConfigMgr& conf = ConfigMgr::instance();
connect(&conf, &ConfigMgr::saved, this, &NicklistController::newConfiguration);
/* Precalculate m_prefixOrdered */
const auto& isupport = m_server.isupport();
const auto& prefix = isupport.at("PREFIX"); // PREFIX is always present
auto start = prefix.find_last_of(')');
if (start != std::string::npos) {
++start;
m_prefixOrdered = prefix.substr(start).c_str();
}
}
void NicklistController::reload()
{
m_listbox->clear();
m_items.clear();
m_listbox->clear();
m_items.clear();
auto chan = m_server.getChannel(m_channel.toStdString());
if (!chan)
return;
auto chan = m_server.getChannel(m_channel.toStdString());
if (!chan)
return;
const auto& members = chan->members();
const auto& members = chan->members();
for (const auto& mem : members)
m_items << makeItemText(mem);
for (const auto& mem : members)
m_items << makeItemText(mem);
std::sort(m_items.begin(), m_items.end(),
[this](const QString& left, const QString& right) {
return lessThan(left, right);
});
std::sort(m_items.begin(), m_items.end(),
[this](const QString& left, const QString& right) {
return lessThan(left, right);
});
m_listbox->addItems(m_items);
newConfiguration();
m_listbox->addItems(m_items);
newConfiguration();
}
void NicklistController::clear()
{
m_listbox->clear();
m_listbox->clear();
}
bool NicklistController::lessThan(const QString& left, const QString& right)
{
auto li = m_prefixOrdered.indexOf(left[0]);
auto ri = m_prefixOrdered.indexOf(right[0]);
/* Both sides got a prefix */
if (li != -1 && ri != -1) {
if (left[0] == right[0])
return left.mid(1).compare(right.mid(1), Qt::CaseInsensitive) < 0;
else
return li < ri;
}
/* Neither side got a prefix */
else if (li == -1 && ri == -1)
return left.compare(right, Qt::CaseInsensitive) < 0;
/* One side got a prefix */
else
return ri == -1;
auto li = m_prefixOrdered.indexOf(left[0]);
auto ri = m_prefixOrdered.indexOf(right[0]);
/* Both sides got a prefix */
if (li != -1 && ri != -1) {
if (left[0] == right[0])
return left.mid(1).compare(right.mid(1), Qt::CaseInsensitive) < 0;
else
return li < ri;
}
/* Neither side got a prefix */
else if (li == -1 && ri == -1)
return left.compare(right, Qt::CaseInsensitive) < 0;
/* One side got a prefix */
else
return ri == -1;
}
void NicklistController::addMember(const IRCMemberEntry& member)
{
QString newItem = makeItemText(member);
int pos = findNewItemPosition(newItem);
m_items.insert(pos, newItem);
m_listbox->insertItem(pos, newItem);
QString newItem = makeItemText(member);
int pos = findNewItemPosition(newItem);
m_items.insert(pos, newItem);
m_listbox->insertItem(pos, newItem);
QListWidgetItem* item = m_listbox->item(pos);
updatePrefixColor(item, std::nullopt);
QListWidgetItem* item = m_listbox->item(pos);
updatePrefixColor(item, std::nullopt);
}
void NicklistController::updateMember(const QString& nickname, const IRCMemberEntry& member)
{
// Find position of the item in the list.
// TODO use this loop also to find new item position.
int currPos = -1;
for (int i = 0; i < m_items.count(); ++i) {
QString item = m_items[i];
if (m_prefixOrdered.indexOf(item[0]) > -1)
item = item.mid(1);
if (item == nickname) {
currPos = i;
break;
}
}
// We don't have this nickname in this channel, and it is safe to ignore it silently.
// This is because when a nickname changes, that user might not be on all channels we're on.
if (currPos == -1) return;
QListWidgetItem* lbitem = m_listbox->item(currPos);
bool isSelected = lbitem->isSelected();
m_items.removeAt(currPos);
m_listbox->removeItemWidget(lbitem);
delete lbitem;
QString newItemText = makeItemText(member);
int newPos = findNewItemPosition(newItemText);
lbitem = new QListWidgetItem(newItemText);
m_listbox->insertItem(newPos, lbitem);
m_items.insert(newPos, newItemText);
lbitem->setSelected(isSelected);
updatePrefixColor(lbitem, std::nullopt);
// Find position of the item in the list.
// TODO use this loop also to find new item position.
int currPos = -1;
for (int i = 0; i < m_items.count(); ++i) {
QString item = m_items[i];
if (m_prefixOrdered.indexOf(item[0]) > -1)
item = item.mid(1);
if (item == nickname) {
currPos = i;
break;
}
}
// We don't have this nickname in this channel, and it is safe to ignore it silently.
// This is because when a nickname changes, that user might not be on all channels we're on.
if (currPos == -1) return;
QListWidgetItem* lbitem = m_listbox->item(currPos);
bool isSelected = lbitem->isSelected();
m_items.removeAt(currPos);
m_listbox->removeItemWidget(lbitem);
delete lbitem;
QString newItemText = makeItemText(member);
int newPos = findNewItemPosition(newItemText);
lbitem = new QListWidgetItem(newItemText);
m_listbox->insertItem(newPos, lbitem);
m_items.insert(newPos, newItemText);
lbitem->setSelected(isSelected);
updatePrefixColor(lbitem, std::nullopt);
}
void NicklistController::removeMember(const IRCMemberEntry& member)
{
QString itemText = makeItemText(member);
int pos = 0;
int delpos = -1;
while (pos < m_items.count()) {
const QString& item = m_items[pos];
if (item == itemText) {
delpos = pos;
break;
}
++pos;
}
if (delpos == -1) {
qWarning() << "Got request to delete" << itemText << "from" << m_channel << "but it was not found!";
return;
}
m_items.removeAt(delpos);
QListWidgetItem* lbitem = m_listbox->item(delpos);
m_listbox->removeItemWidget(lbitem);
delete lbitem;
QString itemText = makeItemText(member);
int pos = 0;
int delpos = -1;
while (pos < m_items.count()) {
const QString& item = m_items[pos];
if (item == itemText) {
delpos = pos;
break;
}
++pos;
}
if (delpos == -1) {
qWarning() << "Got request to delete" << itemText << "from" << m_channel << "but it was not found!";
return;
}
m_items.removeAt(delpos);
QListWidgetItem* lbitem = m_listbox->item(delpos);
m_listbox->removeItemWidget(lbitem);
delete lbitem;
}
QString NicklistController::makeItemText(const IRCMemberEntry& member)
{
QString item;
auto modes = member.modes();
if (!modes.empty()) {
auto prefixes = m_server.toMemberPrefix(modes);
item += prefixes[0];
}
item += member.member()->prefix().nickname().c_str();
return item;
QString item;
auto modes = member.modes();
if (!modes.empty()) {
auto prefixes = m_server.toMemberPrefix(modes);
item += prefixes[0];
}
item += member.member()->prefix().nickname().c_str();
return item;
}
int NicklistController::findNewItemPosition(const QString& text)
{
int pos = 0;
while (pos < m_items.count()) {
const QString& item = m_items[pos];
if (lessThan(text, item))
break;
int pos = 0;
while (pos < m_items.count()) {
const QString& item = m_items[pos];
if (lessThan(text, item))
break;
++pos;
}
++pos;
}
return pos;
return pos;
}
void NicklistController::updatePrefixColor(QListWidgetItem* item, std::optional<QColor> color)
{
if (!color && m_prefixOrdered.indexOf(item->text()[0]) == -1)
return;
ConfigMgr& conf = ConfigMgr::instance();
if (!color) {
color = conf.prefixColor(item->text()[0]);
if (!color) return;
}
item->setForeground(*color);
if (!color && m_prefixOrdered.indexOf(item->text()[0]) == -1)
return;
ConfigMgr& conf = ConfigMgr::instance();
if (!color) {
color = conf.prefixColor(item->text()[0]);
if (!color) return;
}
item->setForeground(*color);
}
void NicklistController::newConfiguration()
{
ConfigMgr& conf = ConfigMgr::instance();
for (int i = 0; i < m_listbox->count(); ++i) {
updatePrefixColor(m_listbox->item(i) , QColor(conf.color("ListboxForeground")));
updatePrefixColor(m_listbox->item(i) , std::nullopt);
}
ConfigMgr& conf = ConfigMgr::instance();
for (int i = 0; i < m_listbox->count(); ++i) {
updatePrefixColor(m_listbox->item(i) , QColor(conf.color("ListboxForeground")));
updatePrefixColor(m_listbox->item(i) , std::nullopt);
}
}

@ -19,27 +19,27 @@ class IRCMemberEntry;
class NicklistController : public QObject
{
Q_OBJECT
Q_OBJECT
public:
NicklistController(QListWidget* listbox, IRC& server, const QString& channel, QObject* parent = nullptr);
void reload();
void clear();
void addMember(const IRCMemberEntry& member);
void updateMember(const QString& nickname, const IRCMemberEntry& member);
void removeMember(const IRCMemberEntry& member);
NicklistController(QListWidget* listbox, IRC& server, const QString& channel, QObject* parent = nullptr);
void reload();
void clear();
void addMember(const IRCMemberEntry& member);
void updateMember(const QString& nickname, const IRCMemberEntry& member);
void removeMember(const IRCMemberEntry& member);
private:
bool lessThan(const QString& left, const QString& right);
QString makeItemText(const IRCMemberEntry& member);
int findNewItemPosition(const QString& text);
void updatePrefixColor(QListWidgetItem* item, std::optional<QColor> color);
void newConfiguration();
QListWidget* m_listbox;
IRC& m_server;
const QString m_channel;
QStringList m_items;
QString m_prefixOrdered; //! Nickname prefix (@%+) in order
bool lessThan(const QString& left, const QString& right);
QString makeItemText(const IRCMemberEntry& member);
int findNewItemPosition(const QString& text);
void updatePrefixColor(QListWidgetItem* item, std::optional<QColor> color);
void newConfiguration();
QListWidget* m_listbox;
IRC& m_server;
const QString m_channel;
QStringList m_items;
QString m_prefixOrdered; //! Nickname prefix (@%+) in order
};
#endif // NICKLISTCONTROLLER_H

@ -11,27 +11,27 @@
#include <QDateTime>
AboutIIRC::AboutIIRC(QWidget *parent) :
QDialog(parent),
ui(new Ui::AboutIIRC)
QDialog(parent),
ui(new Ui::AboutIIRC)
{
ui->setupUi(this);
ui->setupUi(this);
QDateTime cur = QDateTime::currentDateTime();
QString year = QString::number(cur.date().year());
QDateTime cur = QDateTime::currentDateTime();
QString year = QString::number(cur.date().year());
QString text = ui->textBrowser->toHtml();
text = text.replace("{year}", year);
text = text.replace("{ver}", VERSION_STRING);
QString text = ui->textBrowser->toHtml();
text = text.replace("{year}", year);
text = text.replace("{ver}", VERSION_STRING);
if constexpr (BUILD_TYPE == BuildType::packaged)
text = text.replace("{buildtype}", QStringLiteral("Packaged"));
else if constexpr (BUILD_TYPE == BuildType::standalone)
text = text.replace("{buildtype}", QStringLiteral("Stand-alone"));
if constexpr (BUILD_TYPE == BuildType::packaged)
text = text.replace("{buildtype}", QStringLiteral("Packaged"));
else if constexpr (BUILD_TYPE == BuildType::standalone)
text = text.replace("{buildtype}", QStringLiteral("Stand-alone"));
ui->textBrowser->setHtml(text);
ui->textBrowser->setHtml(text);
}
AboutIIRC::~AboutIIRC()
{
delete ui;
delete ui;
}

@ -16,14 +16,14 @@ class AboutIIRC;
class AboutIIRC : public QDialog
{
Q_OBJECT
Q_OBJECT
public:
explicit AboutIIRC(QWidget *parent = nullptr);
~AboutIIRC();
explicit AboutIIRC(QWidget *parent = nullptr);
~AboutIIRC();
private:
Ui::AboutIIRC *ui;
Ui::AboutIIRC *ui;
};
#endif // ABOUTIIRC_H

@ -11,31 +11,31 @@
int ButtonbarMgr::WindowTypeToGroupPos(const IWin* subwin)
{
switch (subwin->getType()) {
case IWin::Type::Status:
return 1;
case IWin::Type::Channel:
return 2;
case IWin::Type::Private:
return 3;
default:
return 0;
}
switch (subwin->getType()) {
case IWin::Type::Status:
return 1;
case IWin::Type::Channel:
return 2;
case IWin::Type::Private:
return 3;
default:
return 0;
}
}
ButtonbarMgr::ButtonbarMgr(QToolBar *parent)
: QObject(parent)
, m_buttonBar(*parent)
: QObject(parent)
, m_buttonBar(*parent)
{
m_buttonBar.setContextMenuPolicy(Qt::CustomContextMenu);
m_buttonBar.setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
m_buttonBar.setIconSize(QSize(16, 16));
m_buttonBar.setFloatable(false);
m_buttonBar.setContextMenuPolicy(Qt::CustomContextMenu);
m_buttonBar.setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
m_buttonBar.setIconSize(QSize(16, 16));
m_buttonBar.setFloatable(false);
connect(&m_buttonBar, &QToolBar::customContextMenuRequested, this, &ButtonbarMgr::buttonBarContextMenu);
m_others_rightSep = m_buttonBar.addSeparator();
connect(&m_buttonBar, &QToolBar::customContextMenuRequested, this, &ButtonbarMgr::buttonBarContextMenu);
m_others_rightSep = m_buttonBar.addSeparator();
connect(&m_buttonBar, &QToolBar::orientationChanged, [this](Qt::Orientation o){
connect(&m_buttonBar, &QToolBar::orientationChanged, [this](Qt::Orientation o){
QHashIterator<IWin*,QAction*> it(m_winbtn);
while (it.hasNext()) {
auto* action = it.next().value();
@ -45,12 +45,12 @@ ButtonbarMgr::ButtonbarMgr(QToolBar *parent)
else
widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
}
});
});
}
void ButtonbarMgr::addButton(IWin* subwin, QMdiSubWindow* mdiwin)
{
auto* nn = findNextNeighbour(subwin);
auto* nn = findNextNeighbour(subwin);
auto btn = Button(mdiwin);
btn.toolButton->setMaximumWidth(150);
@ -66,84 +66,84 @@ void ButtonbarMgr::addButton(IWin* subwin, QMdiSubWindow* mdiwin)
auto btnWidget = buttons(subwin).back().toolButton;
connect(btnWidget, &QToolButton::clicked, [this,btnWidget,mdiwin](bool checked){
if (!checked) {
if (!checked) {
btnWidget->setChecked(true);
return;
}
emit changeWindow(mdiwin);
});
return;
}
emit changeWindow(mdiwin);
});
}
void ButtonbarMgr::delButton(IWin* subwin)
{
auto clear = [this](Button& button){
m_buttonBar.removeAction(button.action);
button.menu->deleteLater();
button.action->deleteLater();
button.toolButton->deleteLater();
};
m_winbtn.remove(subwin);
if (subwin->getType() != IWin::Type::Status
&& subwin->getType() != IWin::Type::Channel
&& subwin->getType() != IWin::Type::Private)
{
int otherPos = buttonPosition(m_others, subwin);
if (otherPos > -1) {
clear(m_others[otherPos]);
m_others.removeAt(otherPos);
}
return;
}
if (subwin->getType() == IWin::Type::Status) {
deleteGroup(subwin);
return;
}
ButtonGroup* group = tryFindGroup(subwin);
if (!group) {
return;
}
for (int i = 0; i < group->size; ++i) {
QList<Button>& buttons = (*group)[i];
int buttonPos = buttonPosition(buttons, subwin);
if (buttonPos < 0) continue;
Button& button = buttons[buttonPos];
if (button.subwin->widget() == subwin) {
clear(button);
buttons.removeAt(buttonPos);
break;
}
}
auto clear = [this](Button& button){
m_buttonBar.removeAction(button.action);
button.menu->deleteLater();
button.action->deleteLater();
button.toolButton->deleteLater();
};
m_winbtn.remove(subwin);
if (subwin->getType() != IWin::Type::Status
&& subwin->getType() != IWin::Type::Channel
&& subwin->getType() != IWin::Type::Private)
{
int otherPos = buttonPosition(m_others, subwin);
if (otherPos > -1) {
clear(m_others[otherPos]);
m_others.removeAt(otherPos);
}
return;
}
if (subwin->getType() == IWin::Type::Status) {
deleteGroup(subwin);
return;
}
ButtonGroup* group = tryFindGroup(subwin);
if (!group) {
return;
}
for (int i = 0; i < group->size; ++i) {
QList<Button>& buttons = (*group)[i];
int buttonPos = buttonPosition(buttons, subwin);
if (buttonPos < 0) continue;
Button& button = buttons[buttonPos];
if (button.subwin->widget() == subwin) {
clear(button);
buttons.removeAt(buttonPos);
break;
}
}
}
void ButtonbarMgr::reloadButtonName(IWin* subwin, const QString& buttonText)
{
Button* button = findButton(subwin);
if (button) {
button->toolButton->setText(buttonText);
Button* button = findButton(subwin);
if (button) {
button->toolButton->setText(buttonText);
button->toolButton->setToolTip(buttonText);
button->menuHead->setText(buttonText);
}
}
}
void ButtonbarMgr::subwinActivated(IWin* prev, IWin* next)
{
if (prev) {
Button* button{ findButton(prev) };
if (button)
button->toolButton->setChecked(false);
}
if (next) {
Button* button{ findButton(next) };
if (button) {
if (prev) {
Button* button{ findButton(prev) };
if (button)
button->toolButton->setChecked(false);
}
if (next) {
Button* button{ findButton(next) };
if (button) {
button->toolButton->setChecked(true);
highlight(next, HL_None);
}
}
}
}
void ButtonbarMgr::highlight(IWin* window, Highlight state)
@ -180,72 +180,72 @@ void ButtonbarMgr::highlight(IWin* window, Highlight state)
void ButtonbarMgr::buttonBarContextMenu(const QPoint& pos)
{
QAction* action = m_buttonBar.actionAt(pos);
if (!action)
return;
QAction* action = m_buttonBar.actionAt(pos);
if (!action)
return;
IWin *subwin = m_winbtn.key(action);
if (!subwin)
return;
IWin *subwin = m_winbtn.key(action);
if (!subwin)
return;
Button* button = findButton(subwin);
if (!button)
return;
Button* button = findButton(subwin);
if (!button)
return;
QPoint gpos = m_buttonBar.mapToGlobal(pos);
button->menu->popup(gpos);
QPoint gpos = m_buttonBar.mapToGlobal(pos);
button->menu->popup(gpos);
}
ButtonbarMgr::ButtonGroup* ButtonbarMgr::tryFindGroup(IWin* subwin)
{
auto lcontains = [](const QList<Button>& list, IWin* sw){
for (const auto& item : list) {
if (item.subwin->widget() == sw)
return true;
}
return false;
};
ButtonGroup* null_grp{ nullptr };
for (auto& group : m_groups) {
for (int i = 0; i < group.size; ++i) {
auto& list = group[i];
if (lcontains(list, subwin))
return &group;
}
}
return null_grp;
auto lcontains = [](const QList<Button>& list, IWin* sw){
for (const auto& item : list) {
if (item.subwin->widget() == sw)
return true;
}
return false;
};
ButtonGroup* null_grp{ nullptr };
for (auto& group : m_groups) {
for (int i = 0; i < group.size; ++i) {
auto& list = group[i];
if (lcontains(list, subwin))
return &group;
}
}
return null_grp;
}
QList<ButtonbarMgr::Button>& ButtonbarMgr::buttons(IWin* subwin)
{
if (subwin->getType() != IWin::Type::Status
&& subwin->getType() != IWin::Type::Channel
&& subwin->getType() != IWin::Type::Private)
{
return m_others;
}
ButtonGroup* group = tryFindGroup(subwin);
if (group)
return (*group)[static_cast<int>(subwin->getType())-1];
if (subwin->getType() == IWin::Type::Status) {
m_groups.emplace_back();
m_groups.back().rightSep = m_buttonBar.addSeparator();
return m_groups.back().status;
}
IWin* parent = subwin->getParent();
group = tryFindGroup(parent);
if (!group)
return m_others;
if (subwin->getType() == IWin::Type::Channel)
return group->channel;
else // Implicit "Private"
return group->privmsg;
if (subwin->getType() != IWin::Type::Status
&& subwin->getType() != IWin::Type::Channel
&& subwin->getType() != IWin::Type::Private)
{
return m_others;
}
ButtonGroup* group = tryFindGroup(subwin);
if (group)
return (*group)[static_cast<int>(subwin->getType())-1];
if (subwin->getType() == IWin::Type::Status) {
m_groups.emplace_back();
m_groups.back().rightSep = m_buttonBar.addSeparator();
return m_groups.back().status;
}
IWin* parent = subwin->getParent();
group = tryFindGroup(parent);
if (!group)
return m_others;
if (subwin->getType() == IWin::Type::Channel)
return group->channel;
else // Implicit "Private"
return group->privmsg;
}
QAction* ButtonbarMgr::findNextNeighbour(IWin* subwin)
@ -271,82 +271,82 @@ QAction* ButtonbarMgr::findNextNeighbour(IWin* subwin)
ButtonbarMgr::Button* ButtonbarMgr::findButton(IWin* subwin)
{
if (!subwin)
return nullptr;
else {
ButtonGroup* group = tryFindGroup(subwin);
if (group) {
for (int i = 0; i < group->size; ++i) {
QList<Button>& list = (*group)[i];
int bpos = buttonPosition(list, subwin);
if (bpos > -1)
return &list[bpos];
}
}
}
int otherPos = buttonPosition(m_others, subwin);
if (otherPos < 0)
return nullptr;
else {
Button& button = m_others[otherPos];
return &button;
}
if (!subwin)
return nullptr;
else {
ButtonGroup* group = tryFindGroup(subwin);
if (group) {
for (int i = 0; i < group->size; ++i) {
QList<Button>& list = (*group)[i];
int bpos = buttonPosition(list, subwin);
if (bpos > -1)
return &list[bpos];
}
}
}
int otherPos = buttonPosition(m_others, subwin);
if (otherPos < 0)
return nullptr;
else {
Button& button = m_others[otherPos];
return &button;
}
}
void ButtonbarMgr::deleteGroup(IWin* statuswin)
{
auto it = m_groups.begin();
for (; it != m_groups.end(); ++it) {
if ((*it).status[0].subwin->widget() == qobject_cast<QWidget*>(statuswin))
break;
}
ButtonGroup& group = *it;
for (int i = 0; i < group.size; ++i) {
QList<Button>& list = group[i];
for (auto& item : list) {
item.menuHead->deleteLater();
item.menuSep->deleteLater();
item.menuClose->deleteLater();
item.menu->deleteLater();
item.action->deleteLater();
item.toolButton->deleteLater();
}
}
group.rightSep->deleteLater();
m_groups.erase(it);
auto it = m_groups.begin();
for (; it != m_groups.end(); ++it) {
if ((*it).status[0].subwin->widget() == qobject_cast<QWidget*>(statuswin))
break;
}
ButtonGroup& group = *it;
for (int i = 0; i < group.size; ++i) {
QList<Button>& list = group[i];
for (auto& item : list) {
item.menuHead->deleteLater();
item.menuSep->deleteLater();
item.menuClose->deleteLater();
item.menu->deleteLater();
item.action->deleteLater();
item.toolButton->deleteLater();
}
}
group.rightSep->deleteLater();
m_groups.erase(it);
}
int ButtonbarMgr::buttonPosition(QList<ButtonbarMgr::Button>& list, IWin* sw)
{
for (int i = 0; i < list.count(); ++i) {
ButtonbarMgr::Button& item = list[i];
if (item.subwin->widget() == sw)
return i;
}
return -1;
for (int i = 0; i < list.count(); ++i) {
ButtonbarMgr::Button& item = list[i];
if (item.subwin->widget() == sw)
return i;
}
return -1;
}
ButtonbarMgr::Button::Button(QMdiSubWindow* parent)
: subwin(parent)
: subwin(parent)
{
const QString& buttonText = qobject_cast<IWin*>(parent->widget())->getButtonText();
const QString& buttonText = qobject_cast<IWin*>(parent->widget())->getButtonText();
toolButton = new QToolButton(parent);
toolButton->setCheckable(true);
toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toolButton->setCheckable(true);
toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toolButton->setIcon(parent->windowIcon());
toolButton->setText(buttonText);
toolButton->setToolTip(buttonText);
menu = new QMenu(parent);
menuHead = menu->addAction(buttonText);
QFont f = menuHead->font();
f.setBold(true);
menuHead->setFont(f);
menuHead->setDisabled(true);
menuSep = menu->addSeparator();
menuClose = menu->addAction(tr("Close"));
connect(menuClose, &QAction::triggered, [parent](bool){
parent->close();
});
menu = new QMenu(parent);
menuHead = menu->addAction(buttonText);
QFont f = menuHead->font();
f.setBold(true);
menuHead->setFont(f);
menuHead->setDisabled(true);
menuSep = menu->addSeparator();
menuClose = menu->addAction(tr("Close"));
connect(menuClose, &QAction::triggered, [parent](bool){
parent->close();
});
}

@ -27,69 +27,69 @@ enum Highlight {
class ButtonbarMgr : public QObject
{
Q_OBJECT
Q_OBJECT
struct Button
{
explicit Button(QMdiSubWindow* parent);
QMdiSubWindow* subwin{ nullptr };
struct Button
{
explicit Button(QMdiSubWindow* parent);
QMdiSubWindow* subwin{ nullptr };
QAction* action{ nullptr };
QToolButton* toolButton{ nullptr };
QAction* action{ nullptr };
QToolButton* toolButton{ nullptr };
QMenu* menu{ nullptr };
QAction* menuHead{ nullptr };
QAction* menuSep{ nullptr };
QAction* menuClose{ nullptr };
Highlight hlState{ HL_None };
};
QMenu* menu{ nullptr };
QAction* menuHead{ nullptr };
QAction* menuSep{ nullptr };
QAction* menuClose{ nullptr };
Highlight hlState{ HL_None };
};
struct ButtonGroup
{
QList<Button>& operator[](int i) {
switch (i) {
case 0: return status;
case 1: return channel;
case 2: return privmsg;
default: return blank;
}
}
struct ButtonGroup
{
QList<Button>& operator[](int i) {
switch (i) {
case 0: return status;
case 1: return channel;
case 2: return privmsg;
default: return blank;
}
}
QList<Button> status;
QList<Button> channel;
QList<Button> privmsg;
const int size = 3;
QList<Button> status;
QList<Button> channel;
QList<Button> privmsg;
const int size = 3;
QAction* rightSep;
QList<Button> blank;
};
QAction* rightSep;
QList<Button> blank;
};
static int WindowTypeToGroupPos(const IWin* subwin);
static int WindowTypeToGroupPos(const IWin* subwin);
public:
explicit ButtonbarMgr(QToolBar *parent);
void addButton(IWin* subwin, QMdiSubWindow* mdiwin);
void delButton(IWin* subwin);
void reloadButtonName(IWin* subwin, const QString& buttonText);
void subwinActivated(IWin* prev, IWin* next);
void highlight(IWin* window, Highlight state);
explicit ButtonbarMgr(QToolBar *parent);
void addButton(IWin* subwin, QMdiSubWindow* mdiwin);
void delButton(IWin* subwin);
void reloadButtonName(IWin* subwin, const QString& buttonText);
void subwinActivated(IWin* prev, IWin* next);
void highlight(IWin* window, Highlight state);
private:
void buttonBarContextMenu(const QPoint& pos);
static int buttonPosition(QList<ButtonbarMgr::Button>& list, IWin* sw);
ButtonGroup* tryFindGroup(IWin* subwin);
QList<Button>& buttons(IWin* subwin);
QAction* findNextNeighbour(IWin* subwin);
Button* findButton(IWin* subwin);
void deleteGroup(IWin* statuswin);
QList<Button> m_others;
QAction* m_others_rightSep;
std::list<ButtonGroup> m_groups;
QHash<IWin*,QAction*> m_winbtn;
QToolBar& m_buttonBar;
void buttonBarContextMenu(const QPoint& pos);
static int buttonPosition(QList<ButtonbarMgr::Button>& list, IWin* sw);
ButtonGroup* tryFindGroup(IWin* subwin);
QList<Button>& buttons(IWin* subwin);
QAction* findNextNeighbour(IWin* subwin);
Button* findButton(IWin* subwin);
void deleteGroup(IWin* statuswin);
QList<Button> m_others;
QAction* m_others_rightSep;
std::list<ButtonGroup> m_groups;
QHash<IWin*,QAction*> m_winbtn;
QToolBar& m_buttonBar;
signals:
void changeWindow(QMdiSubWindow* mdiwin);
void changeWindow(QMdiSubWindow* mdiwin);
};
#endif // BUTTONBARMGR_H

@ -2,7 +2,7 @@ set(component "IdealIRC")
# Windows resources. Application icon.
if (MSVC)
set(WINDOWS_RC_FILE "${IIRC_SOURCE_DIR}/Resources/windows.rc")
set(WINDOWS_RC_FILE "${IIRC_SOURCE_DIR}/Resources/windows.rc")
endif()
list(APPEND ${component}_SOURCES
@ -36,7 +36,7 @@ qt5_use_modules(${component} Widgets)
# This will hide the Windows Command Prompt.
if (MSVC)
set_property(TARGET ${component} PROPERTY WIN32_EXECUTABLE true)
set_property(TARGET ${component} PROPERTY WIN32_EXECUTABLE true)
endif()
target_link_libraries(${component}
@ -48,7 +48,7 @@ target_link_libraries(${component}
Script
ScriptDialog
ScriptFunctions
Resources
Resources
)
target_include_directories(${component} PRIVATE ${CMAKE_SOURCE_DIR})

@ -112,8 +112,8 @@ const std::string& getDefault(const DefaultMap& map, const std::string& key)
ConfigMgr& ConfigMgr::instance()
{
static ConfigMgr inst;
return inst;
static ConfigMgr inst;
return inst;
}
// Constructor is private, use static instance()
@ -123,8 +123,8 @@ ConfigMgr::ConfigMgr()
void ConfigMgr::save()
{
ini.flush();
emit saved();
ini.flush();
emit saved();
}
QString ConfigMgr::geometry(const QString& key) const
@ -181,7 +181,7 @@ QHash<QString, QString> ConfigMgr::color() const
const auto val = QString::fromStdString( ini.read("Color", i) );
ret.insert(key, val);
}
return ret;
return ret;
}
QVector<std::pair<QChar, QColor> > ConfigMgr::prefixColor() const
@ -253,11 +253,11 @@ void ConfigMgr::setColorPalette(const QHash<QString, QString>& palette)
void ConfigMgr::setPrefixColorPalette(const QVector<std::pair<QChar, QColor>>& palette)
{
for (const auto& item : palette) {
const auto key = std::to_string(item.first.toLatin1());
const auto val = item.second.name();
for (const auto& item : palette) {
const auto key = std::to_string(item.first.toLatin1());
const auto val = item.second.name();
ini.write("PrefixColor", key, val.toStdString());
}
}
}
void ConfigMgr::addScript(const QString& path)

@ -19,39 +19,39 @@
class ConfigMgr : public QObject
{
Q_OBJECT
Q_OBJECT
public:
static ConfigMgr& instance();
void save();
static ConfigMgr& instance();
void save();
QString geometry(const QString& key) const;
QString connection(const QString& key) const;
QString common(const QString& key) const;
QString color(const QString& key) const;
QString geometry(const QString& key) const;
QString connection(const QString& key) const;
QString common(const QString& key) const;
QString color(const QString& key) const;
QString window(const QString& key) const;
std::optional<QColor> prefixColor(const QChar& prefix) const;
QHash<QString,QString> color() const;
QVector<std::pair<QChar,QColor>> prefixColor() const; // QVector of pairs preserves the order.
QString logging(const QString& key) const;
QStringList scripts() const;
void setGeometry(const QString& key, const QString& value);
void setConnection(const QString& key, const QString& value);
void setCommon(const QString& key, const QString& value);
std::optional<QColor> prefixColor(const QChar& prefix) const;
QHash<QString,QString> color() const;
QVector<std::pair<QChar,QColor>> prefixColor() const; // QVector of pairs preserves the order.
QString logging(const QString& key) const;
QStringList scripts() const;
void setGeometry(const QString& key, const QString& value);
void setConnection(const QString& key, const QString& value);
void setCommon(const QString& key, const QString& value);
void setWindow(const QString& key, const QString& value);
void setLogging(const QString& key, const QString& value);
void setColorPalette(const QHash<QString,QString>& palette);
void setPrefixColorPalette(const QVector<std::pair<QChar,QColor>>& palette);
void addScript(const QString& path);
void delScript(const QString& path);
void setLogging(const QString& key, const QString& value);
void setColorPalette(const QHash<QString,QString>& palette);
void setPrefixColorPalette(const QVector<std::pair<QChar,QColor>>& palette);
void addScript(const QString& path);
void delScript(const QString& path);
private:
ConfigMgr();
IniFile ini;
ConfigMgr();
IniFile ini;
signals:
void saved();
void saved();
};
#endif // CONFIGMGR_H

@ -27,18 +27,18 @@
#include <QTimer>
IRC::IRC(IWinStatus& status)
: m_status(status)
: m_status(status)
{
static int idCount{ 0 };
m_id = ++idCount;
static int idCount{ 0 };
m_id = ++idCount;
}
void IRC::disconnectForExit(const QString& quitMessage)
{
m_disconnectForExit = true;
std::string quitMessageConv = quitMessage.toStdString();
if (disconnectFromServer(quitMessageConv) == IRCError::NotConnected)
qWarning() << "Trying to disconnect from an already closed connection.";
m_disconnectForExit = true;
std::string quitMessageConv = quitMessage.toStdString();
if (disconnectFromServer(quitMessageConv) == IRCError::NotConnected)
qWarning() << "Trying to disconnect from an already closed connection.";
}
int IRC::channelAutoComplete(int index, QString& pattern)
@ -76,21 +76,21 @@ Retry:
void IRC::setIgnoreVerbosity(const QStringList& commandsAndNumerics)
{
for (const QString& entry : commandsAndNumerics)
m_ignoreVerbosity.push_back(entry.toUpper());
m_ignoreVerbosity.removeDuplicates();
for (const QString& entry : commandsAndNumerics)
m_ignoreVerbosity.push_back(entry.toUpper());
m_ignoreVerbosity.removeDuplicates();
}
void IRC::unsetIgnoreVerbosity(const QStringList& commandsAndNumerics)
{
for (const QString& entry : commandsAndNumerics)
m_ignoreVerbosity.removeOne(entry.toUpper());
for (const QString& entry : commandsAndNumerics)
m_ignoreVerbosity.removeOne(entry.toUpper());
}
bool IRC::ignoreVerbosity(const std::string& cmd)
{
QString qcmd{ cmd.c_str() };
return m_ignoreVerbosity.contains(qcmd.toUpper());
QString qcmd{ cmd.c_str() };
return m_ignoreVerbosity.contains(qcmd.toUpper());
}
void IRC::setReconnectDetails(bool ssl, const std::string& host, const std::string& port)
@ -103,13 +103,13 @@ void IRC::setReconnectDetails(bool ssl, const std::string& host, const std::stri
void IRC::rejoinChannels()
{
QStringList targetList;
for (IWin* subwin : m_status.subWindows())
targetList << subwin->getButtonText();
if (targetList.isEmpty())
return;
QString targets = targetList.join(',');
command(Command::IRC::JOIN, { targets.toStdString() });
QStringList targetList;
for (IWin* subwin : m_status.subWindows())
targetList << subwin->getButtonText();
if (targetList.isEmpty())
return;
QString targets = targetList.join(',');
command(Command::IRC::JOIN, { targets.toStdString() });
}
void IRC::onRegistered()
@ -118,14 +118,14 @@ void IRC::onRegistered()
emit registered();
const auto& config = ConfigMgr::instance();
if (config.common("RejoinChannelsOnConnect") == "1")
rejoinChannels();
rejoinChannels();
contextualScriptEvent(&m_status, ScriptEvent::Connected);
}
void IRC::onConnected()
{
emit connected();
emit connected();
}
void IRC::onConnectedWithSSLExceptions(const std::vector<IRCError>& codes)
@ -139,21 +139,21 @@ void IRC::onConnectedWithSSLExceptions(const std::vector<IRCError>& codes)
void IRC::onDisconnected()
{
emit disconnected();
emit memberListClearedForAll();
if (m_disconnectForExit)
emit readyForExit();
contextualScriptEvent(&m_status, ScriptEvent::Disconnected);
if (m_reconnectDetails.has_value()) {
setHostname(m_reconnectDetails->host, m_reconnectDetails->ssl);
setPort(m_reconnectDetails->port);
m_reconnectDetails.reset();
const auto e = tryConnect();
if (e != IRCError::NoError)
qWarning() << "tryConnect returned:" << QString::fromStdString(IRCErrorToString(e));
}
else {
emit disconnected();
emit memberListClearedForAll();
if (m_disconnectForExit)
emit readyForExit();
contextualScriptEvent(&m_status, ScriptEvent::Disconnected);
if (m_reconnectDetails.has_value()) {
setHostname(m_reconnectDetails->host, m_reconnectDetails->ssl);
setPort(m_reconnectDetails->port);
m_reconnectDetails.reset();
const auto e = tryConnect();
if (e != IRCError::NoError)
qWarning() << "tryConnect returned:" << QString::fromStdString(IRCErrorToString(e));
}
else {
auto& conf = ConfigMgr::instance();
bool reconnect = conf.common("Reconnect").toInt();
if (reconnect && !m_expectDisconnect) {
@ -193,18 +193,18 @@ void IRC::onMsgNick(const IRCPrefix& sender, const std::string& newNickname, con
else
msg = fmt::format("{} is now known as {}", sender.toString(), newNickname);
for (const auto& channelName : channelsAffected) {
auto channel = getChannel(channelName);
auto memberOpt = channel->getMember(newNickname);
if (!memberOpt)
continue;
for (const auto& channelName : channelsAffected) {
auto channel = getChannel(channelName);
auto memberOpt = channel->getMember(newNickname);
if (!memberOpt)
continue;
if (!ignoreVerbosity(Command::IRC::NICK))
m_status.printTo(channelName.c_str(), PrintType::Nick, msg.c_str());
if (!ignoreVerbosity(Command::IRC::NICK))
m_status.printTo(channelName.c_str(), PrintType::Nick, msg.c_str());
const auto& member = memberOpt->get();
emit memberChanged(sender.toString().c_str(), member);
}
const auto& member = memberOpt->get();
emit memberChanged(sender.toString().c_str(), member);
}
auto pm = MdiManager::instance().findWindow(&m_status, QString::fromStdString(sender.toString()));
auto pmNewExisting = MdiManager::instance().findWindow(&m_status, QString::fromStdString(newNickname));
@ -218,16 +218,16 @@ void IRC::onMsgNick(const IRCPrefix& sender, const std::string& newNickname, con
pm->refreshWindowTitle();
}
contextualScriptEvent(&m_status, ScriptEvent::Nick, sender.toString(), newNickname);
contextualScriptEvent(&m_status, ScriptEvent::Nick, sender.toString(), newNickname);
}
void IRC::onMsgMode(const IRCPrefix& sender, const std::string& target, const std::string& modes, const std::vector<std::string>& args)
{
auto modemsg = modes;
std::for_each(args.begin(), args.end(),
[&modemsg](const std::string& arg){
modemsg += " " + arg;
});
auto modemsg = modes;
std::for_each(args.begin(), args.end(),
[&modemsg](const std::string& arg){
modemsg += " " + arg;
});
const auto& is = isupport();
const auto& chantypes = is.at("CHANTYPES"); // Chantypes is always present.
@ -266,20 +266,20 @@ void IRC::onMsgMode(const IRCPrefix& sender, const std::string& target, const st
/* User-mode changed */
else if (!ignoreVerbosity(Command::IRC::MODE)) {
/* We changed our own usermode */
if (target == getNickname()) {
const std::string msg = fmt::format("Your usermode changed: {}", modemsg);
getStatus().print(PrintType::Mode, msg.c_str());
}
/* We changed our own usermode */
if (target == getNickname()) {
const std::string msg = fmt::format("Your usermode changed: {}", modemsg);
getStatus().print(PrintType::Mode, msg.c_str());
}
/* Probably not possible, but someone set someone elses usermode and we got the message about it */
else {
const std::string msg = fmt::format("{} changed usermode for {}: {}", sender.toString(), target, modemsg);
getStatus().print(PrintType::Mode, msg.c_str());
}
}
/* Probably not possible, but someone set someone elses usermode and we got the message about it */
else {
const std::string msg = fmt::format("{} changed usermode for {}: {}", sender.toString(), target, modemsg);
getStatus().print(PrintType::Mode, msg.c_str());
}
}
contextualScriptEvent(&m_status, ScriptEvent::Mode, sender.toString(), target, modemsg);
contextualScriptEvent(&m_status, ScriptEvent::Mode, sender.toString(), target, modemsg);
}
void IRC::onMsgQuit(const IRCPrefix& sender, const std::string& message, const std::vector<std::string>& channelsAffected)
@ -293,157 +293,157 @@ void IRC::onMsgQuit(const IRCPrefix& sender, const std::string& message, const s
if (channelsAffected.empty() && !ignoreVerbosity(Command::IRC::QUIT))
m_status.print(PrintType::Quit, msg.c_str());
for (const auto& channelName : channelsAffected) {
auto channel = getChannel(channelName);
auto memberOpt = channel->getMember(sender.nickname());
if (!memberOpt)
continue;
for (const auto& channelName : channelsAffected) {
auto channel = getChannel(channelName);
auto memberOpt = channel->getMember(sender.nickname());
if (!memberOpt)
continue;
if (!ignoreVerbosity(Command::IRC::QUIT)) {
m_status.printTo(channelName.c_str(), PrintType::Quit, msg.c_str());
}
if (!ignoreVerbosity(Command::IRC::QUIT)) {
m_status.printTo(channelName.c_str(), PrintType::Quit, msg.c_str());
}
const auto& member = memberOpt->get();
emit memberRemoved(channelName.c_str(), member);
}
const auto& member = memberOpt->get();
emit memberRemoved(channelName.c_str(), member);
}
contextualScriptEvent(&m_status, ScriptEvent::Quit, sender.nickname(), message);
contextualScriptEvent(&m_status, ScriptEvent::Quit, sender.nickname(), message);
}
void IRC::onMsgJoin(const IRCPrefix& sender, const std::string& target)
{
/* We joined a channel */
if (sender.nickname() == getNickname()) {
auto* subwin = getStatus().createChannelWindow(target.c_str());
if (!ignoreVerbosity(Command::IRC::JOIN)) {
const std::string msg = fmt::format("Now talking in {}", target);
subwin->print(PrintType::ServerInfo, msg.c_str());
}
subwin->refreshWindowTitle();
command(Command::IRC::MODE, { target });
}
/* Someone else joined a channel */
else {
if (!ignoreVerbosity(Command::IRC::JOIN)) {
const std::string msg = fmt::format("Join: {} ({}@{})", sender.nickname(), sender.user(), sender.host());
getStatus().printTo(target.c_str(), PrintType::Join, msg.c_str());
}
auto channel = getChannel(target);
auto memberOpt = channel->getMember(sender.nickname());
if (memberOpt)
emit memberAdded(target.c_str(), memberOpt->get());
}
contextualScriptEvent(&m_status, ScriptEvent::Join, sender.nickname(), target);
/* We joined a channel */
if (sender.nickname() == getNickname()) {
auto* subwin = getStatus().createChannelWindow(target.c_str());
if (!ignoreVerbosity(Command::IRC::JOIN)) {
const std::string msg = fmt::format("Now talking in {}", target);
subwin->print(PrintType::ServerInfo, msg.c_str());
}
subwin->refreshWindowTitle();
command(Command::IRC::MODE, { target });
}
/* Someone else joined a channel */
else {
if (!ignoreVerbosity(Command::IRC::JOIN)) {
const std::string msg = fmt::format("Join: {} ({}@{})", sender.nickname(), sender.user(), sender.host());
getStatus().printTo(target.c_str(), PrintType::Join, msg.c_str());
}
auto channel = getChannel(target);
auto memberOpt = channel->getMember(sender.nickname());
if (memberOpt)
emit memberAdded(target.c_str(), memberOpt->get());
}
contextualScriptEvent(&m_status, ScriptEvent::Join, sender.nickname(), target);
}
void IRC::onMsgPart(const IRCPrefix& sender, const std::string& target, const std::string& message)
{
if (!ignoreVerbosity(Command::IRC::PART)) {
std::string msg;
if (msg.empty())
msg = fmt::format("Part: {} ({}@{})", sender.nickname(), sender.user(), sender.host());
else
msg = fmt::format("Part: {} ({}@{}) ({})", sender.nickname(), sender.user(), sender.host(), message);
getStatus().printTo(target.c_str(), PrintType::Part, msg.c_str());
}
if (!ignoreVerbosity(Command::IRC::PART)) {
std::string msg;
if (msg.empty())
msg = fmt::format("Part: {} ({}@{})", sender.nickname(), sender.user(), sender.host());
else
msg = fmt::format("Part: {} ({}@{}) ({})", sender.nickname(), sender.user(), sender.host(), message);
getStatus().printTo(target.c_str(), PrintType::Part, msg.c_str());
}
/* We left a channel */
if (sender.nickname() == getNickname())
emit memberListReloaded(target.c_str());
/* We left a channel */
if (sender.nickname() == getNickname())
emit memberListReloaded(target.c_str());
/* Someone else left a channel */
else {
auto channel = getChannel(target);
auto memberOpt = channel->getMember(sender.nickname());
if (memberOpt)
emit memberRemoved(target.c_str(), memberOpt->get());
}
/* Someone else left a channel */
else {
auto channel = getChannel(target);
auto memberOpt = channel->getMember(sender.nickname());
if (memberOpt)
emit memberRemoved(target.c_str(), memberOpt->get());
}
contextualScriptEvent(&m_status, ScriptEvent::Part, sender.nickname(), target, message);
contextualScriptEvent(&m_status, ScriptEvent::Part, sender.nickname(), target, message);
}
void IRC::onMsgTopic(const IRCPrefix& sender, const std::string& target, const std::string& topic)
{
if (!ignoreVerbosity(Command::IRC::TOPIC)) {
std::string msg;
if (topic.empty())
if (!ignoreVerbosity(Command::IRC::TOPIC)) {
std::string msg;
if (topic.empty())
msg = fmt::format("{} removed the topic", sender.toString());
else
else
msg = fmt::format("{} set the topic to: {}", sender.toString(), topic);
getStatus().printTo(target.c_str(), PrintType::Topic, msg.c_str());
}
getStatus().printTo(target.c_str(), PrintType::Topic, msg.c_str());
}
auto subwin = MdiManager::instance().findWindow(&getStatus(), QString::fromStdString(target));
if (subwin)
subwin->refreshWindowTitle();
auto subwin = MdiManager::instance().findWindow(&getStatus(), QString::fromStdString(target));
if (subwin)
subwin->refreshWindowTitle();
contextualScriptEvent(&m_status, ScriptEvent::Topic, sender.toString(), target, topic);
contextualScriptEvent(&m_status, ScriptEvent::Topic, sender.toString(), target, topic);
}
void IRC::onMsgInvite(const IRCPrefix& sender, const std::string& target)
{
if (!ignoreVerbosity(Command::IRC::INVITE)) {
const std::string msg = fmt::format("{} invited you to join {}", sender.toString(), target);
if (!ignoreVerbosity(Command::IRC::INVITE)) {
const std::string msg = fmt::format("{} invited you to join {}", sender.toString(), target);
auto* win = getStatus().getActiveWindow();
win->print(PrintType::Invite, msg.c_str());
MdiManager::instance().highlight(win, HL_Attention);
emit showTrayMessage(tr("Invited to channel"), QString::fromStdString(msg));
}
}
contextualScriptEvent(&m_status, ScriptEvent::Invite, sender.toString(), target);
contextualScriptEvent(&m_status, ScriptEvent::Invite, sender.toString(), target);
}
void IRC::onMsgKick(const IRCPrefix& sender, const std::string& target, const std::string& who, const std::string& reason)
{
std::string msg;
std::string msg;
/* We were kicked */
if (who == getNickname()) {
msg = fmt::format("You were kicked out by {} ({})", sender.toString(), reason);
emit showTrayMessage(tr("Kicked from channel"), QString::fromStdString(msg));
emit memberListReset(target.c_str());
}
/* We were kicked */
if (who == getNickname()) {
msg = fmt::format("You were kicked out by {} ({})", sender.toString(), reason);
emit showTrayMessage(tr("Kicked from channel"), QString::fromStdString(msg));
emit memberListReset(target.c_str());
}
/* Someone else were kicked */
else {
msg = fmt::format("{} kicked out {} ({})", sender.toString(), who, reason);
/* Someone else were kicked */
else {
msg = fmt::format("{} kicked out {} ({})", sender.toString(), who, reason);
auto channel = getChannel(target);
auto memberOpt = channel->getMember(who);
if (memberOpt)
emit memberRemoved(target.c_str(), memberOpt->get());
}
auto channel = getChannel(target);
auto memberOpt = channel->getMember(who);
if (memberOpt)
emit memberRemoved(target.c_str(), memberOpt->get());
}
if (!ignoreVerbosity(Command::IRC::KICK)) {
if (!ignoreVerbosity(Command::IRC::KICK)) {
auto* win = MdiManager::instance().findWindow(&getStatus(), target.c_str());
win->print(PrintType::Kick, msg.c_str());
if (who == getNickname())
MdiManager::instance().highlight(win, HL_Attention);
}
contextualScriptEvent(&m_status, ScriptEvent::Kick, sender.toString(), who, target, reason);
contextualScriptEvent(&m_status, ScriptEvent::Kick, sender.toString(), who, target, reason);
}
void IRC::onMsgPrivmsg(const IRCPrefix& sender, const std::string& target, const std::string& message)
{
if (!ignoreVerbosity(Command::IRC::PRIVMSG)) {
const auto& is = isupport();
const auto& chantypes = is.at("CHANTYPES"); // Chantypes is always present.
const auto messageHasMyNick = QString::fromStdString(message).contains( QString::fromStdString(getNickname()), Qt::CaseInsensitive );
if (!ignoreVerbosity(Command::IRC::PRIVMSG)) {
const auto& is = isupport();
const auto& chantypes = is.at("CHANTYPES"); // Chantypes is always present.
const auto messageHasMyNick = QString::fromStdString(message).contains( QString::fromStdString(getNickname()), Qt::CaseInsensitive );
PrintType ptype = PrintType::Normal;
if (sender.nickname() == getNickname())
ptype = PrintType::OwnText;
else if (messageHasMyNick)
ptype = PrintType::Highlight;
PrintType ptype = PrintType::Normal;
if (sender.nickname() == getNickname())
ptype = PrintType::OwnText;
else if (messageHasMyNick)
ptype = PrintType::Highlight;
Highlight hl = messageHasMyNick ? HL_Attention : HL_Message;
Highlight hl = messageHasMyNick ? HL_Attention : HL_Message;
/* Message to a channel */
if (chantypes.find_first_of(target[0]) != std::string::npos) {
/* Message to a channel */
if (chantypes.find_first_of(target[0]) != std::string::npos) {
auto* window = MdiManager::instance().findWindow(&getStatus(), target.c_str());
auto& conf = ConfigMgr::instance();
std::string msg;
@ -470,58 +470,58 @@ void IRC::onMsgPrivmsg(const IRCPrefix& sender, const std::string& target, const
emit showTrayMessage(QString::fromStdString(target), QString::fromStdString(msg));
}
/* Private message */
else {
auto* window = getStatus().createPrivateWindow(sender.toString().c_str(), false, hl);
/* Private message */
else {
auto* window = getStatus().createPrivateWindow(sender.toString().c_str(), false, hl);
const std::string msg = fmt::format("<{}> {}", sender.toString(), message);
window->print(ptype, msg.c_str());
MdiManager::instance().highlight(window, hl);
emit showTrayMessage(tr("Private message"), QString::fromStdString(msg));
}
}
}
}
contextualScriptEvent(&m_status, ScriptEvent::Privmsg, sender.toString(), target, message);
contextualScriptEvent(&m_status, ScriptEvent::Privmsg, sender.toString(), target, message);
}
void IRC::onMsgNotice(const IRCPrefix& sender, const std::string& target, const std::string& message)
{
if (!ignoreVerbosity(Command::IRC::NOTICE)) {
IWin* window;
std::string msg;
if (isChannelSymbol(target[0])) {
if (!ignoreVerbosity(Command::IRC::NOTICE)) {
IWin* window;
std::string msg;
if (isChannelSymbol(target[0])) {
msg = fmt::format("-{}({})- {}", sender.toString(), target, message);
window = MdiManager::instance().findWindow(&getStatus(), QString::fromStdString(target));
}
else {
else {
msg = fmt::format("-{}- {}", sender.toString(), message);
window = getStatus().getActiveWindow();
}
window->print(PrintType::Notice, msg.c_str());
MdiManager::instance().highlight(window, HL_Attention);
emit showTrayMessage(tr("Notice"), QString::fromStdString(msg));
}
}
contextualScriptEvent(&m_status, ScriptEvent::Notice, sender.toString(), target, message);
contextualScriptEvent(&m_status, ScriptEvent::Notice, sender.toString(), target, message);
}
void IRC::onMsgKill(const IRCPrefix& sender, const std::string& reason)
{
if (!ignoreVerbosity(Command::IRC::KILL)) {
std::string msg;
if (!ignoreVerbosity(Command::IRC::KILL)) {
std::string msg;
if (sender.type() == IRCPrefix::Type::user)
msg = fmt::format("You were forcibly disconnected by {} ({}@{}) ({})",
sender.nickname(), sender.user(), sender.host(), reason);
else
msg = fmt::format("You were forcibly disconnected by {} ({})",
sender.servername(), reason);
if (sender.type() == IRCPrefix::Type::user)
msg = fmt::format("You were forcibly disconnected by {} ({}@{}) ({})",
sender.nickname(), sender.user(), sender.host(), reason);
else
msg = fmt::format("You were forcibly disconnected by {} ({})",
sender.servername(), reason);
getStatus().print(PrintType::ServerInfo, msg.c_str());
getStatus().print(PrintType::ServerInfo, msg.c_str());
MdiManager::instance().highlight(&getStatus(), HL_Attention);
emit showTrayMessage(tr("Disconnected"), QString::fromStdString(msg));
}
}
contextualScriptEvent(&m_status, ScriptEvent::Kill, sender.toString(), reason);
contextualScriptEvent(&m_status, ScriptEvent::Kill, sender.toString(), reason);
}
void IRC::onMsgPing(const std::string& message)
@ -536,25 +536,25 @@ void IRC::onMsgPong(const std::string& message)
void IRC::onMsgError(const std::string& message)
{
if (!ignoreVerbosity(Command::IRC::ERROR_)) {
if (!ignoreVerbosity(Command::IRC::ERROR_)) {
getStatus().print(PrintType::ServerInfo, message.c_str());
MdiManager::instance().highlight(&getStatus(), HL_Attention);
emit showTrayMessage(tr("Disconnected"), QString::fromStdString(message));
}
contextualScriptEvent(&m_status, ScriptEvent::Error, message);
contextualScriptEvent(&m_status, ScriptEvent::Error, message);
}
void IRC::onMsgWallops(const IRCPrefix& sender, const std::string& message)
{
if (!ignoreVerbosity(Command::IRC::WALLOPS)) {
const std::string msg = fmt::format("!{}! {}", sender.toString(), message);
getStatus().print(PrintType::Wallops, msg.c_str());
if (!ignoreVerbosity(Command::IRC::WALLOPS)) {
const std::string msg = fmt::format("!{}! {}", sender.toString(), message);
getStatus().print(PrintType::Wallops, msg.c_str());
MdiManager::instance().highlight(&getStatus(), HL_Attention);
emit showTrayMessage(tr("Wallops"), QString::fromStdString(msg));
}
}
contextualScriptEvent(&m_status, ScriptEvent::Wallops, sender.toString(), message);
contextualScriptEvent(&m_status, ScriptEvent::Wallops, sender.toString(), message);
}
void IRC::onMsgNumeric(const IRCPrefix& /*sender*/, const std::string& num, const std::vector<std::string>& args, const std::string& message)
@ -827,7 +827,7 @@ void IRC::onMsgCTCPRequest(const IRCPrefix& sender, const std::string& target, c
ctcpResponse(sender.toString(), command, message);
}
contextualScriptEvent(&m_status, ScriptEvent::CtcpRequest, sender.toString(), target, command, message);
contextualScriptEvent(&m_status, ScriptEvent::CtcpRequest, sender.toString(), target, command, message);
}
void IRC::onMsgCTCPResponse(const IRCPrefix& sender, const std::string& target, const std::string& command, const std::string& message)
@ -879,5 +879,5 @@ void IRC::v3onMsgAway(const IRCPrefix& sender, const std::string& message, const
void IRC::v3onMsgJoin(const IRCPrefix& sender, const std::string& channel, const std::string& useraccount, const std::string& realname)
{
onMsgJoin(sender, channel);
onMsgJoin(sender, channel);
}

@ -18,40 +18,40 @@ class MdiManager;
class IWinStatus;
class IRC : public QObject,
public IRCBase
public IRCBase
{
Q_OBJECT
Q_OBJECT
public:
explicit IRC(IWinStatus& status);
explicit IRC(IWinStatus& status);
void disconnectForExit(const QString& quitMessage);
IWinStatus& getStatus() { return m_status; }
void disconnectForExit(const QString& quitMessage);
IWinStatus& getStatus() { return m_status; }
int channelAutoComplete(int index, QString& pattern);
void setIgnoreVerbosity(const QStringList& commandsAndNumerics);
void unsetIgnoreVerbosity(const QStringList& commandsAndNumerics);
void setIgnoreVerbosity(const QStringList& commandsAndNumerics);
void unsetIgnoreVerbosity(const QStringList& commandsAndNumerics);
void expectDisconnect() { m_expectDisconnect = true; }
void dontExpectDisconnect() { m_expectDisconnect = false; }
void expectDisconnect() { m_expectDisconnect = true; }
void dontExpectDisconnect() { m_expectDisconnect = false; }
void setReconnectDetails(bool ssl, const std::string& host, const std::string& port);
void setReconnectDetails(bool ssl, const std::string& host, const std::string& port);
signals:
void readyForExit();
void disconnected();
void connected();
void registered();
void readyForExit();
void disconnected();
void connected();
void registered();
void memberListReloaded(const QString& channel);
void memberListReloaded(const QString& channel);
void memberListReset(const QString& channel);
void memberListClearedForAll();
void memberAdded(const QString& channel, const IRCMemberEntry& entry);
void memberChanged(const QString& nickname, const IRCMemberEntry& entry);
void memberRemoved(const QString& channel, const IRCMemberEntry& entry);
void memberListClearedForAll();
void memberAdded(const QString& channel, const IRCMemberEntry& entry);
void memberChanged(const QString& nickname, const IRCMemberEntry& entry);
void memberRemoved(const QString& channel, const IRCMemberEntry& entry);
void showTrayMessage(const QString& title, const QString& message);
void showTrayMessage(const QString& title, const QString& message);
private:
struct ReconnectDetails {
@ -61,43 +61,43 @@ private:
};
std::optional<ReconnectDetails> m_reconnectDetails;
bool m_disconnectForExit{ false };
bool m_expectDisconnect{ false };
IWinStatus& m_status;
int m_id; //! Used mainly for scripts
bool m_receiveWhoisToCurrentWindow{ false };
bool m_disconnectForExit{ false };
bool m_expectDisconnect{ false };
IWinStatus& m_status;
int m_id; //! Used mainly for scripts
bool m_receiveWhoisToCurrentWindow{ false };
QStringList m_ignoreVerbosity; //! Used to mute certain message types from the server
bool ignoreVerbosity(const std::string& cmd);
QStringList m_ignoreVerbosity; //! Used to mute certain message types from the server
bool ignoreVerbosity(const std::string& cmd);
void rejoinChannels();
void rejoinChannels();
void onRegistered() override;
void onConnected() override;
void onRegistered() override;
void onConnected() override;
void onConnectedWithSSLExceptions(const std::vector<IRCError>& codes) override;
void onDisconnected() override;
void onConnectionError(IRCError e) override;
void onMsgNick(const IRCPrefix& sender, const std::string& newNickname, const std::vector<std::string>& channelsAffected) override;
void onMsgMode(const IRCPrefix& sender, const std::string& target, const std::string& modes, const std::vector<std::string>& args) override;
void onMsgQuit(const IRCPrefix& sender, const std::string& message, const std::vector<std::string>& channelsAffected) override;
void onMsgJoin(const IRCPrefix& sender, const std::string& target) override;
void onMsgPart(const IRCPrefix& sender, const std::string& target, const std::string& message) override;
void onMsgTopic(const IRCPrefix& sender, const std::string& target, const std::string& topic) override;
void onMsgInvite(const IRCPrefix& sender, const std::string& target) override;
void onMsgKick(const IRCPrefix& sender, const std::string& target, const std::string& who, const std::string& reason) override;
void onMsgPrivmsg(const IRCPrefix& sender, const std::string& target, const std::string& message) override;
void onMsgNotice(const IRCPrefix& sender, const std::string& target, const std::string& message) override;
void onMsgKill(const IRCPrefix& sender, const std::string& reason) override;
void onMsgPing(const std::string& message) override;
void onMsgPong(const std::string& message) override;
void onMsgError(const std::string& message) override;
void onMsgWallops(const IRCPrefix& sender, const std::string& message) override;
void onMsgNumeric(const IRCPrefix& sender, const std::string& num, const std::vector<std::string>& args, const std::string& message) override;
void onMsgCTCPRequest(const IRCPrefix& sender, const std::string& target, const std::string& command, const std::string& message) override;
void onMsgCTCPResponse(const IRCPrefix& sender, const std::string& target, const std::string& command, const std::string& message) override;
void onMsgDCCRequest(std::shared_ptr<DCC> dcc, const IRCPrefix& sender, const std::string& target, const std::string& type, const std::string& message) override;
void v3onMsgAway(const IRCPrefix& sender, const std::string& message, const std::vector<std::string>& channelsAffected) override;
void v3onMsgJoin(const IRCPrefix& sender, const std::string& channel, const std::string& useraccount, const std::string& realname) override;
void onDisconnected() override;
void onConnectionError(IRCError e) override;
void onMsgNick(const IRCPrefix& sender, const std::string& newNickname, const std::vector<std::string>& channelsAffected) override;
void onMsgMode(const IRCPrefix& sender, const std::string& target, const std::string& modes, const std::vector<std::string>& args) override;
void onMsgQuit(const IRCPrefix& sender, const std::string& message, const std::vector<std::string>& channelsAffected) override;
void onMsgJoin(const IRCPrefix& sender, const std::string& target) override;
void onMsgPart(const IRCPrefix& sender, const std::string& target, const std::string& message) override;
void onMsgTopic(const IRCPrefix& sender, const std::string& target, const std::string& topic) override;
void onMsgInvite(const IRCPrefix& sender, const std::string& target) override;
void onMsgKick(const IRCPrefix& sender, const std::string& target, const std::string& who, const std::string& reason) override;
void onMsgPrivmsg(const IRCPrefix& sender, const std::string& target, const std::string& message) override;
void onMsgNotice(const IRCPrefix& sender, const std::string& target, const std::string& message) override;
void onMsgKill(const IRCPrefix& sender, const std::string& reason) override;
void onMsgPing(const std::string& message) override;
void onMsgPong(const std::string& message) override;
void onMsgError(const std::string& message) override;
void onMsgWallops(const IRCPrefix& sender, const std::string& message) override;
void onMsgNumeric(const IRCPrefix& sender, const std::string& num, const std::vector<std::string>& args, const std::string& message) override;
void onMsgCTCPRequest(const IRCPrefix& sender, const std::string& target, const std::string& command, const std::string& message) override;
void onMsgCTCPResponse(const IRCPrefix& sender, const std::string& target, const std::string& command, const std::string& message) override;
void onMsgDCCRequest(std::shared_ptr<DCC> dcc, const IRCPrefix& sender, const std::string& target, const std::string& type, const std::string& message) override;
void v3onMsgAway(const IRCPrefix& sender, const std::string& message, const std::vector<std::string>& channelsAffected) override;
void v3onMsgJoin(const IRCPrefix& sender, const std::string& channel, const std::string& useraccount, const std::string& realname) override;
};
#endif // IRC_H

@ -20,13 +20,13 @@
IdealIRC* IIRC_MainWindow_Ptr{ nullptr };
IdealIRC::IdealIRC(QWidget* parent)
: QMainWindow(parent)
, ui(new Ui::IdealIRC)
, confDlg(this)
, aboutDlg(this)
: QMainWindow(parent)
, ui(new Ui::IdealIRC)
, confDlg(this)
, aboutDlg(this)
{
ui->setupUi(this);
IIRC_MainWindow_Ptr = this;
ui->setupUi(this);
IIRC_MainWindow_Ptr = this;
auto& conf = ConfigMgr::instance();
const auto bbposcfg = conf.common("ButtonBarPosition");
@ -40,25 +40,25 @@ IdealIRC::IdealIRC(QWidget* parent)
addToolBar(Qt::RightToolBarArea, ui->windowButtons);
}
const QString title = QString("%1 %2")
.arg(windowTitle())
.arg(VERSION_STRING_NO_APPEND);
setWindowTitle(title);
trayIcon = new QSystemTrayIcon(QIcon(QStringLiteral(":/Icons/icon.png")), this);
const QString title = QString("%1 %2")
.arg(windowTitle())
.arg(VERSION_STRING_NO_APPEND);
setWindowTitle(title);
trayIcon = new QSystemTrayIcon(QIcon(QStringLiteral(":/Icons/icon.png")), this);
mdiManager = new MdiManager(*ui->mdiArea, *ui->windowButtons, *trayIcon);
connect(mdiManager, &MdiManager::statusWindowCreated, this, &IdealIRC::onStatusWindowCreated);
mdiManager = new MdiManager(*ui->mdiArea, *ui->windowButtons, *trayIcon);
connect(mdiManager, &MdiManager::statusWindowCreated, this, &IdealIRC::onStatusWindowCreated);
trayIcon->show();
trayIcon->setToolTip(title);
trayIcon->show();
trayIcon->setToolTip(title);
connect(trayIcon, &QSystemTrayIcon::activated, [this](QSystemTrayIcon::ActivationReason reason) {
connect(trayIcon, &QSystemTrayIcon::activated, [this](QSystemTrayIcon::ActivationReason reason) {
if (reason == QSystemTrayIcon::DoubleClick) {
setVisible(!isVisible());
}
});
});
trayIconMenu = new QMenu("IdealIRC", this);
trayIconMenu = new QMenu("IdealIRC", this);
trayIconMenuShow = trayIconMenu->addAction("Show/Hide");
trayIconMenu->addSeparator();
trayIconMenuExit = trayIconMenu->addAction("Exit");
@ -73,24 +73,24 @@ IdealIRC::IdealIRC(QWidget* parent)
trayIcon->setContextMenu(trayIconMenu);
connect(mdiManager, &MdiManager::readyForExit, this, &QMainWindow::close);
connect(mdiManager, &MdiManager::subwindowSwitched, this, &IdealIRC::subwindowSwitched);
connect(mdiManager, &MdiManager::connectionStateChange, this, &IdealIRC::updateConnectButtonState);
connect(&confDlg, &IConfig::connectToServer, this, &IdealIRC::connectToServer);
connect(&confDlg, &IConfig::disconnectFromServer, this, &IdealIRC::disconnectFromServer);
setConnectButtonState(ConnectButtonState::Connect);
connect(mdiManager, &MdiManager::readyForExit, this, &QMainWindow::close);
connect(mdiManager, &MdiManager::subwindowSwitched, this, &IdealIRC::subwindowSwitched);
connect(mdiManager, &MdiManager::connectionStateChange, this, &IdealIRC::updateConnectButtonState);
connect(&confDlg, &IConfig::connectToServer, this, &IdealIRC::connectToServer);
connect(&confDlg, &IConfig::disconnectFromServer, this, &IdealIRC::disconnectFromServer);
setConnectButtonState(ConnectButtonState::Connect);
IWin* statusw = mdiManager->createSubwindow(nullptr, "Status", IWin::Type::Status);
statusw->refreshWindowTitle();
IWin* statusw = mdiManager->createSubwindow(nullptr, "Status", IWin::Type::Status);
statusw->refreshWindowTitle();
QTimer::singleShot(10, [this]{ startup(); });
QTimer::singleShot(10, [this]{ startup(); });
}
IdealIRC::~IdealIRC()
{
delete mdiManager;
delete trayIcon;
delete ui;
delete mdiManager;
delete trayIcon;
delete ui;
}
void IdealIRC::saveSubwindowGeometries()
@ -247,150 +247,150 @@ void IdealIRC::startup()
void IdealIRC::subwindowSwitched()
{
updateConnectButtonState();
ScriptManager::setContext(mdiManager->currentStatus());
updateConnectButtonState();
ScriptManager::setContext(mdiManager->currentStatus());
}
void IdealIRC::updateConnectButtonState()
{
IWinStatus* status = mdiManager->currentStatus();
bool isConnected = status->getConnection().isConnected();
setConnectButtonState(isConnected ? ConnectButtonState::Disconnect : ConnectButtonState::Connect);
IWinStatus* status = mdiManager->currentStatus();
bool isConnected = status->getConnection().isConnected();
setConnectButtonState(isConnected ? ConnectButtonState::Disconnect : ConnectButtonState::Connect);
}
void IdealIRC::setConnectButtonState(IdealIRC::ConnectButtonState state)
{
switch (state) {
case ConnectButtonState::Connect:
ui->actionConnect->setText(tr("Connect"));
ui->actionConnect->setIcon( QIcon(":/Icons/connect.png") );
break;
case ConnectButtonState::Disconnect:
ui->actionConnect->setText(tr("Disconnect"));
ui->actionConnect->setIcon( QIcon(":/Icons/disconnect.png") );
break;
}
ui->actionConnect->setData(static_cast<int>(state));
switch (state) {
case ConnectButtonState::Connect:
ui->actionConnect->setText(tr("Connect"));
ui->actionConnect->setIcon( QIcon(":/Icons/connect.png") );
break;
case ConnectButtonState::Disconnect:
ui->actionConnect->setText(tr("Disconnect"));
ui->actionConnect->setIcon( QIcon(":/Icons/disconnect.png") );
break;
}
ui->actionConnect->setData(static_cast<int>(state));
}
void IdealIRC::connectToServer(bool newStatus)
{
IWinStatus* status{ nullptr };
if (newStatus)
status = static_cast<IWinStatus*>(mdiManager->createSubwindow(nullptr, "Status", IWin::Type::Status));
else
status = mdiManager->currentStatus();
const ConfigMgr& conf = ConfigMgr::instance();
QString server = conf.connection("Server");
QString nickname = conf.connection("Nickname");
QString realname = conf.connection("Realname");
QString username = conf.connection("Username");
QString password = conf.connection("Password");
if (server.isEmpty() || nickname.isEmpty() || realname.isEmpty() || username.isEmpty()) {
confDlg.show();
return;
}
bool ssl = conf.connection("SSL").toInt();
QString host;
quint16 port;
if (server.contains(':')) {
QString strport = server.split(':')[1];
port = strport.toUShort();
server = server.remove(":"+strport);
}
else
port = 6667;
host = server;
IRC& con = status->getConnection();
con.setHostname(host.toStdString(), ssl);
con.setPort(std::to_string(port));
con.setRealname(realname.toStdString());
con.setIdent(username.toStdString());
con.setNickname(nickname.toStdString());
con.setPassword(password.toStdString());
bool manualKeepaliveEnabled = conf.common("ManualKeepaliveEnabled").toInt();
IWinStatus* status{ nullptr };
if (newStatus)
status = static_cast<IWinStatus*>(mdiManager->createSubwindow(nullptr, "Status", IWin::Type::Status));
else
status = mdiManager->currentStatus();
const ConfigMgr& conf = ConfigMgr::instance();
QString server = conf.connection("Server");
QString nickname = conf.connection("Nickname");
QString realname = conf.connection("Realname");
QString username = conf.connection("Username");
QString password = conf.connection("Password");
if (server.isEmpty() || nickname.isEmpty() || realname.isEmpty() || username.isEmpty()) {
confDlg.show();
return;
}
bool ssl = conf.connection("SSL").toInt();
QString host;
quint16 port;
if (server.contains(':')) {
QString strport = server.split(':')[1];
port = strport.toUShort();
server = server.remove(":"+strport);
}
else
port = 6667;
host = server;
IRC& con = status->getConnection();
con.setHostname(host.toStdString(), ssl);
con.setPort(std::to_string(port));
con.setRealname(realname.toStdString());
con.setIdent(username.toStdString());
con.setNickname(nickname.toStdString());
con.setPassword(password.toStdString());
bool manualKeepaliveEnabled = conf.common("ManualKeepaliveEnabled").toInt();
int manualKeepalive = manualKeepaliveEnabled ? conf.common("ManualKeepalive").toInt() : 0;
con.setManualKeepalive(std::chrono::seconds(manualKeepalive));
const auto cfgSSLExcept_Expired = conf.common("SSLExpired");
const auto cfgSSLExcept_Selfsigned = conf.common("SSLSelfsigned");
const auto cfgSSLExcept_CNMismatch = conf.common("SSLCNMismatch");
con.exceptSSL_Expired(cfgSSLExcept_Expired == "1");
con.exceptSSL_SelfSigned(cfgSSLExcept_Selfsigned == "1");
con.exceptSSL_CNMismatch(cfgSSLExcept_CNMismatch == "1");
const auto cfgSSLExcept_Selfsigned = conf.common("SSLSelfsigned");
const auto cfgSSLExcept_CNMismatch = conf.common("SSLCNMismatch");
con.exceptSSL_Expired(cfgSSLExcept_Expired == "1");
con.exceptSSL_SelfSigned(cfgSSLExcept_Selfsigned == "1");
con.exceptSSL_CNMismatch(cfgSSLExcept_CNMismatch == "1");
if (con.isConnected()) {
if (con.isConnected()) {
auto quitMsg = conf.common("QuitMessage").toStdString();
if (con.disconnectFromServer(quitMsg) == IRCError::NotConnected)
qWarning() << "Trying to disconnect from an already closed connection.";
auto e = con.tryConnect();
if (e != IRCError::NoError)
status->print(PrintType::ProgramInfo, IRCErrorToString(e).c_str());
}
else {
setConnectButtonState(ConnectButtonState::Disconnect);
auto e = con.tryConnect();
if (e != IRCError::NoError)
status->print(PrintType::ProgramInfo, IRCErrorToString(e).c_str());
}
if (con.disconnectFromServer(quitMsg) == IRCError::NotConnected)
qWarning() << "Trying to disconnect from an already closed connection.";
auto e = con.tryConnect();
if (e != IRCError::NoError)
status->print(PrintType::ProgramInfo, IRCErrorToString(e).c_str());
}
else {
setConnectButtonState(ConnectButtonState::Disconnect);
auto e = con.tryConnect();
if (e != IRCError::NoError)
status->print(PrintType::ProgramInfo, IRCErrorToString(e).c_str());
}
}
void IdealIRC::disconnectFromServer()
{
const ConfigMgr& conf = ConfigMgr::instance();
IWinStatus* status = mdiManager->currentStatus();
auto& connection = status->getConnection();
IWinStatus* status = mdiManager->currentStatus();
auto& connection = status->getConnection();
connection.expectDisconnect();
auto quitMsg = conf.common("QuitMessage").toStdString();
if (connection.disconnectFromServer(quitMsg) == IRCError::NotConnected)
qWarning() << "Trying to disconnect from an already closed connection.";
qWarning() << "Trying to disconnect from an already closed connection.";
}
void IdealIRC::on_actionConnect_triggered()
{
ConnectButtonState state = static_cast<ConnectButtonState>(ui->actionConnect->data().toInt());
if (state == ConnectButtonState::Connect)
connectToServer(false);
ConnectButtonState state = static_cast<ConnectButtonState>(ui->actionConnect->data().toInt());
if (state == ConnectButtonState::Connect)
connectToServer(false);
else if (state == ConnectButtonState::Disconnect) {
IRC& connection = mdiManager->currentStatus()->getConnection();
const ConfigMgr& conf = ConfigMgr::instance();
if (!connection.isOnline())
setConnectButtonState(ConnectButtonState::Connect);
else if (state == ConnectButtonState::Disconnect) {
IRC& connection = mdiManager->currentStatus()->getConnection();
const ConfigMgr& conf = ConfigMgr::instance();
if (!connection.isOnline())
setConnectButtonState(ConnectButtonState::Connect);
connection.expectDisconnect();
auto e = connection.disconnectFromServer(conf.common("QuitMessage").toStdString());
if (e == IRCError::NotConnected)
qWarning() << "Trying to disconnect from an already closed connection.";
}
auto e = connection.disconnectFromServer(conf.common("QuitMessage").toStdString());
if (e == IRCError::NotConnected)
qWarning() << "Trying to disconnect from an already closed connection.";
}
}
void IdealIRC::on_actionOptions_triggered()
{
confDlg.show();
if (mdiManager->currentStatus()->getConnection().isConnected())
confDlg.showDisconnectButton();
else
confDlg.hideDisconnectButton();
confDlg.show();
if (mdiManager->currentStatus()->getConnection().isConnected())
confDlg.showDisconnectButton();
else
confDlg.hideDisconnectButton();
}
void IdealIRC::on_actionAbout_IdealIRC_triggered()
{
aboutDlg.show();
aboutDlg.show();
}
void IdealIRC::on_actionScripts_triggered()
{
ScriptManager::instance()->show();
ScriptManager::instance()->show();
}
void IdealIRC::on_actionOnline_wiki_triggered()

@ -26,47 +26,47 @@ class IWinStatus;
class IdealIRC : public QMainWindow
{
Q_OBJECT
Q_OBJECT
public:
explicit IdealIRC(QWidget* parent = nullptr);
~IdealIRC();
explicit IdealIRC(QWidget* parent = nullptr);
~IdealIRC();
private slots:
void on_actionConnect_triggered();
void on_actionOptions_triggered();
void on_actionAbout_IdealIRC_triggered();
void on_actionScripts_triggered();
void on_actionConnect_triggered();
void on_actionOptions_triggered();
void on_actionAbout_IdealIRC_triggered();
void on_actionScripts_triggered();
void on_actionOnline_wiki_triggered();
void on_actionHide_toolbar_triggered();
void onStatusWindowCreated(IWinStatus* window);
private:
enum class ConnectButtonState {
Connect,
Disconnect
};
enum class ConnectButtonState {
Connect,
Disconnect
};
void saveSubwindowGeometries();
void closeEvent(QCloseEvent* evt) override;
void startup();
void subwindowSwitched();
void updateConnectButtonState();
void setConnectButtonState(ConnectButtonState state);
void connectToServer(bool newStatus);
void disconnectFromServer();
void saveSubwindowGeometries();
void closeEvent(QCloseEvent* evt) override;
void startup();
void subwindowSwitched();
void updateConnectButtonState();
void setConnectButtonState(ConnectButtonState state);
void connectToServer(bool newStatus);
void disconnectFromServer();
Ui::IdealIRC* ui;
MdiManager* mdiManager;
QSystemTrayIcon* trayIcon;
QMenu* trayIconMenu;
QAction* trayIconMenuShow;
QAction* trayIconMenuExit;
IConfig confDlg;
AboutIIRC aboutDlg;
Ui::IdealIRC* ui;
MdiManager* mdiManager;
QSystemTrayIcon* trayIcon;
QMenu* trayIconMenu;
QAction* trayIconMenuShow;
QAction* trayIconMenuExit;
IConfig confDlg;
AboutIIRC aboutDlg;
signals:
void aboutToClose();
void aboutToClose();
};
#endif // IDEALIRC_H

@ -15,10 +15,10 @@ struct IniFilePriv;
class IniFile
{
public:
explicit IniFile(const std::string& filename);
IniFile(IniFile&& other) noexcept;
IniFile(IniFile&) = delete;
~IniFile();
explicit IniFile(const std::string& filename);
IniFile(IniFile&& other) noexcept;
IniFile(IniFile&) = delete;
~IniFile();
IniFile& operator=(IniFile&) = delete;
IniFile& operator=(IniFile&& other) noexcept;

@ -17,55 +17,55 @@
#include <QDebug>
InputHandler::InputHandler(IRC& connection_)
: connection(connection_)
, cmdHndl(connection_)
: connection(connection_)
, cmdHndl(connection_)
{}
void InputHandler::parse(IWin& sender, const QString& text, bool handleCommand)
{
if (text.isEmpty())
return;
if (text.isEmpty())
return;
if (text[0] == '/' && handleCommand)
cmdHndl.parse(text);
else {
if (sender.getType() == IWin::Type::Status) {
sender.print(PrintType::ProgramInfo, "You're not in a chat window!");
return;
}
else if (!connection.isOnline())
sender.print(PrintType::ProgramInfo, "Not connected to a server");
else {
const std::string target = sender.getButtonText().toStdString();
std::string me = connection.getNickname();
connection.command(Command::IRC::PRIVMSG, { target }, text.toStdString());
if (text[0] == '/' && handleCommand)
cmdHndl.parse(text);
else {
if (sender.getType() == IWin::Type::Status) {
sender.print(PrintType::ProgramInfo, "You're not in a chat window!");
return;
}
else if (!connection.isOnline())
sender.print(PrintType::ProgramInfo, "Not connected to a server");
else {
const std::string target = sender.getButtonText().toStdString();
std::string me = connection.getNickname();
connection.command(Command::IRC::PRIVMSG, { target }, text.toStdString());
if (ConfigMgr::instance().common("ShowModeInMessage") == "1") do {
const auto& myChannels = connection.channels();
auto chanIt = std::find_if(myChannels.begin(), myChannels.end(),
[&target](std::shared_ptr<IRCChannel> ptr) {
return target == ptr->name();
});
if (ConfigMgr::instance().common("ShowModeInMessage") == "1") do {
const auto& myChannels = connection.channels();
auto chanIt = std::find_if(myChannels.begin(), myChannels.end(),
[&target](std::shared_ptr<IRCChannel> ptr) {
return target == ptr->name();
});
if (chanIt == myChannels.end()) {
qWarning() << "Could not find channel '" << target.c_str() << "' internally, although it seems we're on it anyway.";
break;
}
if (chanIt == myChannels.end()) {
qWarning() << "Could not find channel '" << target.c_str() << "' internally, although it seems we're on it anyway.";
break;
}
const auto& member = (*chanIt)->getMember(me)->get();
const auto prefixes = connection.toMemberPrefix(member.modes());
if (!prefixes.empty())
me.insert(me.begin(), prefixes[0]);
} while(0);
const auto& member = (*chanIt)->getMember(me)->get();
const auto prefixes = connection.toMemberPrefix(member.modes());
if (!prefixes.empty())
me.insert(me.begin(), prefixes[0]);
} while(0);
sender.print(PrintType::OwnText, QStringLiteral("<%1> %2")
.arg(me.c_str())
.arg(text));
}
}
sender.print(PrintType::OwnText, QStringLiteral("<%1> %2")
.arg(me.c_str())
.arg(text));
}
}
}
void InputHandler::parseCommand(const QString& text)
{
cmdHndl.parse(text);
cmdHndl.parse(text);
}

@ -18,15 +18,15 @@ class MdiManager;
class InputHandler
{
public:
explicit InputHandler(IRC& connection_);
explicit InputHandler(IRC& connection_);
void parse(IWin& sender, const QString& text, bool handleCommand = true);
void parseCommand(const QString& text);
ICommand* getHandler() { return &cmdHndl; }
void parse(IWin& sender, const QString& text, bool handleCommand = true);
void parseCommand(const QString& text);
ICommand* getHandler() { return &cmdHndl; }
private:
IRC& connection;
ICommand cmdHndl;
IRC& connection;
ICommand cmdHndl;
};
#endif // INPUTHANDLER_H

@ -24,91 +24,91 @@ constexpr int WINDOW_STEP_RESET_ADJUST = 60;
MdiManager* MdiManager::m_instance{ nullptr };
MdiManager::MdiManager(QMdiArea& mdiArea, QToolBar& buttonBar, QSystemTrayIcon& trayIcon)
: m_mdiArea(mdiArea)
, trayIcon(trayIcon)
, m_bbMgr(&buttonBar)
: m_mdiArea(mdiArea)
, trayIcon(trayIcon)
, m_bbMgr(&buttonBar)
{
m_instance = this;
connect(&m_mdiArea, &QMdiArea::subWindowActivated, this, &MdiManager::subwinActivated);
connect(&m_bbMgr, &ButtonbarMgr::changeWindow, &m_mdiArea, &QMdiArea::setActiveSubWindow);
m_instance = this;
connect(&m_mdiArea, &QMdiArea::subWindowActivated, this, &MdiManager::subwinActivated);
connect(&m_bbMgr, &ButtonbarMgr::changeWindow, &m_mdiArea, &QMdiArea::setActiveSubWindow);
}
MdiManager& MdiManager::instance()
{
return *m_instance;
return *m_instance;
}
MdiManager::ButtonBarEntry::ButtonBarEntry(QAction* button_, const QString& buttonText, QMdiSubWindow* parent)
: subwin(parent)
, button(button_)
: subwin(parent)
, button(button_)
{
menu = new QMenu(parent);
menuHead = menu->addAction(buttonText);
QFont f = menuHead->font();
f.setBold(true);
menuHead->setFont(f);
menuHead->setDisabled(true);
menuSep = menu->addSeparator();
menuClose = menu->addAction(tr("Close"));
connect(menuClose, &QAction::triggered, [&](bool){
subwin->close();
});
menu = new QMenu(parent);
menuHead = menu->addAction(buttonText);
QFont f = menuHead->font();
f.setBold(true);
menuHead->setFont(f);
menuHead->setDisabled(true);
menuSep = menu->addSeparator();
menuClose = menu->addAction(tr("Close"));
connect(menuClose, &QAction::triggered, [&](bool){
subwin->close();
});
}
IWin* MdiManager::createSubwindow(IWin* parent, const QString& buttonText, IWin::Type windowType, bool activate, Highlight buttonHighlight)
{
IWin* basePtr{ nullptr };
IWinStatus* statusParent{ nullptr };
QMdiSubWindow* mdiwin{ nullptr };
QString iconPath;
if (parent && parent->getType() == IWin::Type::Status)
statusParent = dynamic_cast<IWinStatus*>(parent);
switch (windowType) {
case IWin::Type::Status:
basePtr = new IWinStatus(buttonText);
if (!m_activeStatus || activate)
IWin* basePtr{ nullptr };
IWinStatus* statusParent{ nullptr };
QMdiSubWindow* mdiwin{ nullptr };
QString iconPath;
if (parent && parent->getType() == IWin::Type::Status)
statusParent = dynamic_cast<IWinStatus*>(parent);
switch (windowType) {
case IWin::Type::Status:
basePtr = new IWinStatus(buttonText);
if (!m_activeStatus || activate)
m_activeStatus = dynamic_cast<IWinStatus*>(basePtr);
iconPath = ":/Icons/serverwindow.png";
break;
iconPath = ":/Icons/serverwindow.png";
break;
case IWin::Type::Channel:
basePtr = new IWinChannel(statusParent, buttonText);
iconPath = ":/Icons/channel.png";
break;
case IWin::Type::Channel:
basePtr = new IWinChannel(statusParent, buttonText);
iconPath = ":/Icons/channel.png";
break;
case IWin::Type::Private:
basePtr = new IWinPrivate(statusParent, buttonText);
iconPath = ":/Icons/private.png";
break;
case IWin::Type::Private:
basePtr = new IWinPrivate(statusParent, buttonText);
iconPath = ":/Icons/private.png";
break;
case IWin::Type::Custom:
[[fallthrough]]; // Use createCustomSubwindow; treat this as an error.
case IWin::Type::Custom:
[[fallthrough]]; // Use createCustomSubwindow; treat this as an error.
case IWin::Type::Undefined:
break;
}
case IWin::Type::Undefined:
break;
}
if (!basePtr) {
qWarning() << "Failed to create subwindow of type" << IWin::TypeString[windowType] << "with button text" << buttonText;
return nullptr;
}
if (!basePtr) {
qWarning() << "Failed to create subwindow of type" << IWin::TypeString[windowType] << "with button text" << buttonText;
return nullptr;
}
connect(basePtr, &IWin::aboutToClose,
this, &MdiManager::subwinAboutToClose);
connect(basePtr, &IWin::aboutToClose,
this, &MdiManager::subwinAboutToClose);
mdiwin = m_mdiArea.addSubWindow(basePtr, Qt::SubWindow);
mdiwin = m_mdiArea.addSubWindow(basePtr, Qt::SubWindow);
if (!iconPath.isEmpty())
mdiwin->setWindowIcon(QIcon(iconPath));
if (!iconPath.isEmpty())
mdiwin->setWindowIcon(QIcon(iconPath));
mdiwin->setAttribute(Qt::WA_DeleteOnClose);
m_bbMgr.addButton(basePtr, mdiwin);
m_bbMgr.highlight(basePtr, buttonHighlight);
mdiwin->setAttribute(Qt::WA_DeleteOnClose);
m_bbMgr.addButton(basePtr, mdiwin);
m_bbMgr.highlight(basePtr, buttonHighlight);
QString networkKey, windowKey;
if (windowType == IWin::Type::Channel || windowType == IWin::Type::Private) {
if (windowType == IWin::Type::Channel || windowType == IWin::Type::Private) {
const auto& connection = dynamic_cast<IWinStatus*>(parent)->getConnection();
try {
@ -117,113 +117,113 @@ IWin* MdiManager::createSubwindow(IWin* parent, const QString& buttonText, IWin:
catch (...) {}
windowKey = buttonText;
}
}
QRect spawnCoord = generateSpawnCoordinates(networkKey, windowKey);
mdiwin->setGeometry(spawnCoord);
QRect spawnCoord = generateSpawnCoordinates(networkKey, windowKey);
mdiwin->setGeometry(spawnCoord);
qInfo() << "Created a subwindow of type" << IWin::TypeString[windowType] << "with button text" << buttonText;
qInfo() << "Created a subwindow of type" << IWin::TypeString[windowType] << "with button text" << buttonText;
if (!m_active || m_active->isMaximized())
mdiwin->showMaximized();
else
mdiwin->show();
if (!m_active || m_active->isMaximized())
mdiwin->showMaximized();
else
mdiwin->show();
if (!activate) {
m_mdiArea.activatePreviousSubWindow();
mdiwin->lower();
}
if (!activate) {
m_mdiArea.activatePreviousSubWindow();
mdiwin->lower();
}
if (windowType == IWin::Type::Status)
emit statusWindowCreated(dynamic_cast<IWinStatus*>(basePtr));
if (windowType == IWin::Type::Status)
emit statusWindowCreated(dynamic_cast<IWinStatus*>(basePtr));
return basePtr;
return basePtr;
}
IWin* MdiManager::currentWindow() const
{
return m_active;
return m_active;
}
IWinStatus* MdiManager::currentStatus() const
{
return m_activeStatus;
return m_activeStatus;
}
IWin* MdiManager::findWindow(IWin* statusParent, const QString& buttonText)
{
for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
IWin* subwin = dynamic_cast<IWin*>(mdiwin->widget());
if (subwin->getParent() == statusParent && buttonText.compare(subwin->getButtonText(), Qt::CaseInsensitive) == 0) {
subwin->setButtonText(buttonText);
renameWindowButton(subwin, buttonText);
return subwin;
}
}
return nullptr;
for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
IWin* subwin = dynamic_cast<IWin*>(mdiwin->widget());
if (subwin->getParent() == statusParent && buttonText.compare(subwin->getButtonText(), Qt::CaseInsensitive) == 0) {
subwin->setButtonText(buttonText);
renameWindowButton(subwin, buttonText);
return subwin;
}
}
return nullptr;
}
QMdiSubWindow* MdiManager::toMdiwin(IWin* win)
{
for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
if (mdiwin->widget() == win)
return mdiwin;
}
return nullptr;
for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
if (mdiwin->widget() == win)
return mdiwin;
}
return nullptr;
}
QList<QMdiSubWindow*> MdiManager::mdiChildrenOf(const IWin* statusParent, IWin::Type type) const
{
QList<QMdiSubWindow*> retlist;
for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
IWin* subwin = dynamic_cast<IWin*>(mdiwin->widget());
if (type != IWin::Type::Undefined && subwin->getType() != type)
continue;
if (subwin->getParent() == statusParent)
retlist.push_back(mdiwin);
}
return retlist;
QList<QMdiSubWindow*> retlist;
for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
IWin* subwin = dynamic_cast<IWin*>(mdiwin->widget());
if (type != IWin::Type::Undefined && subwin->getType() != type)
continue;
if (subwin->getParent() == statusParent)
retlist.push_back(mdiwin);
}
return retlist;
}
QList<IWin*> MdiManager::childrenOf(const IWin* statusParent, IWin::Type type) const
{
QList<IWin*> retlist;
QList<QMdiSubWindow*> subwinList = mdiChildrenOf(statusParent, type);
for (QMdiSubWindow* subwin : subwinList)
retlist.push_back(qobject_cast<IWin*>(subwin->widget()));
return retlist;
QList<IWin*> retlist;
QList<QMdiSubWindow*> subwinList = mdiChildrenOf(statusParent, type);
for (QMdiSubWindow* subwin : subwinList)
retlist.push_back(qobject_cast<IWin*>(subwin->widget()));
return retlist;
}
void MdiManager::showTrayInfo(const QString& title, const QString& message)
{
showTray(title, message, QSystemTrayIcon::MessageIcon::Information);
showTray(title, message, QSystemTrayIcon::MessageIcon::Information);
}
void MdiManager::showTrayWarn(const QString& title, const QString& message)
{
showTray(title, message, QSystemTrayIcon::MessageIcon::Warning);
showTray(title, message, QSystemTrayIcon::MessageIcon::Warning);
}
int MdiManager::connectionsOnlineCount() const
{
int c = 0;
for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
IWin* subwin = dynamic_cast<IWin*>(mdiwin->widget());
if (subwin->getType() != IWin::Type::Status)
continue;
auto* status = dynamic_cast<IWinStatus*>(subwin);
if (status->getConnection().isOnline())
++c;
}
return c;
int c = 0;
for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
IWin* subwin = dynamic_cast<IWin*>(mdiwin->widget());
if (subwin->getType() != IWin::Type::Status)
continue;
auto* status = dynamic_cast<IWinStatus*>(subwin);
if (status->getConnection().isOnline())
++c;
}
return c;
}
void MdiManager::broadcastProgramExit()
{
for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
IWin* subwin = dynamic_cast<IWin*>(mdiwin->widget());
for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
IWin* subwin = dynamic_cast<IWin*>(mdiwin->widget());
if (subwin->getType() == IWin::Type::Status) {
auto* status = dynamic_cast<IWinStatus*>(subwin);
if (status->getConnection().isOnline()) {
@ -237,93 +237,93 @@ void MdiManager::broadcastProgramExit()
status->getConnection().disconnectForExit(conf.common("QuitMessage"));
}
}
}
}
}
void MdiManager::renameWindowButton(IWin* window, const QString& text)
{
m_bbMgr.reloadButtonName(window, text);
window->setButtonText(text);
m_bbMgr.reloadButtonName(window, text);
window->setButtonText(text);
}
void MdiManager::print(IWinStatus* parent, const QString& target, const PrintType ptype, const QString& text)
{
IWin* subwin = findWindow(parent, target);
if (!subwin || !subwin->print(ptype, text)) {
if (ptype != PrintType::CTCP)
parent->print(ptype, QStringLiteral("[%1] %2").arg(target).arg(text));
else
parent->print(ptype, text);
}
IWin* subwin = findWindow(parent, target);
if (!subwin || !subwin->print(ptype, text)) {
if (ptype != PrintType::CTCP)
parent->print(ptype, QStringLiteral("[%1] %2").arg(target).arg(text));
else
parent->print(ptype, text);
}
}
void MdiManager::print(const QString& target, const PrintType ptype, const QString& text)
{
print(m_activeStatus, target, ptype, text);
print(m_activeStatus, target, ptype, text);
}
void MdiManager::printToActive(IWinStatus* parent, const PrintType ptype, const QString& text)
{
if (!m_active)
parent->print(ptype, text);
else if (m_active->getParent() != parent)
parent->print(ptype, text);
else
m_active->print(ptype, text);
if (!m_active)
parent->print(ptype, text);
else if (m_active->getParent() != parent)
parent->print(ptype, text);
else
m_active->print(ptype, text);
}
void MdiManager::printToTypes(IWinStatus* parent, const IWin::Type toType, const PrintType ptype, const QString& text)
{
for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
IWin* subwin = qobject_cast<IWin*>(mdiwin->widget());
if (subwin->getParent() == parent && subwin->getType() == toType)
subwin->print(ptype, text);
}
for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
IWin* subwin = qobject_cast<IWin*>(mdiwin->widget());
if (subwin->getParent() == parent && subwin->getType() == toType)
subwin->print(ptype, text);
}
}
void MdiManager::printToAll(IWinStatus* parent, const PrintType ptype, const QString& text)
{
for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
IWin* subwin = qobject_cast<IWin*>(mdiwin->widget());
if (subwin->getParent() == parent || subwin == parent)
subwin->print(ptype, text);
}
for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
IWin* subwin = qobject_cast<IWin*>(mdiwin->widget());
if (subwin->getParent() == parent || subwin == parent)
subwin->print(ptype, text);
}
}
void MdiManager::showTray(const QString& title, const QString& message, QSystemTrayIcon::MessageIcon icon)
{
if (QApplication::activeWindow() || QApplication::focusWidget()) return;
if (QApplication::activeWindow() || QApplication::focusWidget()) return;
ConfigMgr& conf = ConfigMgr::instance();
if (conf.common("TrayNotify") != "1") return;
int delay = conf.common("TrayNotifyDelay").toInt();
trayIcon.showMessage(title, message, icon, delay*1000);
ConfigMgr& conf = ConfigMgr::instance();
if (conf.common("TrayNotify") != "1") return;
int delay = conf.common("TrayNotifyDelay").toInt();
trayIcon.showMessage(title, message, icon, delay*1000);
}
void MdiManager::subwinAboutToClose(IWin* who)
{
qInfo() << "Closing" << who;
m_bbMgr.delButton(who);
qInfo() << "Closing" << who;
m_bbMgr.delButton(who);
}
void MdiManager::subwinActivated(QMdiSubWindow* window)
{
if (!window) return;
IWin* cw = dynamic_cast<IWin*>(window->widget());
qInfo() << "Activated:" << cw;
if (!cw) {
qWarning() << "NULL window!";
return;
}
m_bbMgr.subwinActivated(m_active, cw);
m_active = cw;
if (cw->getParent() && cw->getParent()->getType() == IWin::Type::Status)
m_activeStatus = dynamic_cast<IWinStatus*>(cw->getParent());
else if (cw->getType() == IWin::Type::Status)
m_activeStatus = qobject_cast<IWinStatus*>(cw);
emit subwindowSwitched();
if (!window) return;
IWin* cw = dynamic_cast<IWin*>(window->widget());
qInfo() << "Activated:" << cw;
if (!cw) {
qWarning() << "NULL window!";
return;
}
m_bbMgr.subwinActivated(m_active, cw);
m_active = cw;
if (cw->getParent() && cw->getParent()->getType() == IWin::Type::Status)
m_activeStatus = dynamic_cast<IWinStatus*>(cw->getParent());
else if (cw->getType() == IWin::Type::Status)
m_activeStatus = qobject_cast<IWinStatus*>(cw);
emit subwindowSwitched();
}
QRect MdiManager::generateSpawnCoordinates(const QString& networkKey, const QString& windowKey)

@ -24,68 +24,68 @@ class IWinStatus;
class MdiManager : public QObject
{
Q_OBJECT
Q_OBJECT
public:
MdiManager(QMdiArea& mdiArea, QToolBar& buttonBar, QSystemTrayIcon& trayIcon);
MdiManager(QMdiArea& mdiArea, QToolBar& buttonBar, QSystemTrayIcon& trayIcon);
static MdiManager& instance();
static MdiManager& instance();
IWin* createSubwindow(IWin* parent, const QString& buttonText, IWin::Type windowType, bool activate = true, Highlight buttonHighlight = HL_None);
IWin* currentWindow() const;
IWinStatus* currentStatus() const;
IWin* createSubwindow(IWin* parent, const QString& buttonText, IWin::Type windowType, bool activate = true, Highlight buttonHighlight = HL_None);
IWin* currentWindow() const;
IWinStatus* currentStatus() const;
IWin* findWindow(IWin* statusParent, const QString& buttonText);
QMdiSubWindow* toMdiwin(IWin* win);
QList<QMdiSubWindow*> mdiChildrenOf(const IWin* statusParent, IWin::Type type = IWin::Type::Undefined) const;
IWin* findWindow(IWin* statusParent, const QString& buttonText);
QMdiSubWindow* toMdiwin(IWin* win);
QList<QMdiSubWindow*> mdiChildrenOf(const IWin* statusParent, IWin::Type type = IWin::Type::Undefined) const;
QList<IWin*> childrenOf(const IWin* statusParent, IWin::Type type = IWin::Type::Undefined) const;
QList<QMdiSubWindow*> mdiChildren() const;
void showTrayInfo(const QString& title, const QString& message);
void showTrayWarn(const QString& title, const QString& message);
int connectionsOnlineCount() const;
void broadcastProgramExit();
void renameWindowButton(IWin* window, const QString& text);
void showTrayWarn(const QString& title, const QString& message);
int connectionsOnlineCount() const;
void broadcastProgramExit();
void renameWindowButton(IWin* window, const QString& text);
void print(IWinStatus* parent, const QString& target, const PrintType ptype, const QString& text);
void print(const QString& target, const PrintType ptype, const QString& text);
void printToActive(IWinStatus* parent, const PrintType ptype, const QString& text);
void printToTypes(IWinStatus* parent, const IWin::Type toType, const PrintType ptype, const QString& text);
void printToAll(IWinStatus* parent, const PrintType ptype, const QString& text);
void print(IWinStatus* parent, const QString& target, const PrintType ptype, const QString& text);
void print(const QString& target, const PrintType ptype, const QString& text);
void printToActive(IWinStatus* parent, const PrintType ptype, const QString& text);
void printToTypes(IWinStatus* parent, const IWin::Type toType, const PrintType ptype, const QString& text);
void printToAll(IWinStatus* parent, const PrintType ptype, const QString& text);
void highlight(IWin* window, Highlight state) { m_bbMgr.highlight(window, state); }
private:
struct ButtonBarEntry {
ButtonBarEntry(){}
ButtonBarEntry(QAction* button_, const QString& buttonText, QMdiSubWindow* parent);
QMdiSubWindow* subwin{ nullptr };
QAction* button{ nullptr };
QMenu* menu{ nullptr };
QAction* menuHead{ nullptr };
QAction* menuSep{ nullptr };
QAction* menuClose{ nullptr };
};
QMdiArea& m_mdiArea;
QSystemTrayIcon& trayIcon;
IWinStatus* m_activeStatus{ nullptr };
IWin* m_active{ nullptr };
ButtonbarMgr m_bbMgr;
void showTray(const QString& title, const QString& message, QSystemTrayIcon::MessageIcon icon);
void subwinAboutToClose(IWin* who);
void subwinActivated(QMdiSubWindow *window);
int nextXY{ 0 };
int nextXYadjust{ 0 };
QRect generateSpawnCoordinates(const QString& networkKey, const QString& windowKey);
static MdiManager* m_instance;
struct ButtonBarEntry {
ButtonBarEntry(){}
ButtonBarEntry(QAction* button_, const QString& buttonText, QMdiSubWindow* parent);
QMdiSubWindow* subwin{ nullptr };
QAction* button{ nullptr };
QMenu* menu{ nullptr };
QAction* menuHead{ nullptr };
QAction* menuSep{ nullptr };
QAction* menuClose{ nullptr };
};
QMdiArea& m_mdiArea;
QSystemTrayIcon& trayIcon;
IWinStatus* m_activeStatus{ nullptr };
IWin* m_active{ nullptr };
ButtonbarMgr m_bbMgr;
void showTray(const QString& title, const QString& message, QSystemTrayIcon::MessageIcon icon);
void subwinAboutToClose(IWin* who);
void subwinActivated(QMdiSubWindow *window);
int nextXY{ 0 };
int nextXYadjust{ 0 };
QRect generateSpawnCoordinates(const QString& networkKey, const QString& windowKey);
static MdiManager* m_instance;
signals:
void readyForExit();
void subwindowSwitched();
void connectionStateChange();
void statusWindowCreated(IWinStatus* window);
void readyForExit();
void subwindowSwitched();
void connectionStateChange();
void statusWindowCreated(IWinStatus* window);
};
#endif // MDIMANAGER_H

@ -9,27 +9,27 @@
#define SCRIPTEVENTS_H
namespace ScriptEvent {
constexpr auto* Start = "start";
constexpr auto* Exit = "exit";
constexpr auto* Connected = "connected";
constexpr auto* Disconnected = "disconnected";
constexpr auto* Privmsg = "privmsg";
constexpr auto* Notice = "notice";
constexpr auto* CtcpRequest = "ctcprequest";
constexpr auto* CtcpReply = "ctcpreply";
constexpr auto* Nick = "nick";
constexpr auto* Wallops = "wallops";
constexpr auto* Join = "join";
constexpr auto* Part = "part";
constexpr auto* Quit = "quit";
constexpr auto* Kick = "kick";
constexpr auto* Mode = "mode";
constexpr auto* Topic = "topic";
constexpr auto* Invite = "invite";
constexpr auto* Kill = "kill";
constexpr auto* Error = "error";
constexpr auto* Numeric = "numeric";
constexpr auto* Away = "away"; // IRCv3 only
constexpr auto* Start = "start";
constexpr auto* Exit = "exit";
constexpr auto* Connected = "connected";
constexpr auto* Disconnected = "disconnected";
constexpr auto* Privmsg = "privmsg";
constexpr auto* Notice = "notice";
constexpr auto* CtcpRequest = "ctcprequest";
constexpr auto* CtcpReply = "ctcpreply";
constexpr auto* Nick = "nick";
constexpr auto* Wallops = "wallops";
constexpr auto* Join = "join";
constexpr auto* Part = "part";
constexpr auto* Quit = "quit";
constexpr auto* Kick = "kick";
constexpr auto* Mode = "mode";
constexpr auto* Topic = "topic";
constexpr auto* Invite = "invite";
constexpr auto* Kill = "kill";
constexpr auto* Error = "error";
constexpr auto* Numeric = "numeric";
constexpr auto* Away = "away"; // IRCv3 only
}
#endif // SCRIPTEVENTS_H

@ -71,8 +71,8 @@ void runtimeEnvironmentSetup()
int main(int argc, char *argv[])
{
qSetMessagePattern("[%{time}] %{file}:%{line} %{message}");
Q_INIT_RESOURCE(resources);
qSetMessagePattern("[%{time}] %{file}:%{line} %{message}");
Q_INIT_RESOURCE(resources);
QApplication a(argc, argv);
qInfo() << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~";
@ -82,8 +82,8 @@ int main(int argc, char *argv[])
qDebug() << "Global path:" << GLOBAL_PATH;
qDebug() << "Local path:" << LOCAL_PATH;
IdealIRC w;
w.show();
IdealIRC w;
w.show();
return a.exec();
return a.exec();
}

@ -21,99 +21,99 @@ bool alreadyRegistered = false;
void registerBuiltinFunctions()
{
if (alreadyRegistered) return;
alreadyRegistered = true;
if (alreadyRegistered) return;
alreadyRegistered = true;
/*
* Mathematical functions
*/
Script::registerUniversalFunction("pow", &Builtin::Mathematics::pow);
Script::registerUniversalFunction("sqrt", &Builtin::Mathematics::sqrt);
Script::registerUniversalFunction("cbrt", &Builtin::Mathematics::cbrt);
Script::registerUniversalFunction("floor", &Builtin::Mathematics::floor);
Script::registerUniversalFunction("ceil", &Builtin::Mathematics::ceil);
/*
* Mathematical functions
*/
Script::registerUniversalFunction("pow", &Builtin::Mathematics::pow);
Script::registerUniversalFunction("sqrt", &Builtin::Mathematics::sqrt);
Script::registerUniversalFunction("cbrt", &Builtin::Mathematics::cbrt);
Script::registerUniversalFunction("floor", &Builtin::Mathematics::floor);
Script::registerUniversalFunction("ceil", &Builtin::Mathematics::ceil);
Script::registerUniversalFunction("round", &Builtin::Mathematics::round);
Script::registerUniversalFunction("abs", &Builtin::Mathematics::abs);
Script::registerUniversalFunction("clamp", &Builtin::Mathematics::clamp);
Script::registerUniversalFunction("clamp01", &Builtin::Mathematics::clamp01);
Script::registerUniversalFunction("rand", &Builtin::Mathematics::rand);
Script::registerUniversalFunction("scale", &Builtin::Mathematics::scale);
Script::registerUniversalFunction("log", &Builtin::Mathematics::log);
Script::registerUniversalFunction("log10", &Builtin::Mathematics::log10);
Script::registerUniversalFunction("sin", &Builtin::Mathematics::sin);
Script::registerUniversalFunction("asin", &Builtin::Mathematics::asin);
Script::registerUniversalFunction("cos", &Builtin::Mathematics::cos);
Script::registerUniversalFunction("acos", &Builtin::Mathematics::acos);
Script::registerUniversalFunction("tan", &Builtin::Mathematics::tan);
Script::registerUniversalFunction("atan", &Builtin::Mathematics::atan);
Script::registerUniversalFunction("abs", &Builtin::Mathematics::abs);
Script::registerUniversalFunction("clamp", &Builtin::Mathematics::clamp);
Script::registerUniversalFunction("clamp01", &Builtin::Mathematics::clamp01);
Script::registerUniversalFunction("rand", &Builtin::Mathematics::rand);
Script::registerUniversalFunction("scale", &Builtin::Mathematics::scale);
Script::registerUniversalFunction("log", &Builtin::Mathematics::log);
Script::registerUniversalFunction("log10", &Builtin::Mathematics::log10);
Script::registerUniversalFunction("sin", &Builtin::Mathematics::sin);
Script::registerUniversalFunction("asin", &Builtin::Mathematics::asin);
Script::registerUniversalFunction("cos", &Builtin::Mathematics::cos);
Script::registerUniversalFunction("acos", &Builtin::Mathematics::acos);
Script::registerUniversalFunction("tan", &Builtin::Mathematics::tan);
Script::registerUniversalFunction("atan", &Builtin::Mathematics::atan);
/*
* String utilities
*/
Script::registerUniversalFunction("strupper", &Builtin::StringUtils::strupper);
Script::registerUniversalFunction("strlower", &Builtin::StringUtils::strlower);
Script::registerUniversalFunction("strlen", &Builtin::StringUtils::strlen);
Script::registerUniversalFunction("strmid", &Builtin::StringUtils::strmid);
Script::registerUniversalFunction("strsplit", &Builtin::StringUtils::strsplit);
Script::registerUniversalFunction("strfind", &Builtin::StringUtils::strfind);
Script::registerUniversalFunction("strleft", &Builtin::StringUtils::strleft);
Script::registerUniversalFunction("strright", &Builtin::StringUtils::strright);
Script::registerUniversalFunction("strrep", &Builtin::StringUtils::strrep);
Script::registerUniversalFunction("resemblesint", &Builtin::StringUtils::resemblesint);
Script::registerUniversalFunction("resemblesreal", &Builtin::StringUtils::resemblesreal);
Script::registerUniversalFunction("resemblesbool", &Builtin::StringUtils::resemblesbool);
/*
* String utilities
*/
Script::registerUniversalFunction("strupper", &Builtin::StringUtils::strupper);
Script::registerUniversalFunction("strlower", &Builtin::StringUtils::strlower);
Script::registerUniversalFunction("strlen", &Builtin::StringUtils::strlen);
Script::registerUniversalFunction("strmid", &Builtin::StringUtils::strmid);
Script::registerUniversalFunction("strsplit", &Builtin::StringUtils::strsplit);
Script::registerUniversalFunction("strfind", &Builtin::StringUtils::strfind);
Script::registerUniversalFunction("strleft", &Builtin::StringUtils::strleft);
Script::registerUniversalFunction("strright", &Builtin::StringUtils::strright);
Script::registerUniversalFunction("strrep", &Builtin::StringUtils::strrep);
Script::registerUniversalFunction("resemblesint", &Builtin::StringUtils::resemblesint);
Script::registerUniversalFunction("resemblesreal", &Builtin::StringUtils::resemblesreal);
Script::registerUniversalFunction("resemblesbool", &Builtin::StringUtils::resemblesbool);
/*
* General utilities
*/
Script::registerUniversalFunction("toint", &Builtin::GeneralUtils::toint);
Script::registerUniversalFunction("tostr", &Builtin::GeneralUtils::tostr);
Script::registerUniversalFunction("todouble", &Builtin::GeneralUtils::toreal);
Script::registerUniversalFunction("tobool", &Builtin::GeneralUtils::tobool);
Script::registerUniversalFunction("isvoid", &Builtin::GeneralUtils::isvoid);
Script::registerUniversalFunction("isint", &Builtin::GeneralUtils::isint);
Script::registerUniversalFunction("isreal", &Builtin::GeneralUtils::isreal);
Script::registerUniversalFunction("isbool", &Builtin::GeneralUtils::isbool);
Script::registerUniversalFunction("isstr", &Builtin::GeneralUtils::isstr);
Script::registerUniversalFunction("ismap", &Builtin::GeneralUtils::ismap);
Script::registerUniversalFunction("decimalsep", &Builtin::GeneralUtils::decimalsep);
/*
* General utilities
*/
Script::registerUniversalFunction("toint", &Builtin::GeneralUtils::toint);
Script::registerUniversalFunction("tostr", &Builtin::GeneralUtils::tostr);
Script::registerUniversalFunction("todouble", &Builtin::GeneralUtils::toreal);
Script::registerUniversalFunction("tobool", &Builtin::GeneralUtils::tobool);
Script::registerUniversalFunction("isvoid", &Builtin::GeneralUtils::isvoid);
Script::registerUniversalFunction("isint", &Builtin::GeneralUtils::isint);
Script::registerUniversalFunction("isreal", &Builtin::GeneralUtils::isreal);
Script::registerUniversalFunction("isbool", &Builtin::GeneralUtils::isbool);
Script::registerUniversalFunction("isstr", &Builtin::GeneralUtils::isstr);
Script::registerUniversalFunction("ismap", &Builtin::GeneralUtils::ismap);
Script::registerUniversalFunction("decimalsep", &Builtin::GeneralUtils::decimalsep);
/*
* Map utilities
*/
Script::registerUniversalFunction("map", &Builtin::MapUtils::map);
Script::registerUniversalFunction("maplen", &Builtin::MapUtils::maplen);
Script::registerUniversalFunction("mapremove", &Builtin::MapUtils::mapremove);
Script::registerUniversalFunction("mapdump", &Builtin::MapUtils::mapdump);
/*
* Map utilities
*/
Script::registerUniversalFunction("map", &Builtin::MapUtils::map);
Script::registerUniversalFunction("maplen", &Builtin::MapUtils::maplen);
Script::registerUniversalFunction("mapremove", &Builtin::MapUtils::mapremove);
Script::registerUniversalFunction("mapdump", &Builtin::MapUtils::mapdump);
Script::registerUniversalFunction("mapkeys", &Builtin::MapUtils::mapkeys);
/*
* List utilities
*/
Script::registerUniversalFunction("list", &Builtin::ListUtils::list);
Script::registerUniversalFunction("listpush", &Builtin::ListUtils::listpush);
Script::registerUniversalFunction("listpop", &Builtin::ListUtils::listpop);
Script::registerUniversalFunction("listlen", &Builtin::ListUtils::listlen);
Script::registerUniversalFunction("listremove", &Builtin::ListUtils::listremove);
Script::registerUniversalFunction("listjoin", &Builtin::ListUtils::listjoin);
Script::registerUniversalFunction("listsplice", &Builtin::ListUtils::listsplice);
/*
* List utilities
*/
Script::registerUniversalFunction("list", &Builtin::ListUtils::list);
Script::registerUniversalFunction("listpush", &Builtin::ListUtils::listpush);
Script::registerUniversalFunction("listpop", &Builtin::ListUtils::listpop);
Script::registerUniversalFunction("listlen", &Builtin::ListUtils::listlen);
Script::registerUniversalFunction("listremove", &Builtin::ListUtils::listremove);
Script::registerUniversalFunction("listjoin", &Builtin::ListUtils::listjoin);
Script::registerUniversalFunction("listsplice", &Builtin::ListUtils::listsplice);
/*
* Dialog utilities
*/
Script::registerUniversalFunction("dialog", &Builtin::DialogUtils::dialog);
Script::registerUniversalFunction("dlgvalidhndl", &Builtin::DialogUtils::dlgvalidhndl);
Script::registerUniversalFunction("dlgclose", &Builtin::DialogUtils::dlgclose);
Script::registerUniversalFunction("dlghide", &Builtin::DialogUtils::dlghide);
Script::registerUniversalFunction("dlgshow", &Builtin::DialogUtils::dlgshow);
Script::registerUniversalFunction("dlglist", &Builtin::DialogUtils::dlglist);
Script::registerUniversalFunction("dlgsetattr", &Builtin::DialogUtils::dlgsetattr);
Script::registerUniversalFunction("dlggetattr", &Builtin::DialogUtils::dlggetattr);
Script::registerUniversalFunction("dlgsetwidgetattr", &Builtin::DialogUtils::dlgsetwidgetattr);
Script::registerUniversalFunction("dlggetwidgetattr", &Builtin::DialogUtils::dlggetwidgetattr);
Script::registerUniversalFunction("dlgtablecount", &Builtin::DialogUtils::dlgtablecount);
Script::registerUniversalFunction("dlgtableinsert", &Builtin::DialogUtils::dlgtableinsert);
Script::registerUniversalFunction("dlgtableremove", &Builtin::DialogUtils::dlgtableremove);
Script::registerUniversalFunction("dlgtableselected", &Builtin::DialogUtils::dlgtableselected);
Script::registerUniversalFunction("dlgtablerow", &Builtin::DialogUtils::dlgtablerow);
/*
* Dialog utilities
*/
Script::registerUniversalFunction("dialog", &Builtin::DialogUtils::dialog);
Script::registerUniversalFunction("dlgvalidhndl", &Builtin::DialogUtils::dlgvalidhndl);
Script::registerUniversalFunction("dlgclose", &Builtin::DialogUtils::dlgclose);
Script::registerUniversalFunction("dlghide", &Builtin::DialogUtils::dlghide);
Script::registerUniversalFunction("dlgshow", &Builtin::DialogUtils::dlgshow);
Script::registerUniversalFunction("dlglist", &Builtin::DialogUtils::dlglist);
Script::registerUniversalFunction("dlgsetattr", &Builtin::DialogUtils::dlgsetattr);
Script::registerUniversalFunction("dlggetattr", &Builtin::DialogUtils::dlggetattr);
Script::registerUniversalFunction("dlgsetwidgetattr", &Builtin::DialogUtils::dlgsetwidgetattr);
Script::registerUniversalFunction("dlggetwidgetattr", &Builtin::DialogUtils::dlggetwidgetattr);
Script::registerUniversalFunction("dlgtablecount", &Builtin::DialogUtils::dlgtablecount);
Script::registerUniversalFunction("dlgtableinsert", &Builtin::DialogUtils::dlgtableinsert);
Script::registerUniversalFunction("dlgtableremove", &Builtin::DialogUtils::dlgtableremove);
Script::registerUniversalFunction("dlgtableselected", &Builtin::DialogUtils::dlgtableselected);
Script::registerUniversalFunction("dlgtablerow", &Builtin::DialogUtils::dlgtablerow);
}

@ -17,238 +17,238 @@ namespace Builtin::DialogUtils {
ValueHolder dialog(Script& script, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("dialog", args.size(), 1);
if (args.size() != 1)
throwInsufficientParameters("dialog", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("dialog", 1);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("dialog", 1);
return script.dialogConstruct(ValueExtract(args[0]).toString());
return script.dialogConstruct(ValueExtract(args[0]).toString());
}
ValueHolder dlgvalidhndl(Script& script, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("dlgvalidhndl", args.size(), 1);
if (args.size() != 1)
throwInsufficientParameters("dlgvalidhndl", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer)
return false;
if (args[0].getType() != ValueHolder::Type::Integer)
return false;
return script.dialogIsValidHandle(ValueExtract(args[0]).toInt());
return script.dialogIsValidHandle(ValueExtract(args[0]).toInt());
}
ValueHolder dlgclose(Script& script, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("dlgclose", args.size(), 1);
if (args.size() != 1)
throwInsufficientParameters("dlgclose", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlgclose", 1);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlgclose", 1);
script.dialogClose(ValueExtract(args[0]).toInt());
return ValueHolder();
script.dialogClose(ValueExtract(args[0]).toInt());
return ValueHolder();
}
ValueHolder dlghide(Script& script, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("dlghide", args.size(), 1);
if (args.size() != 1)
throwInsufficientParameters("dlghide", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlghide", 1);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlghide", 1);
script.dialogHide(ValueExtract(args[0]).toInt());
return ValueHolder();
script.dialogHide(ValueExtract(args[0]).toInt());
return ValueHolder();
}
ValueHolder dlgshow(Script& script, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("dlgshow", args.size(), 1);
if (args.size() != 1)
throwInsufficientParameters("dlgshow", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlgshow", 1);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlgshow", 1);
script.dialogShow(ValueExtract(args[0]).toInt());
return ValueHolder();
script.dialogShow(ValueExtract(args[0]).toInt());
return ValueHolder();
}
ValueHolder dlglist(Script& script, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("dlglist", args.size(), 1);
if (args.size() != 1)
throwInsufficientParameters("dlglist", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("dlglist", 1);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("dlglist", 1);
return script.dialogList(ValueExtract(args[0]).toString());
return script.dialogList(ValueExtract(args[0]).toString());
}
ValueHolder dlgsetattr(Script& script, std::vector<ValueHolder>& args)
{
if (args.size() != 3)
throwInsufficientParameters("dlgsetattr", args.size(), 3);
if (args.size() != 3)
throwInsufficientParameters("dlgsetattr", args.size(), 3);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlgsetattr", 1);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlgsetattr", 1);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("dlgsetattr", 2);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("dlgsetattr", 2);
int handle = ValueExtract(args[0]).toInt();
const auto& attribute = ValueExtract(args[1]).toString();
ValueHolder& value = args[2];
script.dialogSetAttr(handle, attribute, value);
int handle = ValueExtract(args[0]).toInt();
const auto& attribute = ValueExtract(args[1]).toString();
ValueHolder& value = args[2];
script.dialogSetAttr(handle, attribute, value);
return ValueHolder();
return ValueHolder();
}
ValueHolder dlggetattr(Script& script, std::vector<ValueHolder>& args)
{
if (args.size() != 2)
throwInsufficientParameters("dlggetattr", args.size(), 2);
if (args.size() != 2)
throwInsufficientParameters("dlggetattr", args.size(), 2);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlggetattr", 1);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlggetattr", 1);
int handle = ValueExtract(args[0]).toInt();
const auto& attribute = ValueExtract(args[1]).toString();
int handle = ValueExtract(args[0]).toInt();
const auto& attribute = ValueExtract(args[1]).toString();
return script.dialogGetAttr(handle, attribute);
return script.dialogGetAttr(handle, attribute);
}
ValueHolder dlgsetwidgetattr(Script& script, std::vector<ValueHolder>& args)
{
if (args.size() != 4)
throwInsufficientParameters("dlgsetwidgetattr", args.size(), 4);
if (args.size() != 4)
throwInsufficientParameters("dlgsetwidgetattr", args.size(), 4);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlgsetwidgetattr", 1);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlgsetwidgetattr", 1);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("dlgsetwidgetattr", 2);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("dlgsetwidgetattr", 2);
if (args[2].getType() != ValueHolder::Type::String)
throwNotAString("dlgsetwidgetattr", 3);
if (args[2].getType() != ValueHolder::Type::String)
throwNotAString("dlgsetwidgetattr", 3);
int handle = ValueExtract(args[0]).toInt();
const auto& widgetName = ValueExtract(args[1]).toString();
const auto& attribute = ValueExtract(args[2]).toString();
ValueHolder& value = args[3];
script.dialogSetWidgetAttr(handle, widgetName, attribute, value);
int handle = ValueExtract(args[0]).toInt();
const auto& widgetName = ValueExtract(args[1]).toString();
const auto& attribute = ValueExtract(args[2]).toString();
ValueHolder& value = args[3];
script.dialogSetWidgetAttr(handle, widgetName, attribute, value);
return ValueHolder();
return ValueHolder();
}
ValueHolder dlggetwidgetattr(Script& script, std::vector<ValueHolder>& args)
{
if (args.size() != 3)
throwInsufficientParameters("dlggetwidgetattr", args.size(), 3);
if (args.size() != 3)
throwInsufficientParameters("dlggetwidgetattr", args.size(), 3);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlggetwidgetattr", 1);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlggetwidgetattr", 1);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("dlgsetwidgetattr", 2);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("dlgsetwidgetattr", 2);
int handle = ValueExtract(args[0]).toInt();
const auto& widgetName = ValueExtract(args[1]).toString();
const auto& attribute = ValueExtract(args[2]).toString();
int handle = ValueExtract(args[0]).toInt();
const auto& widgetName = ValueExtract(args[1]).toString();
const auto& attribute = ValueExtract(args[2]).toString();
return script.dialogGetWidgetAttr(handle, widgetName, attribute);
return script.dialogGetWidgetAttr(handle, widgetName, attribute);
}
ValueHolder dlgtablecount(Script& script, std::vector<ValueHolder>& args)
{
if (args.size() != 2)
throwInsufficientParameters("dlgtablecount", args.size(), 2);
if (args.size() != 2)
throwInsufficientParameters("dlgtablecount", args.size(), 2);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlgtablecount", 1);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlgtablecount", 1);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("dlgtablecount", 2);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("dlgtablecount", 2);
int handle = ValueExtract(args[0]).toInt();
const auto& widgetName = ValueExtract(args[1]).toString();
int handle = ValueExtract(args[0]).toInt();
const auto& widgetName = ValueExtract(args[1]).toString();
return script.dialogTableWidgetCount(handle, widgetName);
return script.dialogTableWidgetCount(handle, widgetName);
}
ValueHolder dlgtableinsert(Script& script, std::vector<ValueHolder>& args)
{
if (args.size() != 3)
throwInsufficientParameters("dlgtableinsert", args.size(), 3);
if (args.size() != 3)
throwInsufficientParameters("dlgtableinsert", args.size(), 3);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlgtableinsert", 1);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlgtableinsert", 1);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("dlgtableinsert", 2);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("dlgtableinsert", 2);
if (args[2].getType() != ValueHolder::Type::Map)
throwNotAMap("dlgtableinsert", 3);
if (args[2].getType() != ValueHolder::Type::Map)
throwNotAMap("dlgtableinsert", 3);
int handle = ValueExtract(args[0]).toInt();
const auto& widgetName = ValueExtract(args[1]).toString();
const auto& cols = ValueExtract(args[2]).toMap();
int handle = ValueExtract(args[0]).toInt();
const auto& widgetName = ValueExtract(args[1]).toString();
const auto& cols = ValueExtract(args[2]).toMap();
script.dialogTableWidgetInsert(handle, widgetName, cols);
return ValueHolder();
script.dialogTableWidgetInsert(handle, widgetName, cols);
return ValueHolder();
}
ValueHolder dlgtableremove(Script& script, std::vector<ValueHolder>& args)
{
if (args.size() != 3)
throwInsufficientParameters("dlgtableremove", args.size(), 3);
if (args.size() != 3)
throwInsufficientParameters("dlgtableremove", args.size(), 3);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlgtableremove", 1);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotADialogHandle("dlgtableremove", 1);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("dlgtableremove", 2);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("dlgtableremove", 2);
if (args[2].getType() != ValueHolder::Type::Integer)
throwNotANumber("dlgtableremove", 3);
if (args[2].getType() != ValueHolder::Type::Integer)
throwNotANumber("dlgtableremove", 3);
int handle = ValueExtract(args[0]).toInt();
const auto& widgetName = ValueExtract(args[1]).toString();
const auto& index = ValueExtract(args[2]).toInt();
int handle = ValueExtract(args[0]).toInt();
const auto& widgetName = ValueExtract(args[1]).toString();
const auto& index = ValueExtract(args[2]).toInt();
script.dialogTableWidgetRemove(handle, widgetName, index);
return ValueHolder();
script.dialogTableWidgetRemove(handle, widgetName, index);
return ValueHolder();
}
ValueHolder dlgtableselected(Script& script, std::vector<ValueHolder>& args)
{
if (args.size() != 2)
throwInsufficientParameters("dlgtableselected", args.size(), 2);
if (args.size() != 2)
throwInsufficientParameters("dlgtableselected", args.size(), 2);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("dlgtableselected", 2);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("dlgtableselected", 2);
int handle = ValueExtract(args[0]).toInt();
const auto& widgetName = ValueExtract(args[1]).toString();
int handle = ValueExtract(args[0]).toInt();
const auto& widgetName = ValueExtract(args[1]).toString();
return script.dialogTableWidgetSelected(handle, widgetName);
return script.dialogTableWidgetSelected(handle, widgetName);
}
ValueHolder dlgtablerow(Script& script, std::vector<ValueHolder>& args)
{
if (args.size() != 3)
throwInsufficientParameters("dlgtablerow", args.size(), 3);
if (args.size() != 3)
throwInsufficientParameters("dlgtablerow", args.size(), 3);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("dlgtablerow", 2);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("dlgtablerow", 2);
if (args[2].getType() != ValueHolder::Type::Integer)
throwNotANumber("dlgtablerow", 3);
if (args[2].getType() != ValueHolder::Type::Integer)
throwNotANumber("dlgtablerow", 3);
int handle = ValueExtract(args[0]).toInt();
const std::string& widgetName = ValueExtract(args[1]).toString();
int row = ValueExtract(args[2]).toInt();
int handle = ValueExtract(args[0]).toInt();
const std::string& widgetName = ValueExtract(args[1]).toString();
int row = ValueExtract(args[2]).toInt();
return script.dialogTableWidgetRow(handle, widgetName, row);
return script.dialogTableWidgetRow(handle, widgetName, row);
}
}

@ -13,27 +13,27 @@ namespace {
//! Add the "st"/"nd"/"rd"/"th" suffixes. Only supports values up to 20.
std::string ith(int i)
{
std::string ret = std::to_string(i);
switch (i) {
case -1: [[fallthrough]];
case 1:
ret += "st";
break;
std::string ret = std::to_string(i);
switch (i) {
case -1: [[fallthrough]];
case 1:
ret += "st";
break;
case -2: [[fallthrough]];
case 2:
ret += "nd";
break;
case -2: [[fallthrough]];
case 2:
ret += "nd";
break;
case -3: [[fallthrough]];
case 3:
ret += "rd";
break;
case -3: [[fallthrough]];
case 3:
ret += "rd";
break;
default:
ret += "th";
}
return ret;
default:
ret += "th";
}
return ret;
}
}
@ -41,10 +41,10 @@ namespace Builtin::Error {
void throwInsufficientParameters(const std::string& fname, size_t given, size_t required)
{
std::stringstream ss;
ss << fname << ": Insufficient parameter count. "
<< required << " required, " << given << " given";
throw ScriptException(ss.str());
std::stringstream ss;
ss << fname << ": Insufficient parameter count. "
<< required << " required, " << given << " given";
throw ScriptException(ss.str());
}
void throwInsufficientParameters(const std::string& fname, size_t given, size_t requiredLow, size_t requiredHigh)
@ -57,12 +57,12 @@ void throwInsufficientParameters(const std::string& fname, size_t given, size_t
void throwNotAString(const std::string& fname, int paramPos)
{
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a string");
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a string");
}
void throwNotANumber(const std::string& fname, int paramPos)
{
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a number");
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a number");
}
void throwNotAStringOrWholeNumber(const std::string& fname, int paramPos)
@ -72,12 +72,12 @@ void throwNotAStringOrWholeNumber(const std::string& fname, int paramPos)
void throwNotAWholeNumber(const std::string& fname, int paramPos)
{
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a whole number");
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a whole number");
}
void throwNotADialogHandle(const std::string& fname, int paramPos)
{
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a dialog handle (a whole number)");
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a dialog handle (a whole number)");
}
void throwNotAnIniHandle(const std::string& fname, int paramPos)
@ -87,33 +87,33 @@ void throwNotAnIniHandle(const std::string& fname, int paramPos)
void throwNotAMap(const std::string& fname, int paramPos)
{
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a map");
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a map");
}
void throwNotAList(const std::string& fname, int paramPos)
{
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a list");
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a list");
}
void throwIndexOutOfRange(const std::string& fname, int paramPos)
{
throw ScriptException(fname + ": " + ith(paramPos) + " parameter is out of range");
throw ScriptException(fname + ": " + ith(paramPos) + " parameter is out of range");
}
void throwNotAReference(const std::string& fname, int paramPos)
{
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a reference");
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a reference");
}
void throwNegativeInputUndefined(const std::string& fname)
{
throw ScriptException(fname + ": Negative input is undefined");
throw ScriptException(fname + ": Negative input is undefined");
}
void throwNumberMustBePositive(const std::string& fname, int paramPos)
{
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a positive number");
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a positive number");
}
void throwUnrecognizedIniHandle(const std::string& fname, int handle)

@ -17,9 +17,9 @@ using namespace Builtin::Error;
namespace {
inline std::string decimalSeparator()
{
std::string ret;
ret += std::use_facet<std::numpunct<char>>(std::cout.getloc()).decimal_point();
return ret;
std::string ret;
ret += std::use_facet<std::numpunct<char>>(std::cout.getloc()).decimal_point();
return ret;
}
}
@ -27,154 +27,154 @@ namespace Builtin::GeneralUtils {
ValueHolder toint(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("toint", args.size(), 1);
using VType = ValueHolder::Type;
switch (args[0].getType()) {
case VType::Void:
throw ScriptException("toint: A void value cannot convert to int");
case VType::Map:
throw ScriptException("toint: A map cannot convert to int");
case VType::Integer: [[fallthrough]];
case VType::Real: [[fallthrough]];
case VType::Bool:
return ValueExtract(args[0]).toInt();
case VType::String:
{
const std::string& val = ValueExtract(args[0]).toString();
if (val == "false")
return 0;
if (val == "true")
return 1;
return std::stoi(val);
}
}
return {}; // Compiler complains even though all cases are covered in the above switch statement.
if (args.size() != 1)
throwInsufficientParameters("toint", args.size(), 1);
using VType = ValueHolder::Type;
switch (args[0].getType()) {
case VType::Void:
throw ScriptException("toint: A void value cannot convert to int");
case VType::Map:
throw ScriptException("toint: A map cannot convert to int");
case VType::Integer: [[fallthrough]];
case VType::Real: [[fallthrough]];
case VType::Bool:
return ValueExtract(args[0]).toInt();
case VType::String:
{
const std::string& val = ValueExtract(args[0]).toString();
if (val == "false")
return 0;
if (val == "true")
return 1;
return std::stoi(val);
}
}
return {}; // Compiler complains even though all cases are covered in the above switch statement.
}
ValueHolder tostr(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("tostr", args.size(), 1);
if (args.size() != 1)
throwInsufficientParameters("tostr", args.size(), 1);
return args[0].toStdString();
return args[0].toStdString();
}
ValueHolder toreal(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("toreal", args.size(), 1);
using VType = ValueHolder::Type;
switch (args[0].getType()) {
case VType::Void:
throw ScriptException("toint: A void value cannot convert to real");
case VType::Map:
throw ScriptException("toint: A map cannot convert to double");
case VType::Integer: [[fallthrough]];
case VType::Real: [[fallthrough]];
case VType::Bool:
return ValueExtract(args[0]).toReal();
case VType::String:
{
std::string val = ValueExtract(args[0]).toString();
if (val == "false")
return 0.0;
if (val == "true")
return 1.0;
auto dotpos = val.find('.');
if (dotpos != val.npos)
val.replace(dotpos, 1, decimalSeparator());
return std::stod(val);
}
}
if (args.size() != 1)
throwInsufficientParameters("toreal", args.size(), 1);
using VType = ValueHolder::Type;
switch (args[0].getType()) {
case VType::Void:
throw ScriptException("toint: A void value cannot convert to real");
case VType::Map:
throw ScriptException("toint: A map cannot convert to double");
case VType::Integer: [[fallthrough]];
case VType::Real: [[fallthrough]];
case VType::Bool:
return ValueExtract(args[0]).toReal();
case VType::String:
{
std::string val = ValueExtract(args[0]).toString();
if (val == "false")
return 0.0;
if (val == "true")
return 1.0;
auto dotpos = val.find('.');
if (dotpos != val.npos)
val.replace(dotpos, 1, decimalSeparator());
return std::stod(val);
}
}
return {}; // Compiler complains even though all cases are covered in the above switch statement.
}
ValueHolder tobool(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("tobool", args.size(), 1);
using VType = ValueHolder::Type;
switch (args[0].getType()) {
case VType::Void:
throw ScriptException("toint: A void value cannot convert to bool");
case VType::Map:
throw ScriptException("toint: A map cannot convert to bool");
case VType::Integer: [[fallthrough]];
case VType::Real: [[fallthrough]];
case VType::Bool:
return ValueExtract(args[0]).toBool();
case VType::String:
{
std::string val = ValueExtract(args[0]).toString();
if (val == "true" || val == "1")
return true;
else
return false;
}
}
return {}; // Compiler complains even though all cases are covered in the above switch statement.
if (args.size() != 1)
throwInsufficientParameters("tobool", args.size(), 1);
using VType = ValueHolder::Type;
switch (args[0].getType()) {
case VType::Void:
throw ScriptException("toint: A void value cannot convert to bool");
case VType::Map:
throw ScriptException("toint: A map cannot convert to bool");
case VType::Integer: [[fallthrough]];
case VType::Real: [[fallthrough]];
case VType::Bool:
return ValueExtract(args[0]).toBool();
case VType::String:
{
std::string val = ValueExtract(args[0]).toString();
if (val == "true" || val == "1")
return true;
else
return false;
}
}
return {}; // Compiler complains even though all cases are covered in the above switch statement.
}
ValueHolder isvoid(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("isvoid", args.size(), 1);
if (args.size() != 1)
throwInsufficientParameters("isvoid", args.size(), 1);
return args[0].getType() == ValueHolder::Type::Void;
return args[0].getType() == ValueHolder::Type::Void;
}
ValueHolder isint(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("isint", args.size(), 1);
if (args.size() != 1)
throwInsufficientParameters("isint", args.size(), 1);
return args[0].getType() == ValueHolder::Type::Integer;
return args[0].getType() == ValueHolder::Type::Integer;
}
ValueHolder isreal(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("isreal", args.size(), 1);
if (args.size() != 1)
throwInsufficientParameters("isreal", args.size(), 1);
return args[0].getType() == ValueHolder::Type::Real;
return args[0].getType() == ValueHolder::Type::Real;
}
ValueHolder isbool(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("isbool", args.size(), 1);
if (args.size() != 1)
throwInsufficientParameters("isbool", args.size(), 1);
return args[0].getType() == ValueHolder::Type::Bool;
return args[0].getType() == ValueHolder::Type::Bool;
}
ValueHolder isstr(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("isstring", args.size(), 1);
if (args.size() != 1)
throwInsufficientParameters("isstring", args.size(), 1);
return args[0].getType() == ValueHolder::Type::String;
return args[0].getType() == ValueHolder::Type::String;
}
ValueHolder ismap(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("ismap", args.size(), 1);
if (args.size() != 1)
throwInsufficientParameters("ismap", args.size(), 1);
return args[0].getType() == ValueHolder::Type::Map;
return args[0].getType() == ValueHolder::Type::Map;
}
ValueHolder decimalsep(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 0)
throwInsufficientParameters("decimalsep", 0, 1);
return decimalSeparator();
if (args.size() != 0)
throwInsufficientParameters("decimalsep", 0, 1);
return decimalSeparator();
}
}

@ -19,187 +19,187 @@ namespace Builtin::ListUtils {
ValueHolder list(Script&, std::vector<ValueHolder>& args)
{
ValueArray ret;
int idx = 0;
for (auto& val : args)
ret.emplace(std::to_string(idx++), new ValueHolder(std::move(val)));
ret.emplace(LIST_END_MAGIC, new ValueHolder(idx));
return ValueHolder(std::move(ret));
ValueArray ret;
int idx = 0;
for (auto& val : args)
ret.emplace(std::to_string(idx++), new ValueHolder(std::move(val)));
ret.emplace(LIST_END_MAGIC, new ValueHolder(idx));
return ValueHolder(std::move(ret));
}
ValueHolder listpush(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 2)
throwInsufficientParameters("listpush", args.size(), 2);
if (!args[0].isReference())
throwNotAReference("listpush", 1);
if (args[0].getType() != ValueHolder::Type::Map)
throwNotAList("listpush", 1);
try {
ValueArray& arr = ValueExtract(args[0]).toMap();
ValueHolder& endVal = *arr[LIST_END_MAGIC];
arr.emplace(std::to_string(ValueExtract(endVal).toInt()), new ValueHolder(args[1]));
endVal = endVal + 1;
} catch (const std::out_of_range&) {
throwNotAList("listpush", 1);
}
return ValueHolder();
if (args.size() != 2)
throwInsufficientParameters("listpush", args.size(), 2);
if (!args[0].isReference())
throwNotAReference("listpush", 1);
if (args[0].getType() != ValueHolder::Type::Map)
throwNotAList("listpush", 1);
try {
ValueArray& arr = ValueExtract(args[0]).toMap();
ValueHolder& endVal = *arr[LIST_END_MAGIC];
arr.emplace(std::to_string(ValueExtract(endVal).toInt()), new ValueHolder(args[1]));
endVal = endVal + 1;
} catch (const std::out_of_range&) {
throwNotAList("listpush", 1);
}
return ValueHolder();
}
ValueHolder listpop(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("listpop", args.size(), 1);
if (!args[0].isReference())
throwNotAReference("listpop", 1);
if (args[0].getType() != ValueHolder::Type::Map)
throwNotAList("listpop", 1);
try {
ValueArray& arr = ValueExtract(args[0]).toMap();
ValueHolder& endVal = *arr[LIST_END_MAGIC];
int end = ValueExtract(endVal).toInt();
if (arr.size() == 0)
throw ScriptException("Cannot pop an empty list");
ValueHolder ret = *arr.at(std::to_string(end - 1));
arr.erase(std::to_string(end - 1));
endVal = end - 1;
return ret;
} catch (const std::out_of_range&) {
throwNotAList("listpop", 1);
}
if (args.size() != 1)
throwInsufficientParameters("listpop", args.size(), 1);
if (!args[0].isReference())
throwNotAReference("listpop", 1);
if (args[0].getType() != ValueHolder::Type::Map)
throwNotAList("listpop", 1);
try {
ValueArray& arr = ValueExtract(args[0]).toMap();
ValueHolder& endVal = *arr[LIST_END_MAGIC];
int end = ValueExtract(endVal).toInt();
if (arr.size() == 0)
throw ScriptException("Cannot pop an empty list");
ValueHolder ret = *arr.at(std::to_string(end - 1));
arr.erase(std::to_string(end - 1));
endVal = end - 1;
return ret;
} catch (const std::out_of_range&) {
throwNotAList("listpop", 1);
}
}
ValueHolder listlen(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("listlen", args.size(), 1);
if (args.size() != 1)
throwInsufficientParameters("listlen", args.size(), 1);
if (!args[0].isReference())
throwNotAReference("listlen", 1);
if (!args[0].isReference())
throwNotAReference("listlen", 1);
if (args[0].getType() != ValueHolder::Type::Map)
throwNotAList("listlen", 1);
if (args[0].getType() != ValueHolder::Type::Map)
throwNotAList("listlen", 1);
ValueArray& arr = ValueExtract(args[0]).toMap();
if (arr.count(LIST_END_MAGIC) < 1)
throwNotAList("listlen", 1);
ValueArray& arr = ValueExtract(args[0]).toMap();
if (arr.count(LIST_END_MAGIC) < 1)
throwNotAList("listlen", 1);
return *arr[LIST_END_MAGIC];
return *arr[LIST_END_MAGIC];
}
ValueHolder listremove(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 2)
throwInsufficientParameters("listremove", args.size(), 2);
if (args.size() != 2)
throwInsufficientParameters("listremove", args.size(), 2);
if (!args[0].isReference())
throwNotAReference("listremove", 1);
if (!args[0].isReference())
throwNotAReference("listremove", 1);
if (args[0].getType() != ValueHolder::Type::Map)
throwNotAList("listremove", 1);
if (args[0].getType() != ValueHolder::Type::Map)
throwNotAList("listremove", 1);
if (args[1].getType() != ValueHolder::Type::Integer)
throwNotAWholeNumber("listremove", 2);
if (args[1].getType() != ValueHolder::Type::Integer)
throwNotAWholeNumber("listremove", 2);
int index = ValueExtract(args[1]).toInt();
ValueArray& arr = ValueExtract(args[0]).toMap();
ValueHolder& endVal = *arr[LIST_END_MAGIC];
int end = ValueExtract(endVal).toInt();
int index = ValueExtract(args[1]).toInt();
ValueArray& arr = ValueExtract(args[0]).toMap();
ValueHolder& endVal = *arr[LIST_END_MAGIC];
int end = ValueExtract(endVal).toInt();
if (index < 0 || index >= end)
throwIndexOutOfRange("listremove", 2);
if (index < 0 || index >= end)
throwIndexOutOfRange("listremove", 2);
arr.erase(std::to_string(index));
for (int i = index + 1; i < end; ++i) {
auto node = arr.extract(std::to_string(i));
node.key() = std::to_string(i - 1);
arr.insert(std::move(node));
}
endVal = end - 1;
arr.erase(std::to_string(index));
for (int i = index + 1; i < end; ++i) {
auto node = arr.extract(std::to_string(i));
node.key() = std::to_string(i - 1);
arr.insert(std::move(node));
}
endVal = end - 1;
return ValueHolder();
return ValueHolder();
}
ValueHolder listjoin(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 2)
throwInsufficientParameters("listjoin", args.size(), 2);
if (args.size() != 2)
throwInsufficientParameters("listjoin", args.size(), 2);
if (!args[0].isReference())
throwNotAReference("listjoin", 1);
if (!args[0].isReference())
throwNotAReference("listjoin", 1);
if (args[0].getType() != ValueHolder::Type::Map)
throwNotAList("listjoin", 1);
if (args[0].getType() != ValueHolder::Type::Map)
throwNotAList("listjoin", 1);
if (!args[1].isReference())
throwNotAReference("listjoin", 2);
if (!args[1].isReference())
throwNotAReference("listjoin", 2);
if (args[1].getType() != ValueHolder::Type::Map)
throwNotAList("listjoin", 2);
if (args[1].getType() != ValueHolder::Type::Map)
throwNotAList("listjoin", 2);
ValueArray& M1arr = ValueExtract(args[0]).toMap();
ValueHolder& M1endVal = *M1arr[LIST_END_MAGIC];
int M1end = ValueExtract(M1endVal).toInt();
ValueArray& M1arr = ValueExtract(args[0]).toMap();
ValueHolder& M1endVal = *M1arr[LIST_END_MAGIC];
int M1end = ValueExtract(M1endVal).toInt();
ValueArray& M2arr = ValueExtract(args[1]).toMap();
ValueHolder& M2endVal = *M2arr[LIST_END_MAGIC];
int M2end = ValueExtract(M2endVal).toInt();
ValueArray& M2arr = ValueExtract(args[1]).toMap();
ValueHolder& M2endVal = *M2arr[LIST_END_MAGIC];
int M2end = ValueExtract(M2endVal).toInt();
for (int i = 0; i < M2end; ++i) {
const int newIdx = i + M1end;
auto& vh = *M2arr[std::to_string(i)];
M1arr.emplace(std::to_string(newIdx), new ValueHolder(vh));
}
M1endVal = M1end + M2end;
return ValueHolder();
for (int i = 0; i < M2end; ++i) {
const int newIdx = i + M1end;
auto& vh = *M2arr[std::to_string(i)];
M1arr.emplace(std::to_string(newIdx), new ValueHolder(vh));
}
M1endVal = M1end + M2end;
return ValueHolder();
}
ValueHolder listsplice(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 2)
throwInsufficientParameters("listsplice", args.size(), 2);
if (args.size() != 2)
throwInsufficientParameters("listsplice", args.size(), 2);
if (!args[0].isReference())
throwNotAReference("listsplice", 1);
if (!args[0].isReference())
throwNotAReference("listsplice", 1);
if (args[0].getType() != ValueHolder::Type::Map)
throwNotAList("listsplice", 1);
if (args[0].getType() != ValueHolder::Type::Map)
throwNotAList("listsplice", 1);
if (!args[1].isReference())
throwNotAReference("listsplice", 2);
if (!args[1].isReference())
throwNotAReference("listsplice", 2);
if (args[1].getType() != ValueHolder::Type::Map)
throwNotAList("listsplice", 2);
if (args[1].getType() != ValueHolder::Type::Map)
throwNotAList("listsplice", 2);
ValueArray ret;
ValueArray ret;
ValueArray& M1arr = ValueExtract(args[0]).toMap();
ValueHolder& M1endVal = *M1arr[LIST_END_MAGIC];
int M1end = ValueExtract(M1endVal).toInt();
ValueArray& M1arr = ValueExtract(args[0]).toMap();
ValueHolder& M1endVal = *M1arr[LIST_END_MAGIC];
int M1end = ValueExtract(M1endVal).toInt();
for (int i = 0; i < M1end; ++i) {
auto& vh = *M1arr[std::to_string(i)];
ret.emplace(std::to_string(i), new ValueHolder(vh));
}
for (int i = 0; i < M1end; ++i) {
auto& vh = *M1arr[std::to_string(i)];
ret.emplace(std::to_string(i), new ValueHolder(vh));
}
ValueArray& M2arr = ValueExtract(args[1]).toMap();
ValueHolder& M2endVal = *M2arr[LIST_END_MAGIC];
int M2end = ValueExtract(M2endVal).toInt();
ValueArray& M2arr = ValueExtract(args[1]).toMap();
ValueHolder& M2endVal = *M2arr[LIST_END_MAGIC];
int M2end = ValueExtract(M2endVal).toInt();
for (int i = 0; i < M2end; ++i) {
const int newIdx = i + M1end;
auto& vh = *M2arr[std::to_string(i)];
ret.emplace(std::to_string(newIdx), new ValueHolder(vh));
}
ret.emplace(LIST_END_MAGIC, new ValueHolder(M1end + M2end));
for (int i = 0; i < M2end; ++i) {
const int newIdx = i + M1end;
auto& vh = *M2arr[std::to_string(i)];
ret.emplace(std::to_string(newIdx), new ValueHolder(vh));
}
ret.emplace(LIST_END_MAGIC, new ValueHolder(M1end + M2end));
return ValueHolder(std::move(ret));
return ValueHolder(std::move(ret));
}
}

@ -18,65 +18,65 @@ namespace Builtin::MapUtils {
ValueHolder map(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 0)
throwInsufficientParameters("map", args.size(), 0);
return ValueArray();
if (args.size() != 0)
throwInsufficientParameters("map", args.size(), 0);
return ValueArray();
}
ValueHolder maplen(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("maplen", args.size(), 1);
if (args.size() != 1)
throwInsufficientParameters("maplen", args.size(), 1);
const ValueArray& arr = ValueExtract(args[0]).toMap();
return static_cast<int>(arr.size());
const ValueArray& arr = ValueExtract(args[0]).toMap();
return static_cast<int>(arr.size());
}
ValueHolder mapremove(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 2)
throwInsufficientParameters("mapremove", args.size(), 1);
if (args.size() != 2)
throwInsufficientParameters("mapremove", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Map)
throwNotAMap("mapremove", 1);
if (args[0].getType() != ValueHolder::Type::Map)
throwNotAMap("mapremove", 1);
if (args[1].getType() == ValueHolder::Type::Void
|| args[1].getType() == ValueHolder::Type::Map)
throw ScriptException("mapremove: 2nd parameter must either be an integer, real, bool or string");
if (args[1].getType() == ValueHolder::Type::Void
|| args[1].getType() == ValueHolder::Type::Map)
throw ScriptException("mapremove: 2nd parameter must either be an integer, real, bool or string");
ValueArray& arr = ValueExtract(args[0]).toMap();
return arr.erase(args[1].toStdString()) > 0;
ValueArray& arr = ValueExtract(args[0]).toMap();
return arr.erase(args[1].toStdString()) > 0;
}
ValueHolder mapdump(Script&, std::vector<ValueHolder>& args)
{
std::cout << "--- Map dump ---\n";
ValueArray& arr = ValueExtract(args[0]).toMap();
for (const auto& item : arr) {
auto type = item.second->getType();
switch (type) {
case ValueHolder::Type::Void:
std::cout << '"' << item.first << "\" = Void\n";
break;
case ValueHolder::Type::Integer:
std::cout << '"' << item.first << "\" = Integer(" << ValueExtract(*item.second).toInt() << ")\n";
break;
case ValueHolder::Type::Real:
std::cout << '"' << item.first << "\" = Real(" << ValueExtract(*item.second).toReal() << ")\n";
break;
case ValueHolder::Type::Bool:
std::cout << '"' << item.first << "\" = Bool(" << ValueExtract(*item.second).toBool() << ")\n";
break;
case ValueHolder::Type::String:
std::cout << '"' << item.first << "\" = String(" << ValueExtract(*item.second).toString() << ")\n";
break;
case ValueHolder::Type::Map:
std::cout << '"' << item.first << "\" = Map\n";
break;
}
}
std::cout << "--- End map dump ---" << std::endl;
return {};
std::cout << "--- Map dump ---\n";
ValueArray& arr = ValueExtract(args[0]).toMap();
for (const auto& item : arr) {
auto type = item.second->getType();
switch (type) {
case ValueHolder::Type::Void:
std::cout << '"' << item.first << "\" = Void\n";
break;
case ValueHolder::Type::Integer:
std::cout << '"' << item.first << "\" = Integer(" << ValueExtract(*item.second).toInt() << ")\n";
break;
case ValueHolder::Type::Real:
std::cout << '"' << item.first << "\" = Real(" << ValueExtract(*item.second).toReal() << ")\n";
break;
case ValueHolder::Type::Bool:
std::cout << '"' << item.first << "\" = Bool(" << ValueExtract(*item.second).toBool() << ")\n";
break;
case ValueHolder::Type::String:
std::cout << '"' << item.first << "\" = String(" << ValueExtract(*item.second).toString() << ")\n";
break;
case ValueHolder::Type::Map:
std::cout << '"' << item.first << "\" = Map\n";
break;
}
}
std::cout << "--- End map dump ---" << std::endl;
return {};
}
ValueHolder mapkeys(Script&, std::vector<ValueHolder>& args)

@ -18,427 +18,427 @@ namespace Builtin::Mathematics {
ValueHolder pow(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 2)
throwInsufficientParameters("pow", args.size(), 2);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("pow", 1);
if (args[1].getType() != ValueHolder::Type::Integer && args[1].getType() != ValueHolder::Type::Real)
throwNotANumber("pow", 2);
if (args[0] == 0 && args[1] == 0)
throw ScriptException("pow: 0^0 is undefined");
if (args[1] < 0)
throw ScriptException("pow: Cannot represent negative exponents");
if (args[0].getType() == ValueHolder::Type::Real
|| args[1].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
auto P = ValueExtract(args[1]).toReal();
return std::pow(N, P);
}
else {
auto N = ValueExtract(args[0]).toInt();
auto P = ValueExtract(args[1]).toInt();
return static_cast<int>(std::pow(N, P));
}
if (args.size() != 2)
throwInsufficientParameters("pow", args.size(), 2);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("pow", 1);
if (args[1].getType() != ValueHolder::Type::Integer && args[1].getType() != ValueHolder::Type::Real)
throwNotANumber("pow", 2);
if (args[0] == 0 && args[1] == 0)
throw ScriptException("pow: 0^0 is undefined");
if (args[1] < 0)
throw ScriptException("pow: Cannot represent negative exponents");
if (args[0].getType() == ValueHolder::Type::Real
|| args[1].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
auto P = ValueExtract(args[1]).toReal();
return std::pow(N, P);
}
else {
auto N = ValueExtract(args[0]).toInt();
auto P = ValueExtract(args[1]).toInt();
return static_cast<int>(std::pow(N, P));
}
}
ValueHolder sqrt(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("sqrt", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("sqrt", 1);
if (args[0] < 0)
throw ScriptException("sqrt: Root of " + args[0].toStdString() + " is an imaginary number, not supported.");
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return std::sqrt(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
return std::sqrt(N);
}
if (args.size() != 1)
throwInsufficientParameters("sqrt", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("sqrt", 1);
if (args[0] < 0)
throw ScriptException("sqrt: Root of " + args[0].toStdString() + " is an imaginary number, not supported.");
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return std::sqrt(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
return std::sqrt(N);
}
}
ValueHolder cbrt(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("cbrt", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("cbrt", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return std::cbrt(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
return std::cbrt(N);
}
if (args.size() != 1)
throwInsufficientParameters("cbrt", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("cbrt", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return std::cbrt(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
return std::cbrt(N);
}
}
ValueHolder floor(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("floor", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("floor", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return static_cast<int>(std::floor(N));
}
else {
return args[0];
}
if (args.size() != 1)
throwInsufficientParameters("floor", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("floor", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return static_cast<int>(std::floor(N));
}
else {
return args[0];
}
}
ValueHolder ceil(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("ceil", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("ceil", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return static_cast<int>(std::ceil(N));
}
else {
return args[0];
}
if (args.size() != 1)
throwInsufficientParameters("ceil", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("ceil", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return static_cast<int>(std::ceil(N));
}
else {
return args[0];
}
}
ValueHolder round(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("round", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("round", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return static_cast<int>(std::round(N));
}
else {
return args[0];
}
if (args.size() != 1)
throwInsufficientParameters("round", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("round", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return static_cast<int>(std::round(N));
}
else {
return args[0];
}
}
ValueHolder abs(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("abs", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("abs", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return static_cast<int>(std::abs(N));
}
else {
auto N = ValueExtract(args[0]).toInt();
return static_cast<int>(std::abs(N));
}
if (args.size() != 1)
throwInsufficientParameters("abs", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("abs", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return static_cast<int>(std::abs(N));
}
else {
auto N = ValueExtract(args[0]).toInt();
return static_cast<int>(std::abs(N));
}
}
ValueHolder clamp(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 3)
throwInsufficientParameters("clamp", args.size(), 3);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("clamp", 1);
if (args[1].getType() != ValueHolder::Type::Integer && args[1].getType() != ValueHolder::Type::Real)
throwNotANumber("clamp", 2);
if (args[2].getType() != ValueHolder::Type::Integer && args[2].getType() != ValueHolder::Type::Real)
throwNotANumber("clamp", 3);
if (args[0].getType() == ValueHolder::Type::Real
|| args[1].getType() == ValueHolder::Type::Real
|| args[2].getType() == ValueHolder::Type::Real) {
auto low = ValueExtract(args[1]).toReal();
auto high = ValueExtract(args[2]).toReal();
if (low > high)
throw ScriptException("clamp: low > high");
auto N = ValueExtract(args[0]).toReal();
if (N < low)
return low;
else if (N > high)
return high;
else
return N;
}
else {
auto low = ValueExtract(args[1]).toInt();
auto high = ValueExtract(args[2]).toInt();
if (low > high)
throw ScriptException("clamp: low > high");
auto N = ValueExtract(args[0]).toInt();
if (N < low)
return low;
else if (N > high)
return high;
else
return N;
}
if (args.size() != 3)
throwInsufficientParameters("clamp", args.size(), 3);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("clamp", 1);
if (args[1].getType() != ValueHolder::Type::Integer && args[1].getType() != ValueHolder::Type::Real)
throwNotANumber("clamp", 2);
if (args[2].getType() != ValueHolder::Type::Integer && args[2].getType() != ValueHolder::Type::Real)
throwNotANumber("clamp", 3);
if (args[0].getType() == ValueHolder::Type::Real
|| args[1].getType() == ValueHolder::Type::Real
|| args[2].getType() == ValueHolder::Type::Real) {
auto low = ValueExtract(args[1]).toReal();
auto high = ValueExtract(args[2]).toReal();
if (low > high)
throw ScriptException("clamp: low > high");
auto N = ValueExtract(args[0]).toReal();
if (N < low)
return low;
else if (N > high)
return high;
else
return N;
}
else {
auto low = ValueExtract(args[1]).toInt();
auto high = ValueExtract(args[2]).toInt();
if (low > high)
throw ScriptException("clamp: low > high");
auto N = ValueExtract(args[0]).toInt();
if (N < low)
return low;
else if (N > high)
return high;
else
return N;
}
}
ValueHolder clamp01(Script& script, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("clamp01", args.size(), 1);
if (args.size() != 1)
throwInsufficientParameters("clamp01", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("clamp01", 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("clamp01", 1);
std::vector<ValueHolder> clampArgs { args[0], 0, 1 };
return clamp(script, clampArgs);
std::vector<ValueHolder> clampArgs { args[0], 0, 1 };
return clamp(script, clampArgs);
}
ValueHolder rand(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 2)
throwInsufficientParameters("rand", args.size(), 2);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("rand", 1);
if (args[1].getType() != ValueHolder::Type::Integer && args[1].getType() != ValueHolder::Type::Real)
throwNotANumber("rand", 2);
std::random_device rd;
std::mt19937 mt(rd());
if (args[0].getType() == ValueHolder::Type::Real || args[1].getType() == ValueHolder::Type::Real) {
auto low = ValueExtract(args[0]).toReal();
auto high = ValueExtract(args[1]).toReal();
if (low > high)
throw ScriptException("rand: low > high");
std::uniform_real_distribution<double> uniform_dist(low, high);
return uniform_dist(mt);
}
else {
auto low = ValueExtract(args[0]).toInt();
auto high = ValueExtract(args[1]).toInt();
if (low > high)
throw ScriptException("rand: low > high");
std::uniform_int_distribution<int> uniform_dist(low, high);
return uniform_dist(mt);
}
if (args.size() != 2)
throwInsufficientParameters("rand", args.size(), 2);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("rand", 1);
if (args[1].getType() != ValueHolder::Type::Integer && args[1].getType() != ValueHolder::Type::Real)
throwNotANumber("rand", 2);
std::random_device rd;
std::mt19937 mt(rd());
if (args[0].getType() == ValueHolder::Type::Real || args[1].getType() == ValueHolder::Type::Real) {
auto low = ValueExtract(args[0]).toReal();
auto high = ValueExtract(args[1]).toReal();
if (low > high)
throw ScriptException("rand: low > high");
std::uniform_real_distribution<double> uniform_dist(low, high);
return uniform_dist(mt);
}
else {
auto low = ValueExtract(args[0]).toInt();
auto high = ValueExtract(args[1]).toInt();
if (low > high)
throw ScriptException("rand: low > high");
std::uniform_int_distribution<int> uniform_dist(low, high);
return uniform_dist(mt);
}
}
ValueHolder scale(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 5)
throwInsufficientParameters("scale", args.size(), 5);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("scale", 1);
if (args[1].getType() != ValueHolder::Type::Integer && args[1].getType() != ValueHolder::Type::Real)
throwNotANumber("scale", 2);
if (args[2].getType() != ValueHolder::Type::Integer && args[2].getType() != ValueHolder::Type::Real)
throwNotANumber("scale", 3);
if (args[3].getType() != ValueHolder::Type::Integer && args[3].getType() != ValueHolder::Type::Real)
throwNotANumber("scale", 4);
if (args[4].getType() != ValueHolder::Type::Integer && args[4].getType() != ValueHolder::Type::Real)
throwNotANumber("scale", 5);
if (args[0].getType() == ValueHolder::Type::Real
|| args[1].getType() == ValueHolder::Type::Real
|| args[2].getType() == ValueHolder::Type::Real
|| args[3].getType() == ValueHolder::Type::Real
|| args[4].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
auto oldMin = ValueExtract(args[1]).toReal();
auto oldMax = ValueExtract(args[2]).toReal();
auto newMin = ValueExtract(args[3]).toReal();
auto newMax = ValueExtract(args[4]).toReal();
return newMin + (N - oldMin) * (newMax - newMin) / (oldMax - oldMin);
}
else {
auto N = ValueExtract(args[0]).toInt();
auto oldMin = ValueExtract(args[1]).toInt();
auto oldMax = ValueExtract(args[2]).toInt();
auto newMin = ValueExtract(args[3]).toInt();
auto newMax = ValueExtract(args[4]).toInt();
return newMin + (N - oldMin) * (newMax - newMin) / (oldMax - oldMin);
}
if (args.size() != 5)
throwInsufficientParameters("scale", args.size(), 5);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("scale", 1);
if (args[1].getType() != ValueHolder::Type::Integer && args[1].getType() != ValueHolder::Type::Real)
throwNotANumber("scale", 2);
if (args[2].getType() != ValueHolder::Type::Integer && args[2].getType() != ValueHolder::Type::Real)
throwNotANumber("scale", 3);
if (args[3].getType() != ValueHolder::Type::Integer && args[3].getType() != ValueHolder::Type::Real)
throwNotANumber("scale", 4);
if (args[4].getType() != ValueHolder::Type::Integer && args[4].getType() != ValueHolder::Type::Real)
throwNotANumber("scale", 5);
if (args[0].getType() == ValueHolder::Type::Real
|| args[1].getType() == ValueHolder::Type::Real
|| args[2].getType() == ValueHolder::Type::Real
|| args[3].getType() == ValueHolder::Type::Real
|| args[4].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
auto oldMin = ValueExtract(args[1]).toReal();
auto oldMax = ValueExtract(args[2]).toReal();
auto newMin = ValueExtract(args[3]).toReal();
auto newMax = ValueExtract(args[4]).toReal();
return newMin + (N - oldMin) * (newMax - newMin) / (oldMax - oldMin);
}
else {
auto N = ValueExtract(args[0]).toInt();
auto oldMin = ValueExtract(args[1]).toInt();
auto oldMax = ValueExtract(args[2]).toInt();
auto newMin = ValueExtract(args[3]).toInt();
auto newMax = ValueExtract(args[4]).toInt();
return newMin + (N - oldMin) * (newMax - newMin) / (oldMax - oldMin);
}
}
ValueHolder log(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("log", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("log", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
if (N < 0)
throwNegativeInputUndefined("log");
return std::log(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
if (N < 0)
throwNegativeInputUndefined("log");
return std::log(N);
}
if (args.size() != 1)
throwInsufficientParameters("log", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("log", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
if (N < 0)
throwNegativeInputUndefined("log");
return std::log(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
if (N < 0)
throwNegativeInputUndefined("log");
return std::log(N);
}
}
ValueHolder log10(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("log10", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("log10", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
if (N < 0)
throwNegativeInputUndefined("log10");
return std::log10(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
if (N < 0)
throwNegativeInputUndefined("log10");
return std::log10(N);
}
if (args.size() != 1)
throwInsufficientParameters("log10", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("log10", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
if (N < 0)
throwNegativeInputUndefined("log10");
return std::log10(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
if (N < 0)
throwNegativeInputUndefined("log10");
return std::log10(N);
}
}
ValueHolder sin(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("sin", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("sin", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return std::sin(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
return std::sin(N);
}
if (args.size() != 1)
throwInsufficientParameters("sin", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("sin", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return std::sin(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
return std::sin(N);
}
}
ValueHolder asin(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("asin", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("asin", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return std::asin(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
return std::asin(N);
}
if (args.size() != 1)
throwInsufficientParameters("asin", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("asin", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return std::asin(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
return std::asin(N);
}
}
ValueHolder cos(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("cos", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("cos", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return std::cos(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
return std::cos(N);
}
if (args.size() != 1)
throwInsufficientParameters("cos", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("cos", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return std::cos(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
return std::cos(N);
}
}
ValueHolder acos(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("acos", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("acos", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return std::acos(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
return std::acos(N);
}
if (args.size() != 1)
throwInsufficientParameters("acos", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("acos", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return std::acos(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
return std::acos(N);
}
}
ValueHolder tan(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("tan", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("tan", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return std::tan(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
return std::tan(N);
}
if (args.size() != 1)
throwInsufficientParameters("tan", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("tan", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return std::tan(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
return std::tan(N);
}
}
ValueHolder atan(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("atan", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("atan", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return std::atan(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
return std::atan(N);
}
if (args.size() != 1)
throwInsufficientParameters("atan", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer && args[0].getType() != ValueHolder::Type::Real)
throwNotANumber("atan", 1);
if (args[0].getType() == ValueHolder::Type::Real) {
auto N = ValueExtract(args[0]).toReal();
return std::atan(N);
}
else {
auto N = ValueExtract(args[0]).toInt();
return std::atan(N);
}
}
} // namespace Builtin::Mathematics

@ -49,231 +49,231 @@ ValueHolder strlower(Script&, std::vector<ValueHolder>& args)
ValueHolder strlen(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("strlen", args.size(), 1);
if (args.size() != 1)
throwInsufficientParameters("strlen", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("strlen", 1);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("strlen", 1);
return static_cast<int>(ValueExtract(args[0]).toString().length());
return static_cast<int>(ValueExtract(args[0]).toString().length());
}
ValueHolder strmid(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 3)
throwInsufficientParameters("strmid", args.size(), 3);
if (args.size() != 3)
throwInsufficientParameters("strmid", args.size(), 3);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("strmid", 1);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("strmid", 1);
if (args[1].getType() != ValueHolder::Type::Integer)
throwNotAWholeNumber("strmid", 2);
if (args[1].getType() != ValueHolder::Type::Integer)
throwNotAWholeNumber("strmid", 2);
if (args[2].getType() != ValueHolder::Type::Integer)
throwNotAWholeNumber("strmid", 3);
if (args[2].getType() != ValueHolder::Type::Integer)
throwNotAWholeNumber("strmid", 3);
const auto& S = ValueExtract(args[0]).toString();
const auto start = ValueExtract(args[1]).toInt();
const auto length = ValueExtract(args[2]).toInt();
const auto& S = ValueExtract(args[0]).toString();
const auto start = ValueExtract(args[1]).toInt();
const auto length = ValueExtract(args[2]).toInt();
if (start < 0)
throwNumberMustBePositive("strmid", 2);
if (start < 0)
throwNumberMustBePositive("strmid", 2);
if (length < 0)
throwNumberMustBePositive("strmid", 3);
if (length < 0)
throwNumberMustBePositive("strmid", 3);
if (S.empty() || static_cast<unsigned>(start) > S.length() || length == 0)
return std::string();
if (S.empty() || static_cast<unsigned>(start) > S.length() || length == 0)
return std::string();
return S.substr(static_cast<unsigned>(start), static_cast<unsigned>(length));
return S.substr(static_cast<unsigned>(start), static_cast<unsigned>(length));
}
ValueHolder strsplit(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 2)
throwInsufficientParameters("strsplit", args.size(), 2);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("strsplit", 1);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("strsplit", 2);
ValueArray ret;
const auto& S = ValueExtract(args[0]).toString();
const auto& delim = ValueExtract(args[1]).toString();
if (delim.empty())
throw ScriptException("strsplit: Delimiter is an empty string");
const std::size_t delimlen = delim.size();
std::size_t start = 0;
std::size_t delimpos = 0;
int idx = 0;
while ((delimpos = S.find(delim, start)) != S.npos) {
auto text = S.substr(start, delimpos - start);
if (!text.empty())
ret.emplace(std::to_string(idx++), new ValueHolder(text));
start = delimpos + delimlen;
}
auto rest = S.substr(start);
if (!rest.empty())
ret.emplace(std::to_string(idx), new ValueHolder(rest));
return std::move(ret);
if (args.size() != 2)
throwInsufficientParameters("strsplit", args.size(), 2);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("strsplit", 1);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("strsplit", 2);
ValueArray ret;
const auto& S = ValueExtract(args[0]).toString();
const auto& delim = ValueExtract(args[1]).toString();
if (delim.empty())
throw ScriptException("strsplit: Delimiter is an empty string");
const std::size_t delimlen = delim.size();
std::size_t start = 0;
std::size_t delimpos = 0;
int idx = 0;
while ((delimpos = S.find(delim, start)) != S.npos) {
auto text = S.substr(start, delimpos - start);
if (!text.empty())
ret.emplace(std::to_string(idx++), new ValueHolder(text));
start = delimpos + delimlen;
}
auto rest = S.substr(start);
if (!rest.empty())
ret.emplace(std::to_string(idx), new ValueHolder(rest));
return std::move(ret);
}
ValueHolder strfind(Script&, std::vector<ValueHolder>& args)
{
if (args.size() < 2 || args.size() > 3)
throwInsufficientParameters("strfind", args.size(), 2);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("strfind", 1);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("strfind", 2);
std::size_t start = 0;
if (args.size() == 3) {
if (args[2].getType() != ValueHolder::Type::Integer)
throwNotAWholeNumber("strfind", 3);
int val = ValueExtract(args[2]).toInt();
if (val < 0)
throwNumberMustBePositive("strfind", 3);
start = static_cast<unsigned>(val);
}
const auto& S = ValueExtract(args[0]).toString();
const auto& search = ValueExtract(args[1]).toString();
if (search.empty())
throw ScriptException("strfind: Searching for an empty string");
auto pos = S.find(search, start);
if (pos == S.npos)
return -1;
else
return static_cast<int>(pos);
if (args.size() < 2 || args.size() > 3)
throwInsufficientParameters("strfind", args.size(), 2);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("strfind", 1);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("strfind", 2);
std::size_t start = 0;
if (args.size() == 3) {
if (args[2].getType() != ValueHolder::Type::Integer)
throwNotAWholeNumber("strfind", 3);
int val = ValueExtract(args[2]).toInt();
if (val < 0)
throwNumberMustBePositive("strfind", 3);
start = static_cast<unsigned>(val);
}
const auto& S = ValueExtract(args[0]).toString();
const auto& search = ValueExtract(args[1]).toString();
if (search.empty())
throw ScriptException("strfind: Searching for an empty string");
auto pos = S.find(search, start);
if (pos == S.npos)
return -1;
else
return static_cast<int>(pos);
}
ValueHolder strleft(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 2)
throwInsufficientParameters("strleft", args.size(), 2);
if (args.size() != 2)
throwInsufficientParameters("strleft", args.size(), 2);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("strleft", 1);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("strleft", 1);
if (args[1].getType() != ValueHolder::Type::Integer)
throwNotAWholeNumber("strleft", 1);
if (args[1].getType() != ValueHolder::Type::Integer)
throwNotAWholeNumber("strleft", 1);
const auto& S = ValueExtract(args[0]).toString();
const auto len = ValueExtract(args[1]).toInt();
const auto& S = ValueExtract(args[0]).toString();
const auto len = ValueExtract(args[1]).toInt();
if (len < 0)
throwNumberMustBePositive("strleft", 2);
if (len < 0)
throwNumberMustBePositive("strleft", 2);
return S.substr(0, static_cast<unsigned>(len));
return S.substr(0, static_cast<unsigned>(len));
}
ValueHolder strright(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 2)
throwInsufficientParameters("strright", args.size(), 2);
if (args.size() != 2)
throwInsufficientParameters("strright", args.size(), 2);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("strright", 1);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("strright", 1);
if (args[1].getType() != ValueHolder::Type::Integer)
throwNotAWholeNumber("strright", 1);
if (args[1].getType() != ValueHolder::Type::Integer)
throwNotAWholeNumber("strright", 1);
const auto& S = ValueExtract(args[0]).toString();
const auto len = ValueExtract(args[1]).toInt();
const auto& S = ValueExtract(args[0]).toString();
const auto len = ValueExtract(args[1]).toInt();
if (len < 0)
throwNumberMustBePositive("strright", 2);
if (len < 0)
throwNumberMustBePositive("strright", 2);
int pos = static_cast<int>(S.length()) - len;
if (pos < 0)
pos = 0;
int pos = static_cast<int>(S.length()) - len;
if (pos < 0)
pos = 0;
return S.substr(static_cast<unsigned>(pos));
return S.substr(static_cast<unsigned>(pos));
}
ValueHolder strrep(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 2)
throwInsufficientParameters("strrep", args.size(), 2);
if (args.size() != 2)
throwInsufficientParameters("strrep", args.size(), 2);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("strrep", 1);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("strrep", 1);
if (args[1].getType() != ValueHolder::Type::Integer)
throwNotAWholeNumber("strrep", 2);
if (args[1].getType() != ValueHolder::Type::Integer)
throwNotAWholeNumber("strrep", 2);
const auto& S = ValueExtract(args[0]).toString();
const auto count = ValueExtract(args[1]).toInt();
const auto& S = ValueExtract(args[0]).toString();
const auto count = ValueExtract(args[1]).toInt();
if (count < 0)
throwNumberMustBePositive("strright", 2);
if (count < 0)
throwNumberMustBePositive("strright", 2);
std::string ret;
if (count == 0)
return ret;
for (int i = 0; i < count; ++i)
ret += S;
return ret;
std::string ret;
if (count == 0)
return ret;
for (int i = 0; i < count; ++i)
ret += S;
return ret;
}
ValueHolder resemblesint(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("resemblesint", args.size(), 2);
if (args.size() != 1)
throwInsufficientParameters("resemblesint", args.size(), 2);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("resemblesint", 1);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("resemblesint", 1);
const auto& S = ValueExtract(args[0]).toString();
if (S.empty())
return false;
const auto& S = ValueExtract(args[0]).toString();
if (S.empty())
return false;
std::smatch match;
std::regex_match(S, match, rx_resemblesint);
return match[0].length() != 0;
std::smatch match;
std::regex_match(S, match, rx_resemblesint);
return match[0].length() != 0;
}
ValueHolder resemblesreal(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("resemblesreal", args.size(), 2);
if (args.size() != 1)
throwInsufficientParameters("resemblesreal", args.size(), 2);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("resemblesreal", 1);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("resemblesreal", 1);
const auto& S = ValueExtract(args[0]).toString();
if (S.empty())
return false;
const auto& S = ValueExtract(args[0]).toString();
if (S.empty())
return false;
std::smatch match;
std::regex_match(S, match, rx_resemblesreal);
return match[0].length() != 0;
std::smatch match;
std::regex_match(S, match, rx_resemblesreal);
return match[0].length() != 0;
}
ValueHolder resemblesbool(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("resemblesbool", args.size(), 2);
if (args.size() != 1)
throwInsufficientParameters("resemblesbool", args.size(), 2);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("resemblesbool", 1);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("resemblesbool", 1);
const auto& S = ValueExtract(args[0]).toString();
if (S.empty())
return false;
const auto& S = ValueExtract(args[0]).toString();
if (S.empty())
return false;
std::smatch match;
std::regex_match(S, match, rx_resemblesbool);
return match[0].length() != 0;
std::smatch match;
std::regex_match(S, match, rx_resemblesbool);
return match[0].length() != 0;
}
}

@ -11,196 +11,196 @@
struct DialogWidgetPriv
{
explicit DialogWidgetPriv(const std::string& name_, DialogWidget::Type type_);
std::string name;
DialogWidget::Type type;
std::unordered_map<std::string, ValueHolder> attrs;
std::unordered_map<std::string, Scope> hooks;
explicit DialogWidgetPriv(const std::string& name_, DialogWidget::Type type_);
std::string name;
DialogWidget::Type type;
std::unordered_map<std::string, ValueHolder> attrs;
std::unordered_map<std::string, Scope> hooks;
};
struct DialogPriv
{
explicit DialogPriv(const std::string& name_);
std::string name;
std::unordered_map<std::string, ValueHolder> attrs;
std::unordered_map<std::string, DialogWidget> widgets;
std::vector<std::string> widgetNames; // Need this since we cannot iterate over 'widgets' due to deleted copy ctor.
std::unordered_map<std::string, Scope> hooks;
explicit DialogPriv(const std::string& name_);
std::string name;
std::unordered_map<std::string, ValueHolder> attrs;
std::unordered_map<std::string, DialogWidget> widgets;
std::vector<std::string> widgetNames; // Need this since we cannot iterate over 'widgets' due to deleted copy ctor.
std::unordered_map<std::string, Scope> hooks;
};
DialogPriv::DialogPriv(const std::string& name_)
: name(name_)
: name(name_)
{}
DialogWidgetPriv::DialogWidgetPriv(const std::string& name_, DialogWidget::Type type_)
: name(name_)
, type(type_)
: name(name_)
, type(type_)
{}
std::optional<DialogWidget::Type> DialogWidget::strToType(const std::string& stype)
{
if (stype == "label")
return Type::Label;
else if (stype == "button")
return Type::Button;
else if (stype == "textline")
return Type::Textline;
else if (stype == "textbox")
return Type::Textbox;
else if (stype == "spinbox")
return Type::Spinbox;
else if (stype == "listbox")
return Type::Listbox;
else if (stype == "combobox")
return Type::Combobox;
else if (stype == "table")
return Type::Table;
else if (stype == "tree")
return Type::Tree;
else if (stype == "groupbox")
return Type::Groupbox;
else if (stype == "tab")
return Type::Tab;
else if (stype == "radio")
return Type::Radio;
else if (stype == "checkbox")
return Type::Checkbox;
else if (stype == "vscroll")
return Type::Vscroll;
else if (stype == "hscroll")
return Type::Hscroll;
else if (stype == "canvas")
return Type::Canvas;
else
return std::nullopt;
if (stype == "label")
return Type::Label;
else if (stype == "button")
return Type::Button;
else if (stype == "textline")
return Type::Textline;
else if (stype == "textbox")
return Type::Textbox;
else if (stype == "spinbox")
return Type::Spinbox;
else if (stype == "listbox")
return Type::Listbox;
else if (stype == "combobox")
return Type::Combobox;
else if (stype == "table")
return Type::Table;
else if (stype == "tree")
return Type::Tree;
else if (stype == "groupbox")
return Type::Groupbox;
else if (stype == "tab")
return Type::Tab;
else if (stype == "radio")
return Type::Radio;
else if (stype == "checkbox")
return Type::Checkbox;
else if (stype == "vscroll")
return Type::Vscroll;
else if (stype == "hscroll")
return Type::Hscroll;
else if (stype == "canvas")
return Type::Canvas;
else
return std::nullopt;
}
DialogWidget::DialogWidget(const std::string& name, Type type)
: mp(new DialogWidgetPriv(name, type))
: mp(new DialogWidgetPriv(name, type))
{}
DialogWidget::DialogWidget(DialogWidget&& other)
: mp(std::move(other.mp))
: mp(std::move(other.mp))
{}
DialogWidget::~DialogWidget() = default;
DialogWidget& DialogWidget::operator=(DialogWidget&& other)
{
mp = std::move(other.mp);
return *this;
mp = std::move(other.mp);
return *this;
}
void DialogWidget::setDefaultAttr(const std::string& key, ValueHolder value)
{
mp->attrs.insert_or_assign(key, std::move(value));
mp->attrs.insert_or_assign(key, std::move(value));
}
ValueHolder DialogWidget::defaultAttr(const std::string& key) const
{
if (mp->attrs.find(key) == mp->attrs.end()) {
return ValueHolder();
}
else
return mp->attrs.at(key);
if (mp->attrs.find(key) == mp->attrs.end()) {
return ValueHolder();
}
else
return mp->attrs.at(key);
}
const std::string& DialogWidget::name() const
{
return mp->name;
return mp->name;
}
DialogWidget::Type DialogWidget::type() const
{
return mp->type;
return mp->type;
}
Scope& DialogWidget::addHook(const std::string& hookname, Scope&& scope)
{
mp->hooks.emplace(hookname, std::move(scope));
return mp->hooks.at(hookname);
mp->hooks.emplace(hookname, std::move(scope));
return mp->hooks.at(hookname);
}
std::optional<ScopeRef> DialogWidget::hook(const std::string& hookname) const
{
try {
return mp->hooks.at(hookname);
} catch (const std::out_of_range&) {
return std::nullopt;
}
try {
return mp->hooks.at(hookname);
} catch (const std::out_of_range&) {
return std::nullopt;
}
}
Dialog::~Dialog() = default;
Dialog::Dialog(const std::string& name)
: mp(new DialogPriv(name))
: mp(new DialogPriv(name))
{}
Dialog::Dialog(Dialog&& other)
: mp(std::move(other.mp))
: mp(std::move(other.mp))
{}
Dialog& Dialog::operator=(Dialog&& other)
{
mp = std::move(other.mp);
return *this;
mp = std::move(other.mp);
return *this;
}
void Dialog::setDefaultAttr(const std::string& key, ValueHolder value)
{
mp->attrs.insert_or_assign(key, std::move(value));
mp->attrs.insert_or_assign(key, std::move(value));
}
ValueHolder Dialog::defaultAttr(const std::string& key) const
{
if (mp->attrs.find(key) == mp->attrs.end()) {
return ValueHolder();
}
else
return mp->attrs.at(key);
if (mp->attrs.find(key) == mp->attrs.end()) {
return ValueHolder();
}
else
return mp->attrs.at(key);
}
const std::string& Dialog::name() const
{
return mp->name;
return mp->name;
}
DialogWidget& Dialog::addWidget(const std::string& name, DialogWidget::Type type)
{
if (mp->widgets.find(name) != mp->widgets.end())
throw ScriptException("Dialog widget of name '" + name + "' already exists");
mp->widgets.insert_or_assign(name, DialogWidget(name, type));
mp->widgetNames.emplace_back(name);
return mp->widgets.at(name);
if (mp->widgets.find(name) != mp->widgets.end())
throw ScriptException("Dialog widget of name '" + name + "' already exists");
mp->widgets.insert_or_assign(name, DialogWidget(name, type));
mp->widgetNames.emplace_back(name);
return mp->widgets.at(name);
}
DialogWidget& Dialog::widget(const std::string& name)
{
if (mp->widgets.find(name) == mp->widgets.end())
throw ScriptException("Dialog widget of name '" + name + "' does not exist");
return mp->widgets.at(name);
if (mp->widgets.find(name) == mp->widgets.end())
throw ScriptException("Dialog widget of name '" + name + "' does not exist");
return mp->widgets.at(name);
}
std::vector<DialogWidgetRef> Dialog::allWidgets() const
{
std::vector<DialogWidgetRef> ret;
for (auto widgetName : mp->widgetNames)
ret.emplace_back(mp->widgets.at(widgetName));
return ret;
std::vector<DialogWidgetRef> ret;
for (auto widgetName : mp->widgetNames)
ret.emplace_back(mp->widgets.at(widgetName));
return ret;
}
Scope& Dialog::addHook(const std::string& hookname, Scope&& scope)
{
mp->hooks.emplace(hookname, std::move(scope));
return mp->hooks.at(hookname);
mp->hooks.emplace(hookname, std::move(scope));
return mp->hooks.at(hookname);
}
std::optional<ScopeRef> Dialog::hook(const std::string& hookname) const
{
try {
return mp->hooks.at(hookname);
} catch (const std::out_of_range&) {
return std::nullopt;
}
try {
return mp->hooks.at(hookname);
} catch (const std::out_of_range&) {
return std::nullopt;
}
}

@ -24,45 +24,45 @@ struct DialogWidgetPriv;
class DialogWidget
{
public:
enum class Type {
Label,
Button,
Textline,
Textbox,
Spinbox,
Listbox,
Combobox,
Table,
Tree,
Groupbox,
Tab,
Radio,
Checkbox,
Vscroll,
Hscroll,
Canvas
};
static std::optional<Type> strToType(const std::string& stype);
DialogWidget() = delete;
DialogWidget(const std::string& name, Type type);
DialogWidget(DialogWidget&& other);
DialogWidget(DialogWidget&) = delete;
~DialogWidget();
DialogWidget& operator=(DialogWidget&& other);
DialogWidget& operator=(DialogWidget&) = delete;
void setDefaultAttr(const std::string& key, ValueHolder value);
ValueHolder defaultAttr(const std::string& key) const;
const std::string& name() const;
Type type() const;
Scope& addHook(const std::string& hookname, Scope&& scope);
std::optional<ScopeRef> hook(const std::string& hookname) const;
enum class Type {
Label,
Button,
Textline,
Textbox,
Spinbox,
Listbox,
Combobox,
Table,
Tree,
Groupbox,
Tab,
Radio,
Checkbox,
Vscroll,
Hscroll,
Canvas
};
static std::optional<Type> strToType(const std::string& stype);
DialogWidget() = delete;
DialogWidget(const std::string& name, Type type);
DialogWidget(DialogWidget&& other);
DialogWidget(DialogWidget&) = delete;
~DialogWidget();
DialogWidget& operator=(DialogWidget&& other);
DialogWidget& operator=(DialogWidget&) = delete;
void setDefaultAttr(const std::string& key, ValueHolder value);
ValueHolder defaultAttr(const std::string& key) const;
const std::string& name() const;
Type type() const;
Scope& addHook(const std::string& hookname, Scope&& scope);
std::optional<ScopeRef> hook(const std::string& hookname) const;
private:
std::unique_ptr<DialogWidgetPriv> mp;
std::unique_ptr<DialogWidgetPriv> mp;
};
using DialogWidgetRef = std::reference_wrapper<DialogWidget>;
@ -76,27 +76,27 @@ struct DialogPriv;
class Dialog
{
public:
Dialog() = delete;
explicit Dialog(const std::string& name);
Dialog(Dialog&& other);
Dialog(const Dialog&) = delete;
~Dialog();
Dialog& operator=(Dialog&& other);
Dialog& operator=(Dialog&) = delete;
Dialog() = delete;
explicit Dialog(const std::string& name);
Dialog(Dialog&& other);
Dialog(const Dialog&) = delete;
~Dialog();
Dialog& operator=(Dialog&& other);
Dialog& operator=(Dialog&) = delete;
void setDefaultAttr(const std::string& key, ValueHolder value);
ValueHolder defaultAttr(const std::string& key) const;
const std::string& name() const;
void setDefaultAttr(const std::string& key, ValueHolder value);
ValueHolder defaultAttr(const std::string& key) const;
const std::string& name() const;
DialogWidget& addWidget(const std::string& name, DialogWidget::Type type);
DialogWidget& widget(const std::string& name);
std::vector<DialogWidgetRef> allWidgets() const;
DialogWidget& addWidget(const std::string& name, DialogWidget::Type type);
DialogWidget& widget(const std::string& name);
std::vector<DialogWidgetRef> allWidgets() const;
Scope& addHook(const std::string& hookname, Scope&& scope);
std::optional<ScopeRef> hook(const std::string& hookname) const;
Scope& addHook(const std::string& hookname, Scope&& scope);
std::optional<ScopeRef> hook(const std::string& hookname) const;
private:
std::unique_ptr<DialogPriv> mp;
std::unique_ptr<DialogPriv> mp;
};
#endif // DIALOG_H

@ -22,73 +22,73 @@ std::optional<ScriptManager*> Instance;
}
ScriptManager::ScriptManager(MdiManager* mdiManager, QWidget *parent) :
QDialog(parent),
ui(new Ui::ScriptManager),
mdiManager(mdiManager)
QDialog(parent),
ui(new Ui::ScriptManager),
mdiManager(mdiManager)
{
ui->setupUi(this);
registerScriptFunctions();
ui->listView->setModel(&m_listModel);
ui->setupUi(this);
registerScriptFunctions();
ui->listView->setModel(&m_listModel);
}
void ScriptManager::commandHandler(const std::string& input)
{
auto& connection = (*Instance)->m_context->getConnection();
ICommand cmd(connection);
cmd.parse(input.c_str());
auto& connection = (*Instance)->m_context->getConnection();
ICommand cmd(connection);
cmd.parse(input.c_str());
}
void ScriptManager::event(const std::string& eventName, std::vector<ValueHolder>& params)
{
for (Script& s : (*Instance)->m_scripts) {
if (!s.dispatchEvent(eventName, params))
printError(s.lastError());
}
for (Script& s : (*Instance)->m_scripts) {
if (!s.dispatchEvent(eventName, params))
printError(s.lastError());
}
}
void ScriptManager::event(const std::string& eventName, QList<ValueHolder>& params)
{
std::vector<ValueHolder> convp;
for (auto& param : params)
convp.emplace_back(param); // std::move?
event(eventName, convp);
std::vector<ValueHolder> convp;
for (auto& param : params)
convp.emplace_back(param); // std::move?
event(eventName, convp);
}
void ScriptManager::event(const std::string& eventName)
{
std::vector<ValueHolder> emptyParams;
event(eventName, emptyParams);
std::vector<ValueHolder> emptyParams;
event(eventName, emptyParams);
}
void ScriptManager::init(MdiManager* mdiManager, QWidget* parent)
{
Instance.emplace(new ScriptManager(mdiManager, parent));
Instance.emplace(new ScriptManager(mdiManager, parent));
ScriptManager::setContext(mdiManager->currentStatus());
(*Instance)->loadAllScripts();
}
void ScriptManager::setContext(IWinStatus* context)
{
if (!Instance.has_value()) {
qWarning() << "Script manager was not initialized when tried to set context!";
return;
}
instance()->m_context = context;
if (!Instance.has_value()) {
qWarning() << "Script manager was not initialized when tried to set context!";
return;
}
instance()->m_context = context;
}
ScriptManager* ScriptManager::instance()
{
return *Instance;
return *Instance;
}
IWinStatus* ScriptManager::getContext()
{
return (*Instance)->m_context;
return (*Instance)->m_context;
}
ScriptManager::~ScriptManager()
{
delete ui;
delete ui;
}
void ScriptManager::loadAllScripts()
@ -117,41 +117,41 @@ void ScriptManager::loadAllScripts()
void ScriptManager::contextMenuPopup(ScriptMenuType type, const QPoint& pos, SymbolScope& symbols)
{
QString menuName;
switch (type) {
case ScriptMenuType::Status:
menuName = "status";
break;
case ScriptMenuType::Channel:
menuName = "channel";
break;
case ScriptMenuType::Memberlist:
menuName = "memberlist";
break;
case ScriptMenuType::Private:
menuName = "private";
break;
}
if (!menuName.isEmpty()) {
QMenu* menu = contextMenu(menuName, symbols);
if (menu) menu->popup(pos);
}
QString menuName;
switch (type) {
case ScriptMenuType::Status:
menuName = "status";
break;
case ScriptMenuType::Channel:
menuName = "channel";
break;
case ScriptMenuType::Memberlist:
menuName = "memberlist";
break;
case ScriptMenuType::Private:
menuName = "private";
break;
}
if (!menuName.isEmpty()) {
QMenu* menu = contextMenu(menuName, symbols);
if (menu) menu->popup(pos);
}
}
bool ScriptManager::runCommand(const QString& command)
{
bool ret{ false };
std::string cmd = command.toStdString();
for (Script& script : m_scripts) {
bool r = script.runCommand(cmd);
if (script.hasError()) {
printError(script.lastError());
continue;
}
if (!ret)
ret = r;
}
return ret;
bool ret{ false };
std::string cmd = command.toStdString();
for (Script& script : m_scripts) {
bool r = script.runCommand(cmd);
if (script.hasError()) {
printError(script.lastError());
continue;
}
if (!ret)
ret = r;
}
return ret;
}
bool ScriptManager::loadFromFile(const QString& path)
@ -159,76 +159,76 @@ bool ScriptManager::loadFromFile(const QString& path)
auto fixedPath = path;
MAKE_FIXED_PATH(path, fixedPath);
QFile f(fixedPath);
if (!f.open(QIODevice::ReadOnly)) {
QMessageBox::warning(this, tr("Load script"), tr("Unable to open script file from\n%1").arg(fixedPath));
return false;
}
QByteArray data(f.readAll());
f.close();
return loadFromData(data);
QFile f(fixedPath);
if (!f.open(QIODevice::ReadOnly)) {
QMessageBox::warning(this, tr("Load script"), tr("Unable to open script file from\n%1").arg(fixedPath));
return false;
}
QByteArray data(f.readAll());
f.close();
return loadFromData(data);
}
bool ScriptManager::loadFromData(const QByteArray& data)
{
auto& script = m_scripts.emplace_back(data.toStdString(), &commandHandler,
[](const std::string& eventName, std::vector<ValueHolder>& params)
{ event(eventName, params); });
bool ret = script.reload();
if (!ret)
mdiManager->currentStatus()->print(PrintType::ProgramInfo, script.lastError().c_str());
return ret;
auto& script = m_scripts.emplace_back(data.toStdString(), &commandHandler,
[](const std::string& eventName, std::vector<ValueHolder>& params)
{ event(eventName, params); });
bool ret = script.reload();
if (!ret)
mdiManager->currentStatus()->print(PrintType::ProgramInfo, script.lastError().c_str());
return ret;
}
void ScriptManager::unloadAllScripts()
{
m_scripts.clear();
m_scripts.clear();
}
QMenu* ScriptManager::contextMenu(const QString& name, SymbolScope& symbols)
{
if (m_menu)
m_menu->deleteLater();
m_menu = new QMenu();
bool contents = false;
for (Script& script : m_scripts) {
auto maybeMenu = script.menu(name.toStdString());
if (!maybeMenu) continue;
contents = true;
populateMenu(maybeMenu->get().content(), m_menu, script, symbols);
}
if (!contents) {
m_menu->deleteLater();
m_menu = nullptr;
}
return m_menu;
if (m_menu)
m_menu->deleteLater();
m_menu = new QMenu();
bool contents = false;
for (Script& script : m_scripts) {
auto maybeMenu = script.menu(name.toStdString());
if (!maybeMenu) continue;
contents = true;
populateMenu(maybeMenu->get().content(), m_menu, script, symbols);
}
if (!contents) {
m_menu->deleteLater();
m_menu = nullptr;
}
return m_menu;
}
void ScriptManager::populateMenu(const MenuEntry& submenu, QMenu* menu, Script& script, SymbolScope& symbols)
{
for (const MenuEntry& entry : submenu.entries()) {
if (entry.type() == MenuEntry::Type::Item) {
menu->addAction(entry.label().c_str(), [&script, &entry, &symbols]{
auto ret = script.runScopeWithSymbols(entry.scope(), symbols);
if (!ret)
printError(script.lastError());
});
}
else if (entry.type() == MenuEntry::Type::Submenu) {
QMenu* m = menu->addMenu(entry.label().c_str());
populateMenu(entry, m, script, symbols);
}
else {
menu->addSeparator();
}
}
for (const MenuEntry& entry : submenu.entries()) {
if (entry.type() == MenuEntry::Type::Item) {
menu->addAction(entry.label().c_str(), [&script, &entry, &symbols]{
auto ret = script.runScopeWithSymbols(entry.scope(), symbols);
if (!ret)
printError(script.lastError());
});
}
else if (entry.type() == MenuEntry::Type::Submenu) {
QMenu* m = menu->addMenu(entry.label().c_str());
populateMenu(entry, m, script, symbols);
}
else {
menu->addSeparator();
}
}
}
void ScriptManager::printError(const std::string& msg)
{
(*Instance)->mdiManager->currentStatus()->print(PrintType::ProgramInfo, tr("Script error: %1").arg(msg.c_str()));
(*Instance)->mdiManager->currentStatus()->print(PrintType::ProgramInfo, tr("Script error: %1").arg(msg.c_str()));
}
void ScriptManager::reloadScript(Script& script, const QString& pathToScript)
@ -236,93 +236,93 @@ void ScriptManager::reloadScript(Script& script, const QString& pathToScript)
auto fixedPath = pathToScript;
MAKE_FIXED_PATH(pathToScript, fixedPath);
QFile f(fixedPath);
if (!f.open(QIODevice::ReadOnly)) {
QMessageBox::warning(this, tr("Reload script"), tr("Unable to open script file for reloading\n%1").arg(fixedPath));
m_listModel.markWarning(pathToScript);
return;
}
QByteArray data(f.readAll());
f.close();
if (script.reload(data.toStdString()))
m_listModel.unmarkWarning(pathToScript);
else {
printError(script.lastError());
m_listModel.markWarning(pathToScript);
}
QFile f(fixedPath);
if (!f.open(QIODevice::ReadOnly)) {
QMessageBox::warning(this, tr("Reload script"), tr("Unable to open script file for reloading\n%1").arg(fixedPath));
m_listModel.markWarning(pathToScript);
return;
}
QByteArray data(f.readAll());
f.close();
if (script.reload(data.toStdString()))
m_listModel.unmarkWarning(pathToScript);
else {
printError(script.lastError());
m_listModel.markWarning(pathToScript);
}
}
void ScriptManager::on_btnLoad_clicked()
{
QString path = QFileDialog::getOpenFileName(this, tr("Open script..."), LOCAL_PATH, tr("All files (*)"));
if (path.isEmpty())
return;
QModelIndex existingIdx = m_listModel.pathIndex(path);
if (existingIdx.isValid()) {
auto btn = QMessageBox::warning(this, tr("Load script"), tr("The script located at:\n%1\nIs already loaded. Reload script instead?").arg(path),
QMessageBox::Yes | QMessageBox::No);
if (btn == QMessageBox::Yes) {
auto it = m_scripts.begin();
for (int i = 0; i < existingIdx.row(); ++i)
++it;
reloadScript(*it, path);
}
return;
}
bool ok = loadFromFile(path);
if (!ok) {
auto btn = QMessageBox::warning(this, tr("Load script"), tr("The script located at:\n%1\nFailed to load. Keep the entry in the manager?").arg(path),
QMessageBox::Yes | QMessageBox::No);
if (btn == QMessageBox::No) {
m_scripts.pop_back();
return;
}
}
m_listModel.addPath(path);
ConfigMgr::instance().addScript(path);
if (!ok)
m_listModel.markWarning(path);
QString path = QFileDialog::getOpenFileName(this, tr("Open script..."), LOCAL_PATH, tr("All files (*)"));
if (path.isEmpty())
return;
QModelIndex existingIdx = m_listModel.pathIndex(path);
if (existingIdx.isValid()) {
auto btn = QMessageBox::warning(this, tr("Load script"), tr("The script located at:\n%1\nIs already loaded. Reload script instead?").arg(path),
QMessageBox::Yes | QMessageBox::No);
if (btn == QMessageBox::Yes) {
auto it = m_scripts.begin();
for (int i = 0; i < existingIdx.row(); ++i)
++it;
reloadScript(*it, path);
}
return;
}
bool ok = loadFromFile(path);
if (!ok) {
auto btn = QMessageBox::warning(this, tr("Load script"), tr("The script located at:\n%1\nFailed to load. Keep the entry in the manager?").arg(path),
QMessageBox::Yes | QMessageBox::No);
if (btn == QMessageBox::No) {
m_scripts.pop_back();
return;
}
}
m_listModel.addPath(path);
ConfigMgr::instance().addScript(path);
if (!ok)
m_listModel.markWarning(path);
}
void ScriptManager::on_btnReload_clicked()
{
auto* sel = ui->listView->selectionModel();
auto modelidx = sel->currentIndex();
if (!modelidx.isValid())
return;
auto* sel = ui->listView->selectionModel();
auto modelidx = sel->currentIndex();
if (!modelidx.isValid())
return;
auto it = m_scripts.begin();
for (int i = 0; i < modelidx.row(); ++i)
++it;
auto it = m_scripts.begin();
for (int i = 0; i < modelidx.row(); ++i)
++it;
QString path = modelidx.data().toString();
reloadScript(*it, path);
QString path = modelidx.data().toString();
reloadScript(*it, path);
}
void ScriptManager::on_btnRemove_clicked()
{
auto* sel = ui->listView->selectionModel();
auto modelidx = sel->currentIndex();
if (!modelidx.isValid())
return;
{
const auto button = QMessageBox::question(this, tr("Unload script"), tr("Are you sure you want to unload the selected script?"));
if (button == QMessageBox::No)
return;
}
auto it = m_scripts.begin();
for (int i = 0; i < modelidx.row(); ++i)
++it;
QVariant path = m_listModel.data(modelidx);
ConfigMgr::instance().delScript(path.toString());
m_listModel.delPath(path.toString());
m_scripts.erase(it);
auto* sel = ui->listView->selectionModel();
auto modelidx = sel->currentIndex();
if (!modelidx.isValid())
return;
{
const auto button = QMessageBox::question(this, tr("Unload script"), tr("Are you sure you want to unload the selected script?"));
if (button == QMessageBox::No)
return;
}
auto it = m_scripts.begin();
for (int i = 0; i < modelidx.row(); ++i)
++it;
QVariant path = m_listModel.data(modelidx);
ConfigMgr::instance().delScript(path.toString());
m_listModel.delPath(path.toString());
m_scripts.erase(it);
}

@ -18,12 +18,12 @@
using ScriptArray = std::unordered_map<std::string, ValueHolder>;
enum class ScriptMenuType {
Status,
Channel,
Memberlist,
Private,
Menubar,
Custom
Status,
Channel,
Memberlist,
Private,
Menubar,
Custom
};
namespace Ui {
@ -33,44 +33,44 @@ class ScriptManager;
class MdiManager;
class ScriptManager : public QDialog
{
Q_OBJECT
Q_OBJECT
public:
static void init(MdiManager* mdiManager, QWidget* parent);
static void setContext(IWinStatus* context);
static ScriptManager* instance();
static IWinStatus* getContext();
~ScriptManager();
void loadAllScripts();
void contextMenuPopup(ScriptMenuType type, const QPoint& pos, SymbolScope& symbols);
bool runCommand(const QString& command);
bool loadFromFile(const QString& path);
bool loadFromData(const QByteArray& data);
void unloadAllScripts();
static void event(const std::string& eventName, std::vector<ValueHolder>& params);
static void event(const std::string& eventName, QList<ValueHolder>& params);
static void event(const std::string& eventName);
static void printError(const std::string& msg);
static void init(MdiManager* mdiManager, QWidget* parent);
static void setContext(IWinStatus* context);
static ScriptManager* instance();
static IWinStatus* getContext();
~ScriptManager();
void loadAllScripts();
void contextMenuPopup(ScriptMenuType type, const QPoint& pos, SymbolScope& symbols);
bool runCommand(const QString& command);
bool loadFromFile(const QString& path);
bool loadFromData(const QByteArray& data);
void unloadAllScripts();
static void event(const std::string& eventName, std::vector<ValueHolder>& params);
static void event(const std::string& eventName, QList<ValueHolder>& params);
static void event(const std::string& eventName);
static void printError(const std::string& msg);
private slots:
void on_btnLoad_clicked();
void on_btnReload_clicked();
void on_btnRemove_clicked();
void on_btnLoad_clicked();
void on_btnReload_clicked();
void on_btnRemove_clicked();
private:
explicit ScriptManager(MdiManager* mdiManager, QWidget *parent = nullptr);
static void commandHandler(const std::string& input);
QMenu* contextMenu(const QString& name, SymbolScope& symbols);
void populateMenu(const MenuEntry& submenu, QMenu* menu, Script& script, SymbolScope& symbols);
void reloadScript(Script& script, const QString& pathToScript);
Ui::ScriptManager *ui;
MdiManager* mdiManager;
std::list<Script> m_scripts;
QMenu* m_menu{ nullptr };
IWinStatus* m_context;
ManagerListModel m_listModel;
explicit ScriptManager(MdiManager* mdiManager, QWidget *parent = nullptr);
static void commandHandler(const std::string& input);
QMenu* contextMenu(const QString& name, SymbolScope& symbols);
void populateMenu(const MenuEntry& submenu, QMenu* menu, Script& script, SymbolScope& symbols);
void reloadScript(Script& script, const QString& pathToScript);
Ui::ScriptManager *ui;
MdiManager* mdiManager;
std::list<Script> m_scripts;
QMenu* m_menu{ nullptr };
IWinStatus* m_context;
ManagerListModel m_listModel;
};
namespace {
@ -92,42 +92,42 @@ std::string convVal(const QString& v) { return v.toStdString(); }
[[maybe_unused]]
ValueArray convVal(const ValueArray& v)
{
ValueArray copy;
for (const auto& [key,val] : v)
copy.emplace(key, new ValueHolder(*val.get()));
return copy;
ValueArray copy;
for (const auto& [key,val] : v)
copy.emplace(key, new ValueHolder(*val.get()));
return copy;
}
[[maybe_unused]]
ValueArray convVal(const ScriptArray& v)
{
ValueArray copy;
for (const auto& [key,val] : v)
copy.emplace(key, new ValueHolder(val));
return copy;
ValueArray copy;
for (const auto& [key,val] : v)
copy.emplace(key, new ValueHolder(val));
return copy;
}
}
template<typename ... Ts>
void scriptEvent(const std::string& eventName, const Ts& ... args)
{
if constexpr(sizeof...(args) > 0) {
std::vector<ValueHolder> vals{ ValueHolder(convVal(args))... };
ScriptManager::event(eventName, vals);
}
else {
std::vector<ValueHolder> vals;
ScriptManager::event(eventName, vals);
}
if constexpr(sizeof...(args) > 0) {
std::vector<ValueHolder> vals{ ValueHolder(convVal(args))... };
ScriptManager::event(eventName, vals);
}
else {
std::vector<ValueHolder> vals;
ScriptManager::event(eventName, vals);
}
}
template<typename ... Ts>
void contextualScriptEvent(IWinStatus* context, const std::string& eventName, const Ts& ... args)
{
auto* prevContext = ScriptManager::getContext();
ScriptManager::setContext(context);
scriptEvent(eventName, args...);
ScriptManager::setContext(prevContext);
auto* prevContext = ScriptManager::getContext();
ScriptManager::setContext(context);
scriptEvent(eventName, args...);
ScriptManager::setContext(prevContext);
}
#endif // MANAGER_H

@ -11,82 +11,82 @@
void ManagerListModel::addPath(const QString& path)
{
if (m_list.contains(path))
return;
if (m_list.contains(path))
return;
int idx = m_list.size();
int idx = m_list.size();
beginInsertRows(createIndex(idx, 0, this), idx, idx);
m_list.push_back(path);
endInsertRows();
beginInsertRows(createIndex(idx, 0, this), idx, idx);
m_list.push_back(path);
endInsertRows();
}
void ManagerListModel::delPath(const QString& path)
{
if (!m_list.contains(path))
return;
if (!m_list.contains(path))
return;
int idx = findEntry(path);
if (idx < 0)
return;
int idx = findEntry(path);
if (idx < 0)
return;
beginRemoveRows(createIndex(idx, 0, this), idx, idx);
m_warnings.removeAll(idx);
m_list.removeAll(path);
endRemoveRows();
beginRemoveRows(createIndex(idx, 0, this), idx, idx);
m_warnings.removeAll(idx);
m_list.removeAll(path);
endRemoveRows();
}
QModelIndex ManagerListModel::pathIndex(const QString& path)
{
int row = findEntry(path);
if (row < 0)
return QModelIndex();
else
return createIndex(row, 0, this);
int row = findEntry(path);
if (row < 0)
return QModelIndex();
else
return createIndex(row, 0, this);
}
void ManagerListModel::markWarning(const QString& entry)
{
int idx = findEntry(entry);
if (idx < 0) return;
int idx = findEntry(entry);
if (idx < 0) return;
beginInsertRows(QModelIndex(), idx, idx);
if (!isWarning(idx))
m_warnings.push_back(idx);
endInsertRows();
beginInsertRows(QModelIndex(), idx, idx);
if (!isWarning(idx))
m_warnings.push_back(idx);
endInsertRows();
}
void ManagerListModel::unmarkWarning(const QString& entry)
{
int idx = findEntry(entry);
if (idx < 0) return;
int idx = findEntry(entry);
if (idx < 0) return;
beginInsertRows(QModelIndex(), idx, idx);
m_warnings.removeAll(idx);
endInsertRows();
beginInsertRows(QModelIndex(), idx, idx);
m_warnings.removeAll(idx);
endInsertRows();
}
QVariant ManagerListModel::data(const QModelIndex& index, int role) const
{
if (index.row() < 0 || index.row() >= m_list.size())
return QVariant();
if (role == Qt::DisplayRole) {
return m_list[index.row()];
}
else if (role == Qt::SizeHintRole) {
return QSize(0, 30);
}
else if (role == Qt::DecorationRole && isWarning(index.row())) {
return QIcon(":/Icons/exclamation.png");
}
return QVariant();
if (index.row() < 0 || index.row() >= m_list.size())
return QVariant();
if (role == Qt::DisplayRole) {
return m_list[index.row()];
}
else if (role == Qt::SizeHintRole) {
return QSize(0, 30);
}
else if (role == Qt::DecorationRole && isWarning(index.row())) {
return QIcon(":/Icons/exclamation.png");
}
return QVariant();
}
int ManagerListModel::rowCount(const QModelIndex&) const
{
return m_list.size();
return m_list.size();
}
int ManagerListModel::findEntry(const QString& entry) const
@ -94,26 +94,26 @@ int ManagerListModel::findEntry(const QString& entry) const
QString entryFixed = entry;
MAKE_FIXED_PATH(entry, entryFixed);
bool found = false;
int idx = 0;
for (const QString& item : m_list) {
QString itemFixed = item;
bool found = false;
int idx = 0;
for (const QString& item : m_list) {
QString itemFixed = item;
MAKE_FIXED_PATH(item, itemFixed);
if (itemFixed == entryFixed) {
found = true;
break;
}
++idx;
}
if (!found)
return -1;
else
return idx;
if (itemFixed == entryFixed) {
found = true;
break;
}
++idx;
}
if (!found)
return -1;
else
return idx;
}
bool ManagerListModel::isWarning(int idx) const
{
return m_warnings.contains(idx);
return m_warnings.contains(idx);
}

@ -26,24 +26,24 @@
class ManagerListModel : public QAbstractListModel
{
Q_OBJECT
Q_OBJECT
public:
void addPath(const QString& path);
void delPath(const QString& path);
QModelIndex pathIndex(const QString& path);
void addPath(const QString& path);
void delPath(const QString& path);
QModelIndex pathIndex(const QString& path);
void markWarning(const QString& entry);
void unmarkWarning(const QString& entry);
void markWarning(const QString& entry);
void unmarkWarning(const QString& entry);
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
private:
int findEntry(const QString& entry) const;
bool isWarning(int idx) const;
QStringList m_list;
QVector<int> m_warnings; // entries contain indexes of m_list that is marked with warning.
int findEntry(const QString& entry) const;
bool isWarning(int idx) const;
QStringList m_list;
QVector<int> m_warnings; // entries contain indexes of m_list that is marked with warning.
};
#endif // MANAGERLISTMODEL_H

@ -10,160 +10,160 @@
struct MenuPriv
{
explicit MenuPriv(const std::string& name_);
std::string name;
MenuEntry content;
explicit MenuPriv(const std::string& name_);
std::string name;
MenuEntry content;
};
struct MenuEntryPriv
{
MenuEntryPriv(const std::string& label_, MenuEntry::Type type_)
: label(label_)
, type(type_)
{}
MenuEntryPriv(const std::string& label_, MenuEntry::Type type_)
: label(label_)
, type(type_)
{}
virtual ~MenuEntryPriv() = default;
const std::string label;
const MenuEntry::Type type;
virtual ~MenuEntryPriv() = default;
const std::string label;
const MenuEntry::Type type;
};
struct SubmenuEntryPriv : MenuEntryPriv
{
explicit SubmenuEntryPriv(const std::string& label_)
: MenuEntryPriv(label_, MenuEntry::Type::Submenu)
{}
explicit SubmenuEntryPriv(const std::string& label_)
: MenuEntryPriv(label_, MenuEntry::Type::Submenu)
{}
~SubmenuEntryPriv() override = default;
~SubmenuEntryPriv() override = default;
std::vector<MenuEntry> entries;
std::vector<MenuEntry> entries;
};
struct DivideEntryPriv : MenuEntryPriv
{
DivideEntryPriv()
: MenuEntryPriv("", MenuEntry::Type::Divide)
{}
DivideEntryPriv()
: MenuEntryPriv("", MenuEntry::Type::Divide)
{}
~DivideEntryPriv() = default;
~DivideEntryPriv() = default;
};
struct ItemEntryPriv : MenuEntryPriv
{
ItemEntryPriv(const std::string& label_, Scope&& scope)
: MenuEntryPriv(label_, MenuEntry::Type::Item)
, scope(std::move(scope))
{}
ItemEntryPriv(const std::string& label_, Scope&& scope)
: MenuEntryPriv(label_, MenuEntry::Type::Item)
, scope(std::move(scope))
{}
~ItemEntryPriv() = default;
~ItemEntryPriv() = default;
Scope scope;
Scope scope;
};
Menu::~Menu() = default;
MenuPriv::MenuPriv(const std::string& name_)
: name(name_)
, content(name_)
: name(name_)
, content(name_)
{}
Menu::Menu(const std::string& name)
: mp(new MenuPriv(name))
: mp(new MenuPriv(name))
{}
Menu::Menu(Menu&& other)
: mp(std::move(other.mp))
: mp(std::move(other.mp))
{}
Menu& Menu::operator=(Menu&& other)
{
mp = std::move(other.mp);
return *this;
mp = std::move(other.mp);
return *this;
}
const MenuEntry& Menu::content() const
{
return mp->content;
return mp->content;
}
MenuEntry& Menu::content()
{
return mp->content;
return mp->content;
}
const std::string Menu::name() const
{
return mp->name;
return mp->name;
}
MenuEntry::MenuEntry()
: mp(new DivideEntryPriv())
: mp(new DivideEntryPriv())
{}
MenuEntry::MenuEntry(const std::string& label)
: mp(new SubmenuEntryPriv(label))
: mp(new SubmenuEntryPriv(label))
{}
MenuEntry::MenuEntry(const std::string& label, Scope&& scope)
: mp(new ItemEntryPriv(label, std::move(scope)))
: mp(new ItemEntryPriv(label, std::move(scope)))
{}
MenuEntry::MenuEntry(MenuEntry&& other)
: mp(std::move(other.mp))
: mp(std::move(other.mp))
{}
MenuEntry::~MenuEntry() = default;
const Scope& MenuEntry::scope() const
{
auto* imp = dynamic_cast<ItemEntryPriv*>(mp.get());
return imp->scope;
auto* imp = dynamic_cast<ItemEntryPriv*>(mp.get());
return imp->scope;
}
const std::vector<MenuEntry>& MenuEntry::entries() const
{
auto* imp = dynamic_cast<SubmenuEntryPriv*>(mp.get());
return imp->entries;
auto* imp = dynamic_cast<SubmenuEntryPriv*>(mp.get());
return imp->entries;
}
const std::string& MenuEntry::label() const
{
return mp->label;
return mp->label;
}
MenuEntry& MenuEntry::addSubMenu(const std::string& label)
{
auto* imp = dynamic_cast<SubmenuEntryPriv*>(mp.get());
imp->entries.emplace_back(label);
return imp->entries.back();
auto* imp = dynamic_cast<SubmenuEntryPriv*>(mp.get());
imp->entries.emplace_back(label);
return imp->entries.back();
}
MenuEntry& MenuEntry::addItem(const std::string& label, Scope&& scope)
{
auto* imp = dynamic_cast<SubmenuEntryPriv*>(mp.get());
imp->entries.emplace_back(label, std::move(scope));
return imp->entries.back();
auto* imp = dynamic_cast<SubmenuEntryPriv*>(mp.get());
imp->entries.emplace_back(label, std::move(scope));
return imp->entries.back();
}
MenuEntry& MenuEntry::addDivide()
{
auto* imp = dynamic_cast<SubmenuEntryPriv*>(mp.get());
imp->entries.emplace_back();
return imp->entries.back();
auto* imp = dynamic_cast<SubmenuEntryPriv*>(mp.get());
imp->entries.emplace_back();
return imp->entries.back();
}
MenuEntry::Type MenuEntry::type() const
{
return mp->type;
return mp->type;
}
Scope& MenuEntry::scope()
{
auto* imp = dynamic_cast<ItemEntryPriv*>(mp.get());
return imp->scope;
auto* imp = dynamic_cast<ItemEntryPriv*>(mp.get());
return imp->scope;
}
std::vector<MenuEntry>& MenuEntry::entries()
{
auto* imp = dynamic_cast<SubmenuEntryPriv*>(mp.get());
return imp->entries;
auto* imp = dynamic_cast<SubmenuEntryPriv*>(mp.get());
return imp->entries;
}

@ -21,21 +21,21 @@ struct MenuPriv;
class Menu
{
public:
Menu() = delete;
explicit Menu(const std::string& name);
Menu(Menu&& other);
Menu(const Menu&) = delete;
~Menu();
Menu& operator=(Menu&& other);
Menu& operator=(Menu&) = delete;
Menu() = delete;
explicit Menu(const std::string& name);
Menu(Menu&& other);
Menu(const Menu&) = delete;
~Menu();
Menu& operator=(Menu&& other);
Menu& operator=(Menu&) = delete;
const MenuEntry& content() const;
MenuEntry& content();
const MenuEntry& content() const;
MenuEntry& content();
const std::string name() const;
const std::string name() const;
private:
std::unique_ptr<MenuPriv> mp;
std::unique_ptr<MenuPriv> mp;
};
using ConstMenuRef = std::reference_wrapper<const Menu>;
@ -43,36 +43,36 @@ using ConstMenuRef = std::reference_wrapper<const Menu>;
struct MenuEntryPriv;
class MenuEntry
{
friend class Menu;
friend struct TokenizerPriv;
friend class Menu;
friend struct TokenizerPriv;
public:
MenuEntry();
MenuEntry(const std::string& label);
MenuEntry(const std::string& label, Scope&& scope);
MenuEntry(MenuEntry&& other);
MenuEntry(const MenuEntry&) = delete;
MenuEntry& operator=(MenuEntry&) = delete;
~MenuEntry();
MenuEntry();
MenuEntry(const std::string& label);
MenuEntry(const std::string& label, Scope&& scope);
MenuEntry(MenuEntry&& other);
MenuEntry(const MenuEntry&) = delete;
MenuEntry& operator=(MenuEntry&) = delete;
~MenuEntry();
enum class Type {
Item,
Divide,
Submenu
};
enum class Type {
Item,
Divide,
Submenu
};
const Scope& scope() const;
const std::vector<MenuEntry>& entries() const;
const std::string& label() const;
const Scope& scope() const;
const std::vector<MenuEntry>& entries() const;
const std::string& label() const;
MenuEntry& addSubMenu(const std::string& label);
MenuEntry& addItem(const std::string& label, Scope&& scope);
MenuEntry& addDivide();
Type type() const;
MenuEntry& addSubMenu(const std::string& label);
MenuEntry& addItem(const std::string& label, Scope&& scope);
MenuEntry& addDivide();
Type type() const;
private:
Scope& scope(); // TokenizerPriv uses this
std::vector<MenuEntry>& entries(); // Menu uses this
std::unique_ptr<MenuEntryPriv> mp;
Scope& scope(); // TokenizerPriv uses this
std::vector<MenuEntry>& entries(); // Menu uses this
std::unique_ptr<MenuEntryPriv> mp;
};
#endif // MENU_H

@ -10,24 +10,24 @@
enum class ParserOperator
{
Invalid,
Invalid,
Assign, // =
Lt, // <
Gt, // >
Lte, // <=
Gte, // >=
Eq, // ==
Ne, // !=
Assign, // =
Lt, // <
Gt, // >
Lte, // <=
Gte, // >=
Eq, // ==
Ne, // !=
Plus, // +
Minus, // -
Multi, // *
Div, // /
Mod, // %
Plus, // +
Minus, // -
Multi, // *
Div, // /
Mod, // %
LAnd, // &&
LOr // ||
LAnd, // &&
LOr // ||
};
#endif // PARSERTOKENOPERATORS_H

@ -11,136 +11,136 @@
namespace {
using Op = ParserOperator;
std::unordered_map<ParserOperator, int> PrecedenceMap {
{ Op::Eq, 1 },
{ Op::Ne, 1 },
{ Op::Eq, 1 },
{ Op::Ne, 1 },
{ Op::Lt, 2 },
{ Op::Gt, 2 },
{ Op::Lte, 2 },
{ Op::Gte, 2 },
{ Op::Lt, 2 },
{ Op::Gt, 2 },
{ Op::Lte, 2 },
{ Op::Gte, 2 },
{ Op::Multi, 3 },
{ Op::Div, 3 },
{ Op::Mod, 3 },
{ Op::Multi, 3 },
{ Op::Div, 3 },
{ Op::Mod, 3 },
{ Op::Plus, 4 },
{ Op::Minus, 4 },
{ Op::Plus, 4 },
{ Op::Minus, 4 },
{ Op::LAnd, 5 },
{ Op::LOr, 6 },
{ Op::LAnd, 5 },
{ Op::LOr, 6 },
{ Op::Assign, 7 }
{ Op::Assign, 7 }
};
}
ParserToken::ParserToken(ParserToken&& other)
: m_order(other.m_order)
, m_tk(std::move(other.m_tk))
: m_order(other.m_order)
, m_tk(std::move(other.m_tk))
, m_tokenbase(other.m_tokenbase)
{}
ParserToken::ParserToken(const ParserToken& other)
: m_order(other.m_order)
, m_tk(other.m_tk)
: m_order(other.m_order)
, m_tk(other.m_tk)
, m_tokenbase(other.m_tokenbase)
{}
ParserToken::ParserToken(const TokenBase& tokenbase, ParserToken::Operator op)
: m_tk(op)
: m_tk(op)
, m_tokenbase(tokenbase)
{}
ParserToken::ParserToken(const TokenBase& tokenbase, const ValueHolder& val)
: m_tk(val)
: m_tk(val)
, m_tokenbase(tokenbase)
{}
ParserToken& ParserToken::operator=(const ParserToken& other)
{
m_tk = other.m_tk;
m_order = other.m_order;
return *this;
m_tk = other.m_tk;
m_order = other.m_order;
return *this;
}
ParserToken::Type ParserToken::type() const
{
switch (m_tk.index()) {
case 0: return Type::Value;
default: return Type::Operator;
}
switch (m_tk.index()) {
case 0: return Type::Value;
default: return Type::Operator;
}
}
ValueHolder& ParserToken::value()
{
return std::get<ValueHolder>(m_tk);
return std::get<ValueHolder>(m_tk);
}
const ValueHolder& ParserToken::value() const
{
return std::get<ValueHolder>(m_tk);
return std::get<ValueHolder>(m_tk);
}
ParserToken::Operator ParserToken::oper() const
{
return std::get<Operator>(m_tk);
return std::get<Operator>(m_tk);
}
ParserToken::Operator ParserToken::strToOperator(const std::string& oper)
{
if (oper == "=")
return Operator::Assign;
if (oper == "=")
return Operator::Assign;
else if (oper == "<")
return Operator::Lt;
else if (oper == "<")
return Operator::Lt;
else if (oper == ">")
return Operator::Gt;
else if (oper == ">")
return Operator::Gt;
else if (oper == "<=")
return Operator::Lte;
else if (oper == "<=")
return Operator::Lte;
else if (oper == ">=")
return Operator::Gte;
else if (oper == ">=")
return Operator::Gte;
else if (oper == "==")
return Operator::Eq;
else if (oper == "==")
return Operator::Eq;
else if (oper == "!=")
return Operator::Ne;
else if (oper == "!=")
return Operator::Ne;
else if (oper == "+")
return Operator::Plus;
else if (oper == "+")
return Operator::Plus;
else if (oper == "-")
return Operator::Minus;
else if (oper == "-")
return Operator::Minus;
else if (oper == "*")
return Operator::Multi;
else if (oper == "*")
return Operator::Multi;
else if (oper == "/")
return Operator::Div;
else if (oper == "/")
return Operator::Div;
else if (oper == "%")
return Operator::Mod;
else if (oper == "%")
return Operator::Mod;
else if (oper == "&&")
return Operator::LAnd;
else if (oper == "&&")
return Operator::LAnd;
else if (oper == "||")
return Operator::LOr;
else if (oper == "||")
return Operator::LOr;
else
return Operator::Invalid;
else
return Operator::Invalid;
}
int ParserToken::operPrecedence(Operator oper)
{
return PrecedenceMap.at(oper);
return PrecedenceMap.at(oper);
}
void ParserToken::setOrder(int order)
{
m_order = order;
m_order = order;
}
int ParserToken::order() const

@ -15,50 +15,50 @@ class TokenBase;
class ParserToken
{
using Operator = ParserOperator;
using Operator = ParserOperator;
public:
enum class Type
{
Value,
Operator
};
enum class Type
{
Value,
Operator
};
ParserToken() = delete;
ParserToken() = delete;
ParserToken(ParserToken&& other);
ParserToken(ParserToken&& other);
ParserToken(const ParserToken& other);
ParserToken(const ParserToken& other);
ParserToken(const TokenBase& tokenbase, Operator op);
ParserToken(const TokenBase& tokenbase, const ValueHolder& val);
ParserToken& operator=(const ParserToken& other);
ParserToken& operator=(const ParserToken& other);
~ParserToken() = default;
~ParserToken() = default;
Type type() const;
Type type() const;
ValueHolder& value();
ValueHolder& value();
const ValueHolder& value() const;
const ValueHolder& value() const;
Operator oper() const;
Operator oper() const;
static Operator strToOperator(const std::string& oper);
static Operator strToOperator(const std::string& oper);
static int operPrecedence(Operator oper);
static int operPrecedence(Operator oper);
void setOrder(int order);
void setOrder(int order);
int order() const;
int order() const;
const TokenBase& tokenBase() const;
private:
int m_order{ 0 };
std::variant<ValueHolder, Operator> m_tk;
int m_order{ 0 };
std::variant<ValueHolder, Operator> m_tk;
const TokenBase& m_tokenbase;
};

File diff suppressed because it is too large Load Diff

@ -27,66 +27,66 @@ using DispatchHandler = std::function<void(const std::string&, std::vector<Value
class Script
{
public:
Script(const std::string& input, CommandHandler commandHandler_, DispatchHandler dispatchHandler_);
Script(Script&& other);
Script(const Script&) = delete;
~Script();
Script(const std::string& input, CommandHandler commandHandler_, DispatchHandler dispatchHandler_);
Script(Script&& other);
Script(const Script&) = delete;
~Script();
Script& operator=(Script&) = delete;
Script& operator=(Script&) = delete;
bool reload(const std::string& newInput);
bool reload(const std::string& newInput);
bool reload();
std::optional<ValueHolder> runFunction(const std::string& identifier, std::vector<ValueHolder>& paramValues);
bool runCommand(const std::string& input);
bool dispatchEvent(const std::string& event, std::vector<ValueHolder>& paramValues);
std::optional<ValueHolder> runScopeWithSymbols(const Scope& scope, SymbolScope& symbols);
bool runCommand(const std::string& input);
bool dispatchEvent(const std::string& event, std::vector<ValueHolder>& paramValues);
std::optional<ValueHolder> runScopeWithSymbols(const Scope& scope, SymbolScope& symbols);
std::string lastError() const;
bool hasError() const;
bool hasError() const;
const std::optional<ConstMenuRef> menu(const std::string& name) const;
const std::optional<ConstMenuRef> menu(const std::string& name) const;
static void registerUniversalValue(const std::string& name, ValueHolder&& value);
static void registerUniversalFunction(const std::string& name, const BuiltinFn& fnptr);
static void registerUniversalValue(const std::string& name, ValueHolder&& value);
static void registerUniversalFunction(const std::string& name, const BuiltinFn& fnptr);
int dialogConstruct(const std::string& name);
ValueHolder dialogIsValidHandle(int handle);
void dialogClose(int handle);
void dialogHide(int handle);
void dialogShow(int handle);
ValueArray dialogList(const std::string& name);
void dialogSetAttr(int handle, const std::string& attribute, ValueHolder& value);
ValueHolder dialogGetAttr(int handle, const std::string& attribute);
void dialogSetWidgetAttr(int handle, const std::string& widgetName, const std::string& attribute, ValueHolder& value);
ValueHolder dialogGetWidgetAttr(int handle, const std::string& widgetName, const std::string& attribute);
int dialogConstruct(const std::string& name);
ValueHolder dialogIsValidHandle(int handle);
void dialogClose(int handle);
void dialogHide(int handle);
void dialogShow(int handle);
ValueArray dialogList(const std::string& name);
void dialogSetAttr(int handle, const std::string& attribute, ValueHolder& value);
ValueHolder dialogGetAttr(int handle, const std::string& attribute);
void dialogSetWidgetAttr(int handle, const std::string& widgetName, const std::string& attribute, ValueHolder& value);
ValueHolder dialogGetWidgetAttr(int handle, const std::string& widgetName, const std::string& attribute);
int dialogTableWidgetCount(int handle, const std::string& widgetName);
void dialogTableWidgetInsert(int handle, const std::string& widgetName, const ValueArray& cols);
void dialogTableWidgetRemove(int handle, const std::string& widgetName, int index);
int dialogTableWidgetSelected(int handle, const std::string& widgetName);
ValueArray dialogTableWidgetRow(int handle, const std::string& widgetName, int index);
int dialogTableWidgetCount(int handle, const std::string& widgetName);
void dialogTableWidgetInsert(int handle, const std::string& widgetName, const ValueArray& cols);
void dialogTableWidgetRemove(int handle, const std::string& widgetName, int index);
int dialogTableWidgetSelected(int handle, const std::string& widgetName);
ValueArray dialogTableWidgetRow(int handle, const std::string& widgetName, int index);
private:
static SymbolScope m_universal;
static SymbolScope m_universal;
std::optional<SymbolScope> m_glob;
std::optional<SymbolScope> m_cmds;
std::optional<SymbolScope> m_hooks;
std::unordered_map<int,ScriptDialog> m_dlgs;
int m_dlgHndlCount{ 0 };
std::optional<SymbolScope> m_cmds;
std::optional<SymbolScope> m_hooks;
std::unordered_map<int,ScriptDialog> m_dlgs;
int m_dlgHndlCount{ 0 };
std::optional<Tokenizer> m_tokenizer;
std::string m_data;
CommandHandler commandHandler;
DispatchHandler dispatchHandler;
CommandHandler commandHandler;
DispatchHandler dispatchHandler;
std::variant<std::optional<ValueHolder>, TokenBaseRef> runScope(const Scope &scope, SymbolScope& parentSymbols);
ValueHolder resolveParamsRunScope(const Scope &scope, std::vector<ValueHolder>& paramValues, SymbolScope& symbols);
std::variant<std::optional<ValueHolder>, TokenBaseRef> runScope(const Scope &scope, SymbolScope& parentSymbols);
ValueHolder resolveParamsRunScope(const Scope &scope, std::vector<ValueHolder>& paramValues, SymbolScope& symbols);
ValueHolder resolveExpression(const Expression& expr, SymbolScope& symbols);
std::vector<ValueHolder> resolveParameters(const Scope& parameters, SymbolScope& symbols);
std::string resolveCommandLine(const std::string& input, SymbolScope& symbols);
std::string resolveCommandLine(const std::string& input, SymbolScope& symbols);
void resetError();
void resetError();
std::string m_lastError;
std::pair<int,int> m_lastErrorLinecol;
};

@ -10,131 +10,131 @@
#include <iostream>
Symbol::Symbol(const Scope& scope)
: m_data(scope)
, m_type(Type::Scope)
: m_data(scope)
, m_type(Type::Scope)
{}
Symbol::Symbol(const ValueHolder& data)
: m_data(data)
, m_type(Type::Value)
: m_data(data)
, m_type(Type::Value)
{}
Symbol::Symbol(const BuiltinFn& fn)
: m_data(fn)
, m_type(Type::Builtin)
: m_data(fn)
, m_type(Type::Builtin)
{}
Symbol::Type Symbol::getType() const
{
return m_type;
return m_type;
}
ValueHolder& Symbol::getValue()
{
return std::get<ValueHolder>(m_data);
return std::get<ValueHolder>(m_data);
}
const ValueHolder& Symbol::getValue() const
{
return std::get<ValueHolder>(m_data);
return std::get<ValueHolder>(m_data);
}
const Scope& Symbol::getScope() const
{
return std::get<ScopeCRef>(m_data);
return std::get<ScopeCRef>(m_data);
}
ValueHolder Symbol::runBuiltin(Script& script, std::vector<ValueHolder>& params)
{
if (m_type != Type::Builtin)
return {};
auto fn = std::get<BuiltinFn>(m_data);
return fn(script, params);
if (m_type != Type::Builtin)
return {};
auto fn = std::get<BuiltinFn>(m_data);
return fn(script, params);
}
SymbolScope::SymbolScope(SymbolScope* parent)
: m_parent(parent)
: m_parent(parent)
{}
SymbolScope::SymbolScope(SymbolScope&& other)
: m_symbols(std::move(other.m_symbols))
, m_parent(other.m_parent)
: m_symbols(std::move(other.m_symbols))
, m_parent(other.m_parent)
{}
void SymbolScope::setParent(SymbolScope& parent)
{
m_parent = &parent;
m_parent = &parent;
}
void SymbolScope::set(const std::string& name, const ValueHolder& value)
{
auto existing = get(name);
if (existing)
existing->get() = value;
else
m_symbols.insert_or_assign(name, value);
auto existing = get(name);
if (existing)
existing->get() = value;
else
m_symbols.insert_or_assign(name, value);
}
void SymbolScope::set(const std::string& name, ValueHolder&& value)
{
auto existing = get(name);
if (existing)
existing->get() = value;
else
m_symbols.insert_or_assign(name, std::move(value));
auto existing = get(name);
if (existing)
existing->get() = value;
else
m_symbols.insert_or_assign(name, std::move(value));
}
void SymbolScope::set(const std::string& name, Scope& scope)
{
m_symbols.insert_or_assign(name, scope);
m_symbols.insert_or_assign(name, scope);
}
void SymbolScope::set(const std::string& name, const BuiltinFn& fn)
{
m_symbols.insert_or_assign(name, fn);
m_symbols.insert_or_assign(name, fn);
}
std::optional<SymbolRef> SymbolScope::get(const std::string& name)
{
auto iter = m_symbols.find(name);
if (iter == m_symbols.end())
return m_parent ? m_parent->get(name) : std::nullopt;
else
return m_symbols.at(name);
auto iter = m_symbols.find(name);
if (iter == m_symbols.end())
return m_parent ? m_parent->get(name) : std::nullopt;
else
return m_symbols.at(name);
}
std::optional<SymbolCRef> SymbolScope::cget(const std::string& name) const
{
auto iter = m_symbols.find(name);
if (iter == m_symbols.end())
return m_parent ? m_parent->cget(name) : std::nullopt;
else
return m_symbols.at(name);
auto iter = m_symbols.find(name);
if (iter == m_symbols.end())
return m_parent ? m_parent->cget(name) : std::nullopt;
else
return m_symbols.at(name);
}
void SymbolScope::clear()
{
m_symbols.clear();
m_symbols.clear();
}
void SymbolScope::dump(bool deep) const
{
std::cout << "- - - - -\n";
for (auto& [key, val] : m_symbols) {
std::cout << key << " = ";
if (val.getType() == Symbol::Type::Scope)
std::cout << "[Scope]\n";
else
std::cout << "'" << val.getValue().toStdString() << "'\n";
}
if (deep && m_parent)
m_parent->dump(true);
else
std::cout << "- - - - -\n";
std::cout << "- - - - -\n";
for (auto& [key, val] : m_symbols) {
std::cout << key << " = ";
if (val.getType() == Symbol::Type::Scope)
std::cout << "[Scope]\n";
else
std::cout << "'" << val.getValue().toStdString() << "'\n";
}
if (deep && m_parent)
m_parent->dump(true);
else
std::cout << "- - - - -\n";
}
bool SymbolScope::hasValues() const
{
return !m_symbols.empty();
return !m_symbols.empty();
}

@ -23,28 +23,28 @@ using BuiltinFn = std::function<ValueHolder(Script&, std::vector<ValueHolder>&)>
class Symbol
{
public:
enum class Type {
Value,
Scope,
Builtin
};
enum class Type {
Value,
Scope,
Builtin
};
Symbol() = delete;
virtual ~Symbol() = default;
Symbol() = delete;
virtual ~Symbol() = default;
Symbol(const Scope& scope);
Symbol(const ValueHolder& data);
Symbol(const BuiltinFn& fn);
Symbol(const Scope& scope);
Symbol(const ValueHolder& data);
Symbol(const BuiltinFn& fn);
Type getType() const;
ValueHolder& getValue();
const ValueHolder& getValue() const;
const Scope& getScope() const;
ValueHolder runBuiltin(Script& script, std::vector<ValueHolder>& params);
Type getType() const;
ValueHolder& getValue();
const ValueHolder& getValue() const;
const Scope& getScope() const;
ValueHolder runBuiltin(Script& script, std::vector<ValueHolder>& params);
private:
std::variant<ValueHolder, ScopeCRef, BuiltinFn> m_data;
Type m_type;
std::variant<ValueHolder, ScopeCRef, BuiltinFn> m_data;
Type m_type;
};
using SymbolRef = std::reference_wrapper<Symbol>;
@ -53,34 +53,34 @@ using SymbolCRef = std::reference_wrapper<const Symbol>;
class SymbolScope
{
public:
SymbolScope() = default;
~SymbolScope() = default;
SymbolScope() = default;
~SymbolScope() = default;
SymbolScope(SymbolScope&& other);
SymbolScope(const SymbolScope&) = delete;
SymbolScope(SymbolScope* parent);
SymbolScope(SymbolScope&& other);
SymbolScope(const SymbolScope&) = delete;
SymbolScope(SymbolScope* parent);
SymbolScope& operator=(SymbolScope&) = delete;
SymbolScope& operator=(SymbolScope&) = delete;
void setParent(SymbolScope& parent);
void setParent(SymbolScope& parent);
void set(const std::string& name, const ValueHolder& value);
void set(const std::string& name, ValueHolder&& value);
void set(const std::string& name, Scope& scope);
void set(const std::string& name, const BuiltinFn& fn);
void set(const std::string& name, const ValueHolder& value);
void set(const std::string& name, ValueHolder&& value);
void set(const std::string& name, Scope& scope);
void set(const std::string& name, const BuiltinFn& fn);
std::optional<SymbolRef> get(const std::string& name);
std::optional<SymbolCRef> cget(const std::string& name) const;
std::optional<SymbolRef> get(const std::string& name);
std::optional<SymbolCRef> cget(const std::string& name) const;
void clear();
void clear();
void dump(bool deep = false) const;
void dump(bool deep = false) const;
bool hasValues() const;
bool hasValues() const;
private:
std::unordered_map<std::string, Symbol> m_symbols;
SymbolScope* m_parent{ nullptr };
std::unordered_map<std::string, Symbol> m_symbols;
SymbolScope* m_parent{ nullptr };
};
#endif // SYMBOLSCOPE_H

File diff suppressed because it is too large Load Diff

@ -20,38 +20,38 @@ struct TokenizerPriv;
class Tokenizer
{
public:
explicit Tokenizer(const std::string& input);
Tokenizer(Tokenizer&& other);
Tokenizer() = delete;
Tokenizer(const Tokenizer&) = delete;
~Tokenizer();
explicit Tokenizer(const std::string& input);
Tokenizer(Tokenizer&& other);
Tokenizer() = delete;
Tokenizer(const Tokenizer&) = delete;
~Tokenizer();
Tokenizer& operator=(const Tokenizer&) = delete;
Tokenizer& operator=(const Tokenizer&) = delete;
bool process();
const std::string& lastError();
std::pair<int,int> currentLineCol() const;
bool process();
const std::string& lastError();
std::pair<int,int> currentLineCol() const;
using Iterator = VectorOfUniquePtrsIterator<Scope>;
using Iterator = VectorOfUniquePtrsIterator<Scope>;
Iterator begin() const;
Iterator end() const;
Iterator begin() const;
Iterator end() const;
const std::vector<std::string>& globals();
const std::vector<Menu>& menus() const;
const std::vector<Dialog>& dialogs() const;
const std::vector<std::string>& globals();
const std::vector<Menu>& menus() const;
const std::vector<Dialog>& dialogs() const;
private:
/* TODO Consider moving the below functions into TokenizerPriv */
bool nextKeyword();
bool processImperativeBlock(ImperativeBlockType blockType);
bool processGlobals();
bool processMenu();
bool processDialog();
/* *** */
std::unique_ptr<TokenizerPriv> mp;
const std::string m_data; // maybe move this one too, to TokenizerPriv?
/* TODO Consider moving the below functions into TokenizerPriv */
bool nextKeyword();
bool processImperativeBlock(ImperativeBlockType blockType);
bool processGlobals();
bool processMenu();
bool processDialog();
/* *** */
std::unique_ptr<TokenizerPriv> mp;
const std::string m_data; // maybe move this one too, to TokenizerPriv?
};
#endif // TOKENIZER_H

@ -9,67 +9,67 @@
struct MapTokenPriv
{
MapTokenPriv(int line, int col);
MapTokenPriv(MapTokenPriv&& other);
~MapTokenPriv() = default;
MapTokenPriv(int line, int col);
MapTokenPriv(MapTokenPriv&& other);
~MapTokenPriv() = default;
Expression subscript;
Expression subscript;
};
struct ListConstructPriv
{
ListConstructPriv() = default;
ListConstructPriv(ListConstructPriv&& other);
~ListConstructPriv() = default;
ListConstructPriv() = default;
ListConstructPriv(ListConstructPriv&& other);
~ListConstructPriv() = default;
std::vector<Expression> items;
std::vector<Expression> items;
};
struct DispatchTokenPriv
{
DispatchTokenPriv(int line, int col);
DispatchTokenPriv(DispatchTokenPriv&& other);
~DispatchTokenPriv() = default;
DispatchTokenPriv(int line, int col);
DispatchTokenPriv(DispatchTokenPriv&& other);
~DispatchTokenPriv() = default;
Scope parameters;
Scope parameters;
};
struct FunctionTokenPriv
{
explicit FunctionTokenPriv(const std::string& identifier_, int line, int col);
FunctionTokenPriv(FunctionTokenPriv&& other);
~FunctionTokenPriv() = default;
explicit FunctionTokenPriv(const std::string& identifier_, int line, int col);
FunctionTokenPriv(FunctionTokenPriv&& other);
~FunctionTokenPriv() = default;
const std::string identifier;
const std::string identifier;
Scope parameters;
};
struct ExpressionPriv
{
ExpressionPriv() = default;
ExpressionPriv(ExpressionPriv&& other);
~ExpressionPriv() = default;
ExpressionPriv() = default;
ExpressionPriv(ExpressionPriv&& other);
~ExpressionPriv() = default;
std::vector<std::unique_ptr<TokenBase>> tokens;
std::vector<std::unique_ptr<TokenBase>> tokens;
};
struct ScopePriv
{
ScopePriv() = default;
ScopePriv(ScopePriv&& other);
~ScopePriv() = default;
ScopePriv() = default;
ScopePriv(ScopePriv&& other);
~ScopePriv() = default;
std::vector<Expression> expressions;
std::vector<Expression> expressions;
};
struct ConditionalScopeTokenPriv
{
ConditionalScopeTokenPriv(int line, int col, Expression&& condition);
ConditionalScopeTokenPriv(ConditionalScopeTokenPriv&& other);
~ConditionalScopeTokenPriv() = default;
ConditionalScopeTokenPriv(int line, int col, Expression&& condition);
ConditionalScopeTokenPriv(ConditionalScopeTokenPriv&& other);
~ConditionalScopeTokenPriv() = default;
ConditionalScope scope;
ConditionalScope scope;
};
MapToken::~MapToken() = default;
@ -84,46 +84,46 @@ ConditionalScopeToken::~ConditionalScopeToken() = default;
*/
MapTokenPriv::MapTokenPriv(int line, int col)
: subscript(line, col)
: subscript(line, col)
{}
MapTokenPriv::MapTokenPriv(MapTokenPriv&& other)
: subscript(std::move(other.subscript))
: subscript(std::move(other.subscript))
{}
ListConstructPriv::ListConstructPriv(ListConstructPriv&& other)
: items(std::move(other.items))
: items(std::move(other.items))
{}
DispatchTokenPriv::DispatchTokenPriv(int line, int col)
: parameters(line, col)
: parameters(line, col)
{}
DispatchTokenPriv::DispatchTokenPriv(DispatchTokenPriv&& other)
: parameters(std::move(other.parameters))
: parameters(std::move(other.parameters))
{}
FunctionTokenPriv::FunctionTokenPriv(const std::string& identifier_, int line, int col)
: identifier(identifier_)
, parameters(line, col)
: identifier(identifier_)
, parameters(line, col)
{}
FunctionTokenPriv::FunctionTokenPriv(FunctionTokenPriv&& other)
: identifier(other.identifier)
, parameters(std::move(other.parameters))
: identifier(other.identifier)
, parameters(std::move(other.parameters))
{}
ExpressionPriv::ExpressionPriv(ExpressionPriv&& other)
: tokens(std::move(other.tokens))
: tokens(std::move(other.tokens))
{}
ScopePriv::ScopePriv(ScopePriv&& other)
: expressions(std::move(other.expressions))
: expressions(std::move(other.expressions))
{}
ConditionalScopeTokenPriv::ConditionalScopeTokenPriv(int line, int col, Expression&& condition)
: scope(line, col, std::move(condition))
: scope(line, col, std::move(condition))
{}
/*
@ -131,110 +131,110 @@ ConditionalScopeTokenPriv::ConditionalScopeTokenPriv(int line, int col, Expressi
*/
TokenBase::TokenBase(TokenType type, int line, int col)
: m_type(type)
, m_line(line)
, m_col(col)
: m_type(type)
, m_line(line)
, m_col(col)
{}
TokenBase::TokenBase(TokenBase&& other)
: m_type(other.m_type)
, m_line(other.m_line)
, m_col(other.m_col)
: m_type(other.m_type)
, m_line(other.m_line)
, m_col(other.m_col)
{}
Token::Token(const std::string& token, TokenType type, int line, int col)
: TokenBase(type, line, col)
, m_token(token)
: TokenBase(type, line, col)
, m_token(token)
{}
Token::Token(Token&& other)
: TokenBase(std::move(other))
: TokenBase(std::move(other))
, m_token(std::move(other.m_token))
{}
BoolToken::BoolToken(bool val, int line, int col)
: TokenBase(TokenType::BoolLiteral, line, col)
, m_val(val)
: TokenBase(TokenType::BoolLiteral, line, col)
, m_val(val)
{}
BoolToken::BoolToken(BoolToken&& other)
: TokenBase(std::move(other))
, m_val(other.m_val)
: TokenBase(std::move(other))
, m_val(other.m_val)
{}
MapToken::MapToken(const std::string& token, int line, int col, bool isRef)
: Token(token, isRef ? TokenType::Reference : TokenType::MapKeyword, line, col)
, mp(new MapTokenPriv(line, col))
: Token(token, isRef ? TokenType::Reference : TokenType::MapKeyword, line, col)
, mp(new MapTokenPriv(line, col))
{}
MapToken::MapToken(MapToken&& other)
: Token(std::move(other))
, mp(std::move(other.mp))
: Token(std::move(other))
, mp(std::move(other.mp))
{}
DispatchToken::DispatchToken(const std::string& token, int line, int col)
: Token(token, TokenType::Dispatch, line, col)
, mp(new DispatchTokenPriv(line, col))
: Token(token, TokenType::Dispatch, line, col)
, mp(new DispatchTokenPriv(line, col))
{}
DispatchToken::DispatchToken(DispatchToken&& other)
: Token(std::move(other))
, mp(std::move(other.mp))
: Token(std::move(other))
, mp(std::move(other.mp))
{}
FunctionToken::FunctionToken(const std::string& identifier, int line, int col)
: TokenBase(TokenType::Function, line, col)
, mp(new FunctionTokenPriv(identifier, line, col))
: TokenBase(TokenType::Function, line, col)
, mp(new FunctionTokenPriv(identifier, line, col))
{}
FunctionToken::FunctionToken(FunctionToken&& other)
: TokenBase(std::move(other))
, mp(std::move(other.mp))
: TokenBase(std::move(other))
, mp(std::move(other.mp))
{}
Expression::Expression(int line, int col)
: TokenBase(TokenType::Expression, line, col)
, mp(new ExpressionPriv)
: TokenBase(TokenType::Expression, line, col)
, mp(new ExpressionPriv)
{}
Expression::Expression(Expression&& other)
: TokenBase(std::move(other))
, mp(std::move(other.mp))
: TokenBase(std::move(other))
, mp(std::move(other.mp))
{}
Scope::Scope(int line, int col)
: mp(new ScopePriv)
, m_line(line)
, m_col(col)
: mp(new ScopePriv)
, m_line(line)
, m_col(col)
{}
Scope::Scope(std::pair<int, int> lineCol)
: mp(new ScopePriv)
, m_line(lineCol.first)
, m_col(lineCol.second)
: mp(new ScopePriv)
, m_line(lineCol.first)
, m_col(lineCol.second)
{}
Scope::Scope(Scope&& other)
: mp(std::move(other.mp))
, m_line(other.m_line)
, m_col(other.m_col)
: mp(std::move(other.mp))
, m_line(other.m_line)
, m_col(other.m_col)
{}
ImperativeScope::ImperativeScope(int line, int col, ImperativeBlockType type, const std::string& identifier, Scope&& params)
: Scope(line, col)
, m_type(type)
, m_id(identifier)
: Scope(line, col)
, m_type(type)
, m_id(identifier)
, m_params(std::move(params))
{}
ConditionalScope::ConditionalScope(int line, int col, Expression&& condition)
: Scope(line, col)
, m_cond(std::move(condition))
: Scope(line, col)
, m_cond(std::move(condition))
{}
ConditionalScopeToken::ConditionalScopeToken(TokenType type, int line, int col, Expression&& condition)
: TokenBase(type, line, col)
, mp(new ConditionalScopeTokenPriv(line, col, std::move(condition)))
: TokenBase(type, line, col)
, mp(new ConditionalScopeTokenPriv(line, col, std::move(condition)))
{}
/*
@ -245,12 +245,12 @@ ConditionalScopeToken::ConditionalScopeToken(TokenType type, int line, int col,
std::pair<int, int> TokenBase::lineCol() const
{
return std::make_pair(m_line, m_col);
return std::make_pair(m_line, m_col);
}
TokenType TokenBase::type() const
{
return m_type;
return m_type;
}
/*
@ -261,7 +261,7 @@ TokenType TokenBase::type() const
const std::string& Token::token() const
{
return m_token;
return m_token;
}
/*
@ -272,7 +272,7 @@ const std::string& Token::token() const
bool BoolToken::value() const
{
return m_val;
return m_val;
}
/*
@ -283,12 +283,12 @@ bool BoolToken::value() const
Expression& MapToken::subscript()
{
return mp->subscript;
return mp->subscript;
}
const Expression& MapToken::subscript() const
{
return mp->subscript;
return mp->subscript;
}
/*
@ -299,8 +299,8 @@ const Expression& MapToken::subscript() const
Expression& ListConstruct::addItem(int line, int col)
{
mp->items.emplace_back(line, col);
return mp->items.back();
mp->items.emplace_back(line, col);
return mp->items.back();
}
/*
@ -311,7 +311,7 @@ Expression& ListConstruct::addItem(int line, int col)
Scope& DispatchToken::parameters()
{
return mp->parameters;
return mp->parameters;
}
/*
@ -322,17 +322,17 @@ Scope& DispatchToken::parameters()
Scope& FunctionToken::parameters()
{
return mp->parameters;
return mp->parameters;
}
const Scope& FunctionToken::parameters() const
{
return mp->parameters;
return mp->parameters;
}
const std::string& FunctionToken::identifier() const
{
return mp->identifier;
return mp->identifier;
}
/*
@ -343,7 +343,7 @@ const std::string& FunctionToken::identifier() const
ConditionalScope& ConditionalScopeToken::scope() const
{
return mp->scope;
return mp->scope;
}
/*
@ -354,86 +354,86 @@ ConditionalScope& ConditionalScopeToken::scope() const
void Expression::addToken(const std::string& token, TokenType type, int line, int col)
{
mp->tokens.emplace_back(new Token(token, type, line, col));
mp->tokens.emplace_back(new Token(token, type, line, col));
}
void Expression::addBool(bool val, int line, int col)
{
mp->tokens.emplace_back(new BoolToken(val, line, col));
mp->tokens.emplace_back(new BoolToken(val, line, col));
}
MapToken& Expression::addMapToken(const std::string& token, int line, int col, bool isRef)
{
mp->tokens.emplace_back(new MapToken(token, line, col, isRef));
return *(static_cast<MapToken*>(mp->tokens.back().get()));
mp->tokens.emplace_back(new MapToken(token, line, col, isRef));
return *(static_cast<MapToken*>(mp->tokens.back().get()));
}
DispatchToken& Expression::addDispatchToken(const std::string& token, int line, int col)
{
mp->tokens.emplace_back(new DispatchToken(token, line, col));
return *(static_cast<DispatchToken*>(mp->tokens.back().get()));
mp->tokens.emplace_back(new DispatchToken(token, line, col));
return *(static_cast<DispatchToken*>(mp->tokens.back().get()));
}
Expression& Expression::addExpression(int line, int col)
{
mp->tokens.emplace_back(new Expression(line, col));
return *(static_cast<Expression*>(mp->tokens.back().get()));
mp->tokens.emplace_back(new Expression(line, col));
return *(static_cast<Expression*>(mp->tokens.back().get()));
}
FunctionToken& Expression::addFunction(const std::string& identifier, int line, int col)
{
mp->tokens.emplace_back(new FunctionToken(identifier, line, col));
return *(static_cast<FunctionToken*>(mp->tokens.back().get()));
mp->tokens.emplace_back(new FunctionToken(identifier, line, col));
return *(static_cast<FunctionToken*>(mp->tokens.back().get()));
}
ConditionalScopeToken& Expression::addIfScope(int line, int col, Expression&& condition)
{
mp->tokens.emplace_back(new ConditionalScopeToken(TokenType::CondIf, line, col, std::move(condition)));
return *(static_cast<ConditionalScopeToken*>(mp->tokens.back().get()));
mp->tokens.emplace_back(new ConditionalScopeToken(TokenType::CondIf, line, col, std::move(condition)));
return *(static_cast<ConditionalScopeToken*>(mp->tokens.back().get()));
}
ConditionalScopeToken& Expression::addElseScope(int line, int col)
{
Expression dummy(0, 0);
mp->tokens.emplace_back(new ConditionalScopeToken(TokenType::CondElse, line, col, std::move(dummy)));
return *(static_cast<ConditionalScopeToken*>(mp->tokens.back().get()));
Expression dummy(0, 0);
mp->tokens.emplace_back(new ConditionalScopeToken(TokenType::CondElse, line, col, std::move(dummy)));
return *(static_cast<ConditionalScopeToken*>(mp->tokens.back().get()));
}
ConditionalScopeToken& Expression::addWhileScope(int line, int col, Expression&& condition)
{
mp->tokens.emplace_back(new ConditionalScopeToken(TokenType::CondWhile, line, col, std::move(condition)));
return *(static_cast<ConditionalScopeToken*>(mp->tokens.back().get()));
mp->tokens.emplace_back(new ConditionalScopeToken(TokenType::CondWhile, line, col, std::move(condition)));
return *(static_cast<ConditionalScopeToken*>(mp->tokens.back().get()));
}
Expression::Iterator Expression::begin() const
{
return mp->tokens.begin();
return mp->tokens.begin();
}
Expression::Iterator Expression::end() const
{
return mp->tokens.end();
return mp->tokens.end();
}
size_t Expression::count() const
{
return mp->tokens.size();
return mp->tokens.size();
}
void Expression::eraseBack()
{
if (!mp->tokens.empty())
mp->tokens.erase(mp->tokens.end() - 1);
if (!mp->tokens.empty())
mp->tokens.erase(mp->tokens.end() - 1);
}
TokenBase& Expression::operator[](size_t idx) const
{
return *(mp->tokens.at(idx));
return *(mp->tokens.at(idx));
}
TokenBase& Expression::back() const
{
return *(mp->tokens.back());
return *(mp->tokens.back());
}
/*
@ -444,12 +444,12 @@ TokenBase& Expression::back() const
std::pair<int, int> Scope::getLineCol() const
{
return std::make_pair(m_line, m_col);
return std::make_pair(m_line, m_col);
}
Expression& Scope::addStatement(Expression&& expr)
{
mp->expressions.push_back(std::move(expr));
mp->expressions.push_back(std::move(expr));
return mp->expressions.back();
}
@ -460,12 +460,12 @@ std::size_t Scope::countStatements() const
std::vector<Expression>::iterator Scope::begin() const
{
return mp->expressions.begin();
return mp->expressions.begin();
}
std::vector<Expression>::iterator Scope::end() const
{
return mp->expressions.end();
return mp->expressions.end();
}
/*
@ -476,17 +476,17 @@ std::vector<Expression>::iterator Scope::end() const
const std::string& ImperativeScope::identifier() const
{
return m_id;
return m_id;
}
const Scope& ImperativeScope::params() const
{
return m_params;
return m_params;
}
ImperativeBlockType ImperativeScope::type() const
{
return m_type;
return m_type;
}
/*
@ -497,5 +497,5 @@ ImperativeBlockType ImperativeScope::type() const
const Expression& ConditionalScope::condition() const
{
return m_cond;
return m_cond;
}

@ -13,45 +13,45 @@
#include <memory>
enum class TokenType {
Undefined,
Command,
Function,
Keyword,
MapKeyword,
Reference,
BoolLiteral,
Hook,
Dispatch,
Operator,
StringLiteral,
NumberLiteral,
RealLiteral,
Comma,
SemiColon,
Undefined,
Command,
Function,
Keyword,
MapKeyword,
Reference,
BoolLiteral,
Hook,
Dispatch,
Operator,
StringLiteral,
NumberLiteral,
RealLiteral,
Comma,
SemiColon,
CondIf,
CondElse,
CondWhile,
Continue,
Break,
Continue,
Break,
Scope, // Triggers at opening curly-parenthesis, signal a scope of expressions / statements.
Expression, // Triggers at opening parenthesis, with an operator before, signal a sub-expression.
Parameters, // Triggers at opening parenthesis, with a keyword before, signals a list of function parameters
Scope, // Triggers at opening curly-parenthesis, signal a scope of expressions / statements.
Expression, // Triggers at opening parenthesis, with an operator before, signal a sub-expression.
Parameters, // Triggers at opening parenthesis, with a keyword before, signals a list of function parameters
/* Not actual token types */
EndScope, // Used to signal end of a scope of expressions / statements
EndParameters, // Used to signal end of function parameter list
EndExpression, // Used to signal end of a sub-expression (parenthesises)
Comment // Used during tokenization for stripping comments.
/* Not actual token types */
EndScope, // Used to signal end of a scope of expressions / statements
EndParameters, // Used to signal end of function parameter list
EndExpression, // Used to signal end of a sub-expression (parenthesises)
Comment // Used during tokenization for stripping comments.
};
enum class ImperativeBlockType {
Function,
Command,
Hook
Function,
Command,
Hook
};
/* ************************************************************** */
@ -60,64 +60,64 @@ enum class ImperativeBlockType {
template<typename T>
class VectorOfUniquePtrsIterator
{
typename std::vector<std::unique_ptr<T>>::iterator it;
typename std::vector<std::unique_ptr<T>>::iterator it;
public:
VectorOfUniquePtrsIterator() = delete;
~VectorOfUniquePtrsIterator() = default;
VectorOfUniquePtrsIterator(typename std::vector<std::unique_ptr<T>>::iterator underlyingIter)
: it(underlyingIter)
{}
VectorOfUniquePtrsIterator(const VectorOfUniquePtrsIterator& other)
: it(other.it)
{}
VectorOfUniquePtrsIterator(VectorOfUniquePtrsIterator&& other)
: it(std::move(other.it))
{}
VectorOfUniquePtrsIterator& operator++()
{
++it;
return *this;
}
VectorOfUniquePtrsIterator& operator--()
{
--it;
return *this;
}
VectorOfUniquePtrsIterator operator+(int i)
{
return it + i;
}
VectorOfUniquePtrsIterator operator-(int i)
{
return it - i;
}
bool operator==(const VectorOfUniquePtrsIterator& other)
{
return it == other.it;
}
bool operator!=(const VectorOfUniquePtrsIterator& other)
{
return it != other.it;
}
T& operator*()
{
return **it;
}
T* operator->()
{
return &(**it);
}
VectorOfUniquePtrsIterator() = delete;
~VectorOfUniquePtrsIterator() = default;
VectorOfUniquePtrsIterator(typename std::vector<std::unique_ptr<T>>::iterator underlyingIter)
: it(underlyingIter)
{}
VectorOfUniquePtrsIterator(const VectorOfUniquePtrsIterator& other)
: it(other.it)
{}
VectorOfUniquePtrsIterator(VectorOfUniquePtrsIterator&& other)
: it(std::move(other.it))
{}
VectorOfUniquePtrsIterator& operator++()
{
++it;
return *this;
}
VectorOfUniquePtrsIterator& operator--()
{
--it;
return *this;
}
VectorOfUniquePtrsIterator operator+(int i)
{
return it + i;
}
VectorOfUniquePtrsIterator operator-(int i)
{
return it - i;
}
bool operator==(const VectorOfUniquePtrsIterator& other)
{
return it == other.it;
}
bool operator!=(const VectorOfUniquePtrsIterator& other)
{
return it != other.it;
}
T& operator*()
{
return **it;
}
T* operator->()
{
return &(**it);
}
};
/* ************************************************************** */
@ -133,18 +133,18 @@ class ConditionalScope;
class TokenBase
{
public:
virtual ~TokenBase() = default;
TokenType type() const;
std::pair<int,int> lineCol() const;
virtual ~TokenBase() = default;
TokenType type() const;
std::pair<int,int> lineCol() const;
protected:
TokenBase(TokenBase&& other);
explicit TokenBase(TokenType type, int line, int col);
TokenBase(TokenBase&& other);
explicit TokenBase(TokenType type, int line, int col);
private:
const TokenType m_type;
const int m_line;
const int m_col;
const TokenType m_type;
const int m_line;
const int m_col;
};
using TokenBaseRef = std::reference_wrapper<TokenBase>;
@ -154,19 +154,19 @@ using TokenBaseRef = std::reference_wrapper<TokenBase>;
class Token : public TokenBase
{
friend class Expression;
friend class MapToken;
friend class DispatchToken;
friend class Expression;
friend class MapToken;
friend class DispatchToken;
public:
~Token() override = default;
const std::string& token() const;
~Token() override = default;
const std::string& token() const;
private:
Token(const std::string& token, TokenType type, int line, int col);
Token(Token&& other);
Token(const std::string& token, TokenType type, int line, int col);
Token(Token&& other);
const std::string m_token;
const std::string m_token;
};
/* ************************************************************** */
@ -174,19 +174,19 @@ private:
class BoolToken : public TokenBase
{
friend class Expression;
friend class MapToken;
friend class DispatchToken;
friend class Expression;
friend class MapToken;
friend class DispatchToken;
public:
~BoolToken() override = default;
bool value() const;
~BoolToken() override = default;
bool value() const;
private:
BoolToken(bool val, int line, int col);
BoolToken(BoolToken&& other);
BoolToken(bool val, int line, int col);
BoolToken(BoolToken&& other);
const bool m_val;
const bool m_val;
};
/* ************************************************************** */
@ -196,15 +196,15 @@ struct MapTokenPriv;
class MapToken : public Token
{
public:
MapToken(const std::string& token, int line, int col, bool isRef = false);
MapToken(MapToken&& other);
~MapToken() override;
MapToken(const std::string& token, int line, int col, bool isRef = false);
MapToken(MapToken&& other);
~MapToken() override;
Expression& subscript();
const Expression& subscript() const;
Expression& subscript();
const Expression& subscript() const;
private:
std::unique_ptr<MapTokenPriv> mp;
std::unique_ptr<MapTokenPriv> mp;
};
/* ************************************************************** */
@ -214,15 +214,15 @@ struct ListConstructPriv;
class ListConstruct : public Token // TODO ListConstruct is not yet fully implemented.
{
public:
ListConstruct(int line, int col);
ListConstruct(ListConstruct&& other);
~ListConstruct() override;
ListConstruct(int line, int col);
ListConstruct(ListConstruct&& other);
~ListConstruct() override;
const std::vector<Expression>& items();
Expression& addItem(int line, int col);
const std::vector<Expression>& items();
Expression& addItem(int line, int col);
private:
std::unique_ptr<ListConstructPriv> mp;
std::unique_ptr<ListConstructPriv> mp;
};
/* ************************************************************** */
@ -232,14 +232,14 @@ struct DispatchTokenPriv;
class DispatchToken : public Token
{
public:
DispatchToken(const std::string& token, int line, int col);
DispatchToken(DispatchToken&& other);
~DispatchToken() override;
DispatchToken(const std::string& token, int line, int col);
DispatchToken(DispatchToken&& other);
~DispatchToken() override;
Scope& parameters();
Scope& parameters();
private:
std::unique_ptr<DispatchTokenPriv> mp;
std::unique_ptr<DispatchTokenPriv> mp;
};
/* ************************************************************** */
@ -248,19 +248,19 @@ private:
struct FunctionTokenPriv;
class FunctionToken : public TokenBase
{
friend class Expression;
friend class Expression;
public:
~FunctionToken() override;
Scope& parameters();
const Scope& parameters() const;
const std::string& identifier() const;
~FunctionToken() override;
Scope& parameters();
const Scope& parameters() const;
const std::string& identifier() const;
private:
FunctionToken(const std::string& identifier, int line, int col);
FunctionToken(FunctionToken&& other);
FunctionToken(const std::string& identifier, int line, int col);
FunctionToken(FunctionToken&& other);
std::unique_ptr<FunctionTokenPriv> mp;
std::unique_ptr<FunctionTokenPriv> mp;
};
/* ************************************************************** */
@ -269,15 +269,15 @@ private:
struct ConditionalScopeTokenPriv;
class ConditionalScopeToken : public TokenBase
{
friend class Expression;
friend class Expression;
public:
~ConditionalScopeToken() override;
ConditionalScope& scope() const;
~ConditionalScopeToken() override;
ConditionalScope& scope() const;
private:
ConditionalScopeToken(TokenType type, int line, int col, Expression&& condition);
std::unique_ptr<ConditionalScopeTokenPriv> mp;
ConditionalScopeToken(TokenType type, int line, int col, Expression&& condition);
std::unique_ptr<ConditionalScopeTokenPriv> mp;
};
/* ************************************************************** */
@ -287,36 +287,36 @@ struct ExpressionPriv;
class Expression : public TokenBase
{
public:
Expression(int line, int col);
Expression(Expression&& other);
Expression(Expression&) = delete;
~Expression() override;
using Iterator = VectorOfUniquePtrsIterator<TokenBase>;
Expression& operator=(Expression&) = delete;
Expression& operator=(Expression&&);
void addToken(const std::string& token, TokenType type, int line, int col);
void addBool(bool val, int line, int col);
MapToken& addMapToken(const std::string& token, int line, int col, bool isRef = false);
DispatchToken& addDispatchToken(const std::string& token, int line, int col);
Expression& addExpression(int line, int col);
FunctionToken& addFunction(const std::string& identifier, int line, int col);
ConditionalScopeToken& addIfScope(int line, int col, Expression&& condition);
ConditionalScopeToken& addElseScope(int line, int col);
ConditionalScopeToken& addWhileScope(int line, int col, Expression&& condition);
Iterator begin() const;
Iterator end() const;
size_t count() const;
void eraseBack();
TokenBase& operator[](size_t idx) const;
TokenBase& back() const;
Expression(int line, int col);
Expression(Expression&& other);
Expression(Expression&) = delete;
~Expression() override;
using Iterator = VectorOfUniquePtrsIterator<TokenBase>;
Expression& operator=(Expression&) = delete;
Expression& operator=(Expression&&);
void addToken(const std::string& token, TokenType type, int line, int col);
void addBool(bool val, int line, int col);
MapToken& addMapToken(const std::string& token, int line, int col, bool isRef = false);
DispatchToken& addDispatchToken(const std::string& token, int line, int col);
Expression& addExpression(int line, int col);
FunctionToken& addFunction(const std::string& identifier, int line, int col);
ConditionalScopeToken& addIfScope(int line, int col, Expression&& condition);
ConditionalScopeToken& addElseScope(int line, int col);
ConditionalScopeToken& addWhileScope(int line, int col, Expression&& condition);
Iterator begin() const;
Iterator end() const;
size_t count() const;
void eraseBack();
TokenBase& operator[](size_t idx) const;
TokenBase& back() const;
private:
std::unique_ptr<ExpressionPriv> mp;
std::unique_ptr<ExpressionPriv> mp;
};
/* ************************************************************** */
@ -327,23 +327,23 @@ using ScopeRef = std::reference_wrapper<Scope>;
class Scope
{
public:
Scope(int line, int col);
explicit Scope(std::pair<int,int> lineCol);
Scope(Scope&& other);
Scope(Scope&) = delete;
virtual ~Scope();
std::pair<int,int> getLineCol() const;
Expression& addStatement(Expression&& expr);
Scope(int line, int col);
explicit Scope(std::pair<int,int> lineCol);
Scope(Scope&& other);
Scope(Scope&) = delete;
virtual ~Scope();
std::pair<int,int> getLineCol() const;
Expression& addStatement(Expression&& expr);
std::size_t countStatements() const;
std::vector<Expression>::iterator begin() const;
std::vector<Expression>::iterator end() const;
std::vector<Expression>::iterator begin() const;
std::vector<Expression>::iterator end() const;
private:
std::unique_ptr<ScopePriv> mp;
const int m_line;
const int m_col;
std::unique_ptr<ScopePriv> mp;
const int m_line;
const int m_col;
};
/* ************************************************************** */
@ -352,27 +352,27 @@ private:
class ImperativeScope : public Scope
{
public:
ImperativeScope(int line, int col, ImperativeBlockType type, const std::string& identifier, Scope&& params);
~ImperativeScope() override = default;
const std::string& identifier() const;
const Scope& params() const;
ImperativeBlockType type() const;
ImperativeScope(int line, int col, ImperativeBlockType type, const std::string& identifier, Scope&& params);
~ImperativeScope() override = default;
const std::string& identifier() const;
const Scope& params() const;
ImperativeBlockType type() const;
private:
ImperativeBlockType m_type;
const std::string m_id;
Scope m_params;
ImperativeBlockType m_type;
const std::string m_id;
Scope m_params;
};
class ConditionalScope : public Scope
{
public:
ConditionalScope(int line, int col, Expression&& condition);
~ConditionalScope() override = default;
const Expression& condition() const;
ConditionalScope(int line, int col, Expression&& condition);
~ConditionalScope() override = default;
const Expression& condition() const;
private:
Expression m_cond;
Expression m_cond;
};
#endif // TOKENS_H

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save