Initial commit, pre 1.0.0

master
Tomatix 4 years ago
commit 10ec77f70a
  1. 20
      .gitignore
  2. 49
      AboutIIRC.cpp
  3. 41
      AboutIIRC.h
  4. 105
      AboutIIRC.ui
  5. 274
      ButtonbarMgr.cpp
  6. 77
      ButtonbarMgr.h
  7. 93
      Commands.h
  8. 294
      ConfigMgr.cpp
  9. 71
      ConfigMgr.h
  10. 599
      ICommand.cpp
  11. 39
      ICommand.h
  12. 50
      ICommand/CommandData.cpp
  13. 29
      ICommand/CommandData.h
  14. 373
      IConfig/ColorConfig.cpp
  15. 64
      IConfig/ColorConfig.h
  16. 165
      IConfig/IConfig.cpp
  17. 73
      IConfig/IConfig.h
  18. 225
      IConfig/IConfig.ui
  19. 119
      IConfig/IConfigLogging.cpp
  20. 56
      IConfig/IConfigLogging.h
  21. 85
      IConfig/IConfigLogging.ui
  22. 175
      IConfig/IConfigOptions.cpp
  23. 74
      IConfig/IConfigOptions.h
  24. 382
      IConfig/IConfigOptions.ui
  25. 137
      IConfig/IConfigServers.cpp
  26. 62
      IConfig/IConfigServers.h
  27. 247
      IConfig/IConfigServers.ui
  28. 325
      IConfig/ServerEditor.cpp
  29. 71
      IConfig/ServerEditor.h
  30. 251
      IConfig/ServerEditor.ui
  31. 145
      IConfig/ServerMgr.cpp
  32. 68
      IConfig/ServerMgr.h
  33. 371
      IConfig/ServerModel.cpp
  34. 63
      IConfig/ServerModel.h
  35. 137
      IConfig/todo/ServerItem.cpp
  36. 79
      IConfig/todo/ServerItem.h
  37. 184
      IConfig/todo/ServerModel.cpp
  38. 66
      IConfig/todo/ServerModel.h
  39. 435
      IRC.cpp
  40. 92
      IRC.h
  41. 79
      IRCClient/Commands.h
  42. 207
      IRCClient/DCC.cpp
  43. 73
      IRCClient/DCC.h
  44. 1261
      IRCClient/IRCBase.cpp
  45. 152
      IRCClient/IRCBase.h
  46. 85
      IRCClient/IRCChannel.cpp
  47. 49
      IRCClient/IRCChannel.h
  48. 105
      IRCClient/IRCError.cpp
  49. 50
      IRCClient/IRCError.h
  50. 43
      IRCClient/IRCMember.cpp
  51. 30
      IRCClient/IRCMember.h
  52. 55
      IRCClient/IRCMemberEntry.cpp
  53. 28
      IRCClient/IRCMemberEntry.h
  54. 111
      IRCClient/IRCPrefix.cpp
  55. 52
      IRCClient/IRCPrefix.h
  56. 175
      IRCClient/Numeric.h
  57. 104
      IRCClient/Utilities.cpp
  58. 14
      IRCClient/Utilities.h
  59. 62
      IWin/IWin.cpp
  60. 70
      IWin/IWin.h
  61. 219
      IWin/IWinChannel.cpp
  62. 76
      IWin/IWinChannel.h
  63. 101
      IWin/IWinPrivate.cpp
  64. 54
      IWin/IWinPrivate.h
  65. 205
      IWin/IWinStatus.cpp
  66. 72
      IWin/IWinStatus.h
  67. 214
      IWin/NicklistController.cpp
  68. 56
      IWin/NicklistController.h
  69. BIN
      Icons/add.png
  70. BIN
      Icons/exclamation.png
  71. BIN
      Icons/icon.png
  72. BIN
      Icons/iconcutted.png
  73. BIN
      Icons/log.png
  74. BIN
      Icons/options.png
  75. BIN
      Icons/refresh.png
  76. BIN
      Icons/remove.png
  77. BIN
      Icons/script.png
  78. BIN
      Icons/server.png
  79. 282
      IdealIRC.cpp
  80. 74
      IdealIRC.h
  81. 196
      IdealIRC.pro
  82. 175
      IdealIRC.ui
  83. 539
      IniFile.cpp
  84. 57
      IniFile.h
  85. 80
      InputHandler.cpp
  86. 44
      InputHandler.h
  87. 339
      LICENSE
  88. 329
      MdiManager.cpp
  89. 99
      MdiManager.h
  90. 194
      Numeric.h
  91. 30
      README
  92. 108
      Script/Builtin/Builtin.cpp
  93. 6
      Script/Builtin/Builtin.h
  94. 247
      Script/Builtin/DialogUtils.cpp
  95. 28
      Script/Builtin/DialogUtils.h
  96. 94
      Script/Builtin/Error.cpp
  97. 20
      Script/Builtin/Error.h
  98. 167
      Script/Builtin/GeneralUtils.cpp
  99. 23
      Script/Builtin/GeneralUtils.h
  100. 196
      Script/Builtin/ListUtils.cpp
  101. Some files were not shown because too many files have changed in this diff Show More

20
.gitignore vendored

@ -0,0 +1,20 @@
*.slo
*.lo
*.o
*.a
*.la
*.lai
*.so
*.dll
*.dylib
*.pro.user
*.pro.user.*
moc_*.cpp
qrc_*.cpp
Makefile
*-build-*
build
config.h
*.autosave
.idea
obsolete

@ -0,0 +1,49 @@
/*
* IdealIRC - Internet Relay Chat client
* Copyright (C) 2019 Tom-Andre Barstad
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "AboutIIRC.h"
#include "ui_AboutIIRC.h"
#include "config.h"
#include <QDateTime>
AboutIIRC::AboutIIRC(QWidget *parent) :
QDialog(parent),
ui(new Ui::AboutIIRC)
{
ui->setupUi(this);
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);
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);
}
AboutIIRC::~AboutIIRC()
{
delete ui;
}

@ -0,0 +1,41 @@
/*
* IdealIRC - Internet Relay Chat client
* Copyright (C) 2019 Tom-Andre Barstad
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef ABOUTIIRC_H
#define ABOUTIIRC_H
#include <QDialog>
namespace Ui {
class AboutIIRC;
}
class AboutIIRC : public QDialog
{
Q_OBJECT
public:
explicit AboutIIRC(QWidget *parent = nullptr);
~AboutIIRC();
private:
Ui::AboutIIRC *ui;
};
#endif // ABOUTIIRC_H

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AboutIIRC</class>
<widget class="QDialog" name="AboutIIRC">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>425</width>
<height>490</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>425</width>
<height>490</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>425</width>
<height>490</height>
</size>
</property>
<property name="windowTitle">
<string>About IdealIRC</string>
</property>
<property name="windowIcon">
<iconset resource="resources.qrc">
<normaloff>:/Icons/iconcutted.png</normaloff>:/Icons/iconcutted.png</iconset>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<widget class="QTextBrowser" name="textBrowser">
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;img src=&quot;:/Icons/icon.png&quot; /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:16pt;&quot;&gt;IdealIRC {ver}&lt;/span&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Sans';&quot;&gt;Build type: {buildtype}&lt;/span&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:10pt;&quot;&gt;©{year} Tom-Andre Barstad&lt;/span&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:10pt;&quot;&gt;and contributors.&lt;/span&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;a href=&quot;http://www.idealirc.org/&quot;&gt;&lt;span style=&quot; font-family:'Sans'; font-size:8pt; text-decoration: underline; color:#007af4;&quot;&gt;http://www.idealirc.org/&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Noto Sans'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:8pt;&quot;&gt;This program is free software; you can redistribute it and/or modify&lt;/span&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:8pt;&quot;&gt;it under the terms of the GNU General Public License as published by&lt;/span&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:8pt;&quot;&gt;the Free Software Foundation; either version 2 of the License, or&lt;/span&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:8pt;&quot;&gt;(at your option) any later version.&lt;/span&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Noto Sans'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:8pt;&quot;&gt;This program is distributed in the hope that it will be useful,&lt;/span&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:8pt;&quot;&gt;but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:8pt;&quot;&gt;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the&lt;/span&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:8pt;&quot;&gt;GNU General Public License for more details.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>439</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="resources.qrc"/>
</resources>
<connections>
<connection>
<sender>pushButton</sender>
<signal>clicked()</signal>
<receiver>AboutIIRC</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>466</x>
<y>452</y>
</hint>
<hint type="destinationlabel">
<x>430</x>
<y>444</y>
</hint>
</hints>
</connection>
</connections>
</ui>

@ -0,0 +1,274 @@
#include "ButtonbarMgr.h"
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;
}
}
ButtonbarMgr::ButtonbarMgr(QToolBar *parent)
: QObject(parent)
, m_buttonBar(*parent)
{
m_buttonBar.setContextMenuPolicy(Qt::CustomContextMenu);
connect(&m_buttonBar, &QToolBar::customContextMenuRequested, this, &ButtonbarMgr::buttonBarContextMenu);
m_others_rightSep = m_buttonBar.addSeparator();
}
void ButtonbarMgr::addButton(IWin* subwin, QMdiSubWindow* mdiwin)
{
QAction* nn = findNextNeighbour(subwin);
QAction *actn = new QAction(subwin->getButtonText(), &m_buttonBar);
m_buttonBar.insertAction(nn, actn);
m_winbtn.insert(subwin, actn);
actn->setCheckable(true);
buttons(subwin).push_back(Button(actn, mdiwin));
connect(actn, &QAction::triggered, [this,actn,mdiwin](bool triggered){
if (!triggered) {
actn->setChecked(true);
return;
}
emit changeWindow(mdiwin);
});
}
void ButtonbarMgr::delButton(IWin* subwin)
{
auto clear = [this](Button& button){
m_buttonBar.removeAction(button.button);
button.menu->deleteLater();
button.button->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->button->setText(buttonText);
button->menuHead->setText(buttonText);
}
}
void ButtonbarMgr::subwinActivated(IWin* prev, IWin* next)
{
if (prev) {
Button* button{ findButton(prev) };
if (button)
button->button->setChecked(false);
}
if (next) {
Button* button{ findButton(next) };
if (button)
button->button->setChecked(true);
}
}
void ButtonbarMgr::buttonBarContextMenu(const QPoint& pos)
{
QAction* action = m_buttonBar.actionAt(pos);
if (!action)
return;
IWin *subwin = m_winbtn.key(action);
if (!subwin)
return;
Button* button = findButton(subwin);
if (!button)
return;
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;
}
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;
}
QAction* ButtonbarMgr::findNextNeighbour(IWin* subwin)
{
if (subwin->getType() == IWin::Type::Status || subwin->getType() == IWin::Type::Private) {
ButtonGroup* group = tryFindGroup((subwin->getType() == IWin::Type::Status) ? subwin : subwin->getParent());
return group ? group->rightSep : nullptr;
}
else if (subwin->getType() == IWin::Type::Channel) {
ButtonGroup* group = tryFindGroup(subwin->getParent());
if (!group)
return nullptr;
if (group->privmsg.isEmpty())
return group->rightSep;
else
return group->privmsg[0].button;
}
else
return m_others_rightSep;
}
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;
}
}
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.button->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;
}
ButtonbarMgr::Button::Button(QAction* button_, QMdiSubWindow* parent)
: subwin(parent)
, button(button_)
{
const QString& buttonText = qobject_cast<IWin*>(parent->widget())->getButtonText();
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();
});
}

@ -0,0 +1,77 @@
#ifndef BUTTONBARMGR_H
#define BUTTONBARMGR_H
#include <IWin/IWin.h>
#include <QObject>
#include <QHash>
#include <QMdiSubWindow>
#include <QMenu>
#include <QToolBar>
#include <QHash>
#include <list>
class ButtonbarMgr : public QObject
{
Q_OBJECT
struct Button
{
explicit Button(){}
Button(QAction* button_, QMdiSubWindow* parent);
QMdiSubWindow* subwin{ nullptr };
QAction* button{ nullptr };
QMenu* menu{ nullptr };
QAction* menuHead{ nullptr };
QAction* menuSep{ nullptr };
QAction* menuClose{ nullptr };
};
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;
QAction* rightSep;
QList<Button> blank;
};
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);
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;
signals:
void changeWindow(QMdiSubWindow* mdiwin);
};
#endif // BUTTONBARMGR_H

@ -0,0 +1,93 @@
/*
* IdealIRC - Internet Relay Chat client
* Copyright (C) 2019 Tom-Andre Barstad
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef COMMANDS_H
#define COMMANDS_H
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";
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 Internal {
constexpr auto* CTCP = "CTCP";
constexpr auto* ME = "ME";
constexpr auto* ECHO = "ECHO";
constexpr auto* QUERY = "QUERY";
constexpr auto* MUTERESP = "MUTERESP";
constexpr auto* UNMUTERESP = "UNMUTERESP";
}
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";
} // namespace IRCv3
} // namespace command
#endif // COMMANDS_H

@ -0,0 +1,294 @@
#include "ConfigMgr.h"
#include "config.h"
#include <QHashIterator>
ConfigMgr& ConfigMgr::instance()
{
static ConfigMgr inst;
return inst;
}
// Constructor is private, use static instance()
ConfigMgr::ConfigMgr()
{
load();
}
void ConfigMgr::load()
{
IniFile ini(LOCAL_PATH+"/iirc.ini");
loadGeometry(ini);
loadConnection(ini);
loadCommon(ini);
loadColor(ini);
loadPrefixColor(ini);
loadLogging(ini);
loadScripts(ini);
}
void ConfigMgr::save()
{
IniFile ini(LOCAL_PATH+"/iirc.ini");
saveGeometry(ini);
saveConnection(ini);
saveCommon(ini);
saveColor(ini);
savePrefixColor(ini);
saveLogging(ini);
saveScripts(ini);
emit saved();
}
QString ConfigMgr::geometry(const QString& key) const
{
return geometryData.value(key, "");
}
QString ConfigMgr::connection(const QString& key) const
{
return connectionData.value(key, "");
}
QString ConfigMgr::common(const QString& key) const
{
return commonData.value(key, "");
}
QString ConfigMgr::color(const QString& key) const
{
return colorData.value(key, "");
}
std::optional<QColor> ConfigMgr::prefixColor(const QChar prefix) const
{
for (const auto& pair : prefixColorData)
if (pair.first == prefix)
return std::make_optional(pair.second);
return std::nullopt;
}
QHash<QString, QString> ConfigMgr::color() const
{
return colorData;
}
QVector<std::pair<QChar, QColor> > ConfigMgr::prefixColor() const
{
return prefixColorData;
}
QString ConfigMgr::logging(const QString& key) const
{
return loggingData.value(key, "");
}
const QStringList& ConfigMgr::scripts() const
{
return scriptsData;
}
void ConfigMgr::setGeometry(const QString& key, const QString& value)
{
if (geometryData.contains(key))
geometryData.insert(key, value);
}
void ConfigMgr::setConnection(const QString& key, const QString& value)
{
if (connectionData.contains(key))
connectionData.insert(key, value);
}
void ConfigMgr::setCommon(const QString& key, const QString& value)
{
if (commonData.contains(key))
commonData.insert(key, value);
}
void ConfigMgr::setLogging(const QString& key, const QString& value)
{
if (loggingData.contains(key))
loggingData.insert(key, value);
}
void ConfigMgr::setColorPalette(const QHash<QString, QString>& palette)
{
colorData = palette;
}
void ConfigMgr::setPrefixColorPalette(const QVector<std::pair<QChar, QColor>>& palette)
{
prefixColorData = palette;
}
void ConfigMgr::addScript(const QString& path)
{
scriptsData << path;
}
void ConfigMgr::delScript(const QString& path)
{
scriptsData.removeAll(path);
}
void ConfigMgr::loadGeometry(IniFile& ini)
{
geometryData.insert("X", ini.value("Geometry", "X", "-1"));
geometryData.insert("Y", ini.value("Geometry", "Y", "-1"));
geometryData.insert("Width", ini.value("Geometry", "Width", "1000"));
geometryData.insert("Height", ini.value("Geometry", "Height", "768"));
}
void ConfigMgr::loadConnection(IniFile& ini)
{
connectionData.insert("Realname", ini.value("Connection", "Realname"));
connectionData.insert("Username", ini.value("Connection", "Username"));
connectionData.insert("Nickname", ini.value("Connection", "Nickname"));
connectionData.insert("AltNickname", ini.value("Connection", "AltNickname"));
connectionData.insert("Server", ini.value("Connection", "Server"));
connectionData.insert("Password", ini.value("Connection", "Password"));
connectionData.insert("SSL", ini.value("Connection", "SSL", "0"));
}
void ConfigMgr::loadCommon(IniFile& ini)
{
#if defined(Q_OS_WIN32) | defined(Q_OS_WIN64)
QString defaultFontName = "Fixedsys";
#else
QString defaultFontName = "Monospace";
#endif
commonData.insert("ShowOptions", ini.value("Common", "ShowOptions", "1"));
commonData.insert("Reconnect", ini.value("Common", "Reconnect", "0"));
commonData.insert("RejoinChannelsOnConnect", ini.value("Common", "RejoinChannelsOnConnect", "0"));
commonData.insert("ShowWhoisActiveWindow", ini.value("Common", "ShowWhoisActiveWindow", "1"));
commonData.insert("ShowModeInMessage", ini.value("Common", "ShowModeInMessage", "1"));
commonData.insert("TrayNotify", ini.value("Common", "TrayNotify", "1"));
commonData.insert("TrayNotifyDelay", ini.value("Common", "TrayNotifyDelay", "5"));
commonData.insert("ShowTimestamp", ini.value("Common", "ShowTimestamp", "1"));
commonData.insert("TimestampFormat", ini.value("Common", "TimestampFormat", "[HH:mm]"));
commonData.insert("QuitMessage", ini.value("Common", "QuitMessage"));
commonData.insert("Font", ini.value("Common", "Font", defaultFontName));
commonData.insert("FontSize", ini.value("Common", "FontSize", "12"));
commonData.insert("BgImageEnabled", ini.value("Common", "BgImageEnabled", "0"));
commonData.insert("BgImagePath", ini.value("Common", "BgImagePath"));
commonData.insert("BgImageOpacity", ini.value("Common", "BgImageOpacity", "100"));
commonData.insert("BgImageScaling", ini.value("Common", "BgImageScaling"));
commonData.insert("SSLSelfsigned", ini.value("Common", "SSLSelfSigned", "0"));
commonData.insert("SSLExpired", "0");
commonData.insert("SSLCNMismatch", ini.value("Common", "SSLCNMismatch", "0"));
}
void ConfigMgr::loadColor(IniFile& ini)
{
colorData.insert("Action", ini.value("Color", "Action", "#840084"));
colorData.insert("CTCP", ini.value("Color", "CTCP", "#FF0000"));
colorData.insert("Highlight", ini.value("Color", "Highlight", "#848400"));
colorData.insert("Invite", ini.value("Color", "Invite", "#008400"));
colorData.insert("Join", ini.value("Color", "Join", "#008400"));
colorData.insert("Kick", ini.value("Color", "Kick", "#008400"));
colorData.insert("Mode", ini.value("Color", "Mode", "#008400"));
colorData.insert("Nick", ini.value("Color", "Nick", "#008400"));
colorData.insert("Normal", ini.value("Color", "Normal", "#000000"));
colorData.insert("Notice", ini.value("Color", "Notice", "#840000"));
colorData.insert("OwnText", ini.value("Color", "OwnText", "#008484"));
colorData.insert("Part", ini.value("Color", "Part", "#008400"));
colorData.insert("ProgramInfo", ini.value("Color", "ProgramInfo", "#000084"));
colorData.insert("Quit", ini.value("Color", "Quit", "#000084"));
colorData.insert("ServerInfo", ini.value("Color", "ServerInfo", "#008400"));
colorData.insert("Topic", ini.value("Color", "Topic", "#008400"));
colorData.insert("Wallops", ini.value("Color", "Wallops", "#FF0000"));
colorData.insert("TextviewBackground", ini.value("Color", "TextviewBackground", "#FFFFFF"));
colorData.insert("InputBackground", ini.value("Color", "InputBackground", "#FFFFFF"));
colorData.insert("InputForeground", ini.value("Color", "InputForeground", "#000000"));
colorData.insert("ListboxBackground", ini.value("Color", "ListboxBackground", "#FFFFFF"));
colorData.insert("ListboxForeground", ini.value("Color", "ListboxForeground", "#000000"));
colorData.insert("Links", ini.value("Color", "Links", "#0000FF"));
}
void ConfigMgr::loadPrefixColor(IniFile& ini)
{
QString defaultColor = color("ListboxForeground");
const int ItemCount = ini.countItems("PrefixColor");
for (int i = 0; i < ItemCount; ++i) {
int key = ini.key("PrefixColor", i).toInt(); // Stored as ascii-value of given prefix
QColor color(ini.value("PrefixColor", i, defaultColor));
QChar prefix = static_cast<char>(key);
prefixColorData.push_back(std::make_pair(prefix, color));
}
}
void ConfigMgr::loadLogging(IniFile &ini)
{
loggingData.insert("Channels", ini.value("Logging", "Channels", "0"));
loggingData.insert("Privates", ini.value("Logging", "Privates", "0"));
loggingData.insert("Path", ini.value("Logging", "Path"));
}
void ConfigMgr::loadScripts(IniFile& ini)
{
scriptsData.clear();
const int slen = ini.countItems("Scripts");
for (int i = 0; i < slen; ++i) {
scriptsData << ini.value("Scripts", i);
}
}
void ConfigMgr::saveGeometry(IniFile& ini)
{
QHashIterator<QString,QString> it(geometryData);
while (it.hasNext()) {
it.next();
ini.write("Geometry", it.key(), it.value());
}
}
void ConfigMgr::saveConnection(IniFile& ini)
{
QHashIterator<QString,QString> it(connectionData);
while (it.hasNext()) {
it.next();
ini.write("Connection", it.key(), it.value());
}
}
void ConfigMgr::saveCommon(IniFile& ini)
{
QHashIterator<QString,QString> it(commonData);
while (it.hasNext()) {
it.next();
ini.write("Common", it.key(), it.value());
}
}
void ConfigMgr::saveColor(IniFile& ini)
{
QHashIterator<QString,QString> it(colorData);
while (it.hasNext()) {
it.next();
ini.write("Color", it.key(), it.value());
}
}
void ConfigMgr::savePrefixColor(IniFile& ini)
{
for (const auto& pair : prefixColorData) {
int keynum = pair.first.toLatin1();
ini.write("PrefixColor", QString::number(keynum), pair.second.name());
}
}
void ConfigMgr::saveLogging(IniFile& ini)
{
QHashIterator<QString,QString> it(loggingData);
while (it.hasNext()) {
it.next();
ini.write("Logging", it.key(), it.value());
}
}
void ConfigMgr::saveScripts(IniFile& ini)
{
ini.delSection("Scripts");
for (int i = 0; i < scriptsData.length(); ++i)
ini.write("Scripts", QString::number(i), scriptsData[i]);
}

@ -0,0 +1,71 @@
#ifndef CONFIGMGR_H
#define CONFIGMGR_H
#include "IniFile.h"
#include <QObject>
#include <QString>
#include <QStringList>
#include <QHash>
#include <QVector>
#include <QColor>
#include <optional>
class ConfigMgr : public QObject
{
Q_OBJECT
public:
static ConfigMgr& instance();
void load();
void save();
// One getter per section
QString geometry(const QString& key) const;
QString connection(const QString& key) const;
QString common(const QString& key) const;
QString color(const QString& key) const;
std::optional<QColor> prefixColor(const QChar prefix) const;
QHash<QString,QString> color() const;
QVector<std::pair<QChar,QColor>> prefixColor() const;
QString logging(const QString& key) const;
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 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();
void loadGeometry(IniFile& ini);
void loadConnection(IniFile& ini);
void loadCommon(IniFile& ini);
void loadColor(IniFile& ini);
void loadPrefixColor(IniFile& ini);
void loadLogging(IniFile& ini);
void loadScripts(IniFile& ini);
void saveGeometry(IniFile& ini);
void saveConnection(IniFile& ini);
void saveCommon(IniFile& ini);
void saveColor(IniFile& ini);
void savePrefixColor(IniFile& ini);
void saveLogging(IniFile& ini);
void saveScripts(IniFile& ini);
QHash<QString,QString> geometryData;
QHash<QString,QString> connectionData;
QHash<QString,QString> commonData;
QHash<QString,QString> colorData;
QVector<std::pair<QChar,QColor>> prefixColorData;
QHash<QString,QString> loggingData;
QStringList scriptsData;
signals:
void saved();
};
#endif // CONFIGMGR_H

@ -0,0 +1,599 @@
#include "ICommand.h"
#include "Commands.h"
#include "IRC.h"
#include "IRCClient/IRCMember.h"
#include "IRCClient/IRCChannel.h"
#include "IWin/IWinStatus.h"
#include "MdiManager.h"
#include "Script/Manager.h"
#include "fmt/format.h"
namespace {
constexpr char CTCPflag { 0x01 };
}
ICommand::ICommand(IRC& connection_)
: connection(connection_)
, status(connection_.getStatus())
{}
void ICommand::parse(QString text)
{
if (text.isEmpty())
return;
if (text[0] == '/')
text = text.mid(1);
if (tryParseIRC(text) || tryParseInternal(text))
return;
else if (ScriptManager::instance()->runCommand(text))
return;
else {
const int nextSpace = text.indexOf(' ');
if (nextSpace == -1)
connection.raw(text.toUpper().toStdString());
else {
QString command = text.mid(0, nextSpace).toUpper();
QString out = QString("%1 %2")
.arg(command)
.arg(text.mid(nextSpace + 1));
connection.raw(out.toStdString());
}
}
}
IRC& ICommand::getConnection()
{
return connection;
}
bool ICommand::tryParseIRC(QString cmdLine)
{
int spaceidx = cmdLine.indexOf(' ') > -1 ?: cmdLine.length();
const QString command = cmdLine.mid(0, spaceidx).toUpper();
cmdLine.remove(0, spaceidx);
auto& mdi = MdiManager::instance();
using namespace Command::IRC; // For the string constants below.
if (command == PASS) {
CommandData argv(cmdLine, { &ICommand::prd_Message });
if (!argv[0])
print_notEnoughParameters(command);
else
connection.command(PASS, *argv[0]);
return true;
}
else if (command == NICK) {
CommandData argv(cmdLine, { &ICommand::prd_Message });
if (!argv[0])
print_notEnoughParameters(command);
else {
if (!connection.isOnline())
connection.setNickname(*argv[0]);
connection.command(NICK, *argv[0]);
}
return true;
}
else if (command == OPER) {
CommandData argv(cmdLine, { &ICommand::prd_AnyWord, &ICommand::prd_AnyWord });
if (argv.size() < 2)
print_notEnoughParameters(command);
else {
connection.command(OPER, { *argv[0], *argv[1] });
}
return true;
}
else if (command == MODE) {
CommandData argv(cmdLine, { &ICommand::prd_AnyWord, &ICommand::prd_Message });
connection.raw(fmt::format("{} {}", MODE, argv.joinString(' ')));
return true;
}
else if (command == QUIT) {
CommandData argv(cmdLine, { &ICommand::prd_Message });
connection.expectDisconnect();
connection.command(QUIT, *argv[0]);
return true;
}
else if (command == SQUIT) {
CommandData argv(cmdLine, { &ICommand::prd_AnyWord, &ICommand::prd_Message });
if (!argv[0]) {
print_notEnoughParameters(command);
return true;
}
connection.command(SQUIT, { *argv[0] }, *argv[1]);
return true;
}
else if (command == JOIN) {
CommandData argv(cmdLine, { &ICommand::prd_AnyWord, &ICommand::prd_AnyWord });
if (!argv[0]) {
print_notEnoughParameters(command);
return true;
}
connection.command(JOIN, { *argv[0], argv[1].value_or("") });
return true;
}
else if (command == PART) {
CommandData argv(cmdLine, { &ICommand::prd_AnyWord, &ICommand::prd_Message });
if (!argv[0]) {
print_notEnoughParameters(command);
return true;
}
connection.command(PART, { *argv[0] }, argv[1].value_or(""));
return true;
}
else if (command == TOPIC) {
CommandData argv(cmdLine, { &ICommand::prd_Switch, &ICommand::prd_AnyWord, &ICommand::prd_Message });
if (!argv[1]) {
print_notEnoughParameters(command);
return true;
}
if (argv[0].value_or("") == "-c") {
connection.command(TOPIC, { *argv[1] }, "");
return true;
}
if (argv[2])
connection.command(TOPIC, { *argv[1] }, *argv[2]);
else
connection.command(TOPIC, { *argv[1] });
return true;
}
else if (command == NAMES) {
CommandData argv(cmdLine, { &ICommand::prd_AnyWord });
if (!argv[0]) {
print_notEnoughParameters(command);
return true;
}
connection.command(NAMES, { *argv[0] });
return true;
}
else if (command == LIST) {
CommandData argv(cmdLine, { &ICommand::prd_AnyWord });
connection.command(LIST, { argv[0].value_or("") });
return true;
}
else if (command == INVITE) {
CommandData argv(cmdLine, { &ICommand::prd_AnyWord, &ICommand::prd_AnyWord });
if (!argv[0] || !argv[1]) {
print_notEnoughParameters(command);
return true;
}
connection.command(INVITE, { *argv[0], *argv[1] });
return true;
}
else if (command == KICK) {
CommandData argv(cmdLine, { &ICommand::prd_OptionalChannel, &ICommand::prd_AnyWord, &ICommand::prd_Message });
if (!argv[1]) {
print_notEnoughParameters(command);
return true;
}
std::string channel = argv[0].value_or("");
if (!argv[0]) {
if (mdi.currentWindow()->getType() != IWin::Type::Channel) {
mdi.currentWindow()->print(PrintType::ProgramInfo, "Not in a channel window");
return true;
}
channel = mdi.currentWindow()->getButtonText().toStdString();
}
connection.command(KICK, { channel, *argv[1] }, argv[2].value_or(""));
return true;
}
else if (command == PRIVMSG) {
CommandData argv(cmdLine, { &ICommand::prd_AnyWord, &ICommand::prd_Message });
if (!argv[0]) {
print_notEnoughParameters(command);
return true;
}
connection.command(PRIVMSG, { *argv[0] }, argv[1].value_or(""));
IWin* win = mdi.findWindow(&status, argv[0]->c_str());
if (win) {
const auto* myNick = connection.getNickname().c_str();
const auto* msg = argv[1] ? argv[1]->c_str() : "";
win->print(PrintType::OwnText, QStringLiteral("<%1> %2")
.arg(myNick)
.arg(msg));
}
else {
const auto* msg = argv[1] ? argv[1]->c_str() : "";
status.print(PrintType::OwnText, tr(">%1< %2")
.arg(argv[0]->c_str())
.arg(msg));
}
return true;
}
else if (command == NOTICE) {
CommandData argv(cmdLine, { &ICommand::prd_AnyWord, &ICommand::prd_Message });
if (!argv[0]) {
print_notEnoughParameters(command);
return true;
}
connection.command(NOTICE, { *argv[0] }, argv[1].value_or(""));
IWin* win = mdi.findWindow(&status, argv[0]->c_str());
if (!win)
win = &status;
const auto* msg = argv[1] ? argv[1]-