#133 Better model for handling servers; using json (servers.json) for storing servers

master
Tomatix 4 years ago
parent d032da0ce6
commit f4a95a8eb1
  1. 2
      CMakeLists.txt
  2. 4
      IConfig/CMakeLists.txt
  3. 45
      IConfig/IConfigServers.cpp
  4. 3
      IConfig/IConfigServers.h
  5. 295
      IConfig/ServerEditor.cpp
  6. 31
      IConfig/ServerEditor.h
  7. 286
      IConfig/ServerEditor.ui
  8. 38
      IConfig/ServerItem.cpp
  9. 29
      IConfig/ServerItem.h
  10. 133
      IConfig/ServerMgr.cpp
  11. 66
      IConfig/ServerMgr.h
  12. 387
      IConfig/ServerModel.cpp
  13. 51
      IConfig/ServerModel.h

@ -9,7 +9,7 @@ set(BUILD_TYPE "packaged")
set(VERSION_MAJOR 1)
set(VERSION_MINOR 1)
set(VERSION_PATCH 0)
set(VERSION_APPEND "dev5")
set(VERSION_APPEND "dev6")
#
# CMake build environment setup

@ -18,8 +18,8 @@ list(APPEND ${component}_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/ServerEditor.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ServerEditor.h
${CMAKE_CURRENT_SOURCE_DIR}/ServerEditor.ui
${CMAKE_CURRENT_SOURCE_DIR}/ServerMgr.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ServerMgr.h
${CMAKE_CURRENT_SOURCE_DIR}/ServerItem.h
${CMAKE_CURRENT_SOURCE_DIR}/ServerItem.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ServerModel.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ServerModel.h
)

@ -8,6 +8,7 @@
#include "IConfigServers.h"
#include "ui_IConfigServers.h"
#include "ConfigMgr.h"
#include "ServerItem.h"
#include <QDebug>
IConfigServers::IConfigServers(QWidget *parent) :
@ -33,14 +34,7 @@ IConfigServers::IConfigServers(QWidget *parent) :
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);
}
IConfigServers::~IConfigServers()
@ -107,30 +101,27 @@ void IConfigServers::on_btnShowPassword_toggled(bool checked)
void IConfigServers::on_btnEditServers_clicked()
{
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;
/*
editor = new ServerEditor(smodel, this);
ui->servers->setModel(&smodel);
connect(editor, &ServerEditor::saveClicked,
this, &IConfigServers::onServerEditorSaved);
if (details.contains('|')) {
password = details.split('|')[1];
details.remove("|"+password);
}
QModelIndex sindex = smodel.indexFromHost(cf_Server);
if (sindex.isValid())
ui->servers->selectionModel()->setCurrentIndex(sindex, QItemSelectionModel::Select | QItemSelectionModel::Rows);
bool ssl = details[0] == '$';
if (ssl)
details = details.mid(1);
editor->show();
*/
}
server = details;
void IConfigServers::on_servers_clicked(const QModelIndex& index)
{
auto* item = static_cast<const ServerItem*>(index.internalPointer());
ui->edServer->setText(server);
ui->edServerPassword->setText(password);
ui->chkSSL->setChecked(ssl);
ui->edServer->setText( item->address() );
ui->edServerPassword->setText( item->password() );
ui->chkSSL->setChecked( item->ssl() );
}
void IConfigServers::onServerEditorSaved()

@ -32,13 +32,12 @@ public:
private slots:
void on_btnShowPassword_toggled(bool checked);
void on_btnEditServers_clicked();
void on_servers_clicked(const QModelIndex &index);
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;

@ -1,6 +1,6 @@
/*
* IdealIRC - Internet Relay Chat client
* Copyright (C) 2021 Tom-Andre Barstad.
* Copyright (C) 2022 Tom-Andre Barstad.
* This software is licensed under the Software Attribution License.
* See LICENSE for more information.
*/
@ -11,26 +11,12 @@
#include <QMessageBox>
#include <QInputDialog>
ServerEditor::ServerEditor(ServerModel& model, QWidget* parent)
ServerEditor::ServerEditor(QWidget* parent)
: 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);
}
}
ServerEditor::~ServerEditor()
@ -38,293 +24,20 @@ ServerEditor::~ServerEditor()
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);
}
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);
}
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();
}
void ServerEditor::on_btnShowPassword_toggled(bool checked)
{
ui->edPassword->setEchoMode(checked ? QLineEdit::Normal : QLineEdit::Password);
}
void ServerEditor::on_btnSave_clicked()
{
if (editMode == EditMode::Off)
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 (editMode == EditMode::Server)
saveServerEdit();
else if (editMode == EditMode::Network)
saveNetworkEdit();
emit saveClicked();
}
void ServerEditor::enableForServer()
{
enableAll();
ui->lbNetwork->show();
ui->edNetwork->show();
editMode = EditMode::Server;
}
void ServerEditor::enableForNetwork()
{
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);
}
void ServerEditor::disableAll()
void ServerEditor::on_btnClose_clicked()
{
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);
}
void ServerEditor::saveServerEdit()
void ServerEditor::on_btnShowPassword_triggered(QAction*)
{
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();
if (ui->chkSSL->isChecked())
host.prepend('$');
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);
}
}
void ServerEditor::selectItem(const QModelIndex& 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;
// Value format: $host:port|password
// Presence of '$' determines if it's an SSL server.
bool ssl = details[0] == '$';
if (ssl)
details = details.mid(1);
if (details.contains('|')) {
password = details.split('|')[1];
details = details.remove("|"+password);
}
if (details.contains(':')) {
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->chkSSL->setChecked(ssl);
int networkUiIndex = ui->edNetwork->findText(spair.first);
if (networkUiIndex == -1)
networkUiIndex = 0;
ui->edNetwork->setCurrentIndex(networkUiIndex);
}

@ -1,6 +1,6 @@
/*
* IdealIRC - Internet Relay Chat client
* Copyright (C) 2021 Tom-Andre Barstad.
* Copyright (C) 2022 Tom-Andre Barstad.
* This software is licensed under the Software Attribution License.
* See LICENSE for more information.
*/
@ -21,42 +21,19 @@ class ServerEditor : public QDialog
Q_OBJECT
public:
explicit ServerEditor(ServerModel& model, QWidget *parent = nullptr);
explicit ServerEditor(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_btnClose_clicked();
void on_btnShowPassword_triggered(QAction*);
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;
};
#endif // SERVEREDITOR_H

@ -9,220 +9,152 @@
<rect>
<x>0</x>
<y>0</y>
<width>370</width>
<height>450</height>
<width>245</width>
<height>209</height>
</rect>
</property>
<property name="windowTitle">
<string>Server editor</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0" rowspan="3" colspan="2">
<widget class="QTreeView" name="serverView">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Port</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QToolButton" name="btnAdd">
<property name="text">
<string>+</string>
<item row="4" column="1" colspan="2">
<widget class="QComboBox" name="edNetwork">
<property name="enabled">
<bool>false</bool>
</property>
<property name="editable">
<bool>true</bool>
</property>
<property name="popupMode">
<enum>QToolButton::InstantPopup</enum>
<property name="insertPolicy">
<enum>QComboBox::InsertAlphabetically</enum>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="btnDel">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>-</string>
<string>Name</string>
</property>
</widget>
</item>
<item row="2" column="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>178</height>
</size>
</property>
</spacer>
<item row="1" column="1" colspan="2">
<widget class="QLineEdit" name="edHostname">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="4" column="0">
<widget class="QLabel" name="lbNetwork">
<property name="text">
<string>Network</string>
</property>
</widget>
</item>
<item row="4" column="1" colspan="3">
<widget class="QComboBox" name="edNetwork">
<property name="enabled">
<bool>false</bool>
</property>
<property name="editable">
<bool>true</bool>
</property>
<property name="insertPolicy">
<enum>QComboBox::InsertAlphabetically</enum>
</property>
</widget>
</item>
<item row="5" column="3">
<widget class="QPushButton" name="btnSave">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="3">
<widget class="QLineEdit" name="edHostname">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="1" colspan="3">
<widget class="QSpinBox" name="edPort">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>6667</number>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QLineEdit" name="edName">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Hostname</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Password</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="3">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="edPassword">
<property name="enabled">
<bool>false</bool>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnShowPassword">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Show</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Port</string>
</property>
</widget>
</item>
<item row="5" column="1" colspan="2">
<widget class="QCheckBox" name="chkSSL">
<property name="text">
<string>Use SSL</string>
</property>
</widget>
</item>
</layout>
<item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="edName">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<item row="3" column="1" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="edPassword">
<property name="enabled">
<bool>false</bool>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnShowPassword">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Show</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1" colspan="2">
<widget class="QSpinBox" name="edPort">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>230</width>
<height>20</height>
</size>
<property name="maximum">
<number>65535</number>
</property>
</spacer>
<property name="value">
<number>6667</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Password</string>
</property>
</widget>
</item>
<item row="4" column="1">
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Hostname</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="lbNetwork">
<property name="text">
<string>Network</string>
</property>
</widget>
</item>
<item row="6" column="2">
<widget class="QPushButton" name="btnClose">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QPushButton" name="btnSave">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="chkSSL">
<property name="text">
<string>Use SSL</string>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>serverView</tabstop>
<tabstop>btnAdd</tabstop>
<tabstop>btnDel</tabstop>
<tabstop>edName</tabstop>
<tabstop>edHostname</tabstop>
<tabstop>edPort</tabstop>
<tabstop>edPassword</tabstop>
<tabstop>btnShowPassword</tabstop>
<tabstop>edNetwork</tabstop>
<tabstop>chkSSL</tabstop>
<tabstop>btnSave</tabstop>
<tabstop>btnClose</tabstop>
</tabstops>
<resources/>
<connections>

@ -0,0 +1,38 @@
#include "ServerItem.h"
namespace Key {
constexpr auto Name = "Name";
constexpr auto Host = "Host";
constexpr auto Port = "Port";
constexpr auto Password = "Password";
constexpr auto SSL = "SSL";
}
ServerItem::ServerItem(const QJsonObject& data, int row, ServerItem* parentItem)
: m_data(data)
, m_row(row)
, m_parent(parentItem)
{}
QString ServerItem::name() const
{
return m_data[Key::Name].toString();
}
QString ServerItem::address() const
{
const auto host{ m_data[Key::Host].toString() };
const auto port{ m_data[Key::Port].toInt() };
return QStringLiteral("%1:%2").arg(host).arg(port);
}
QString ServerItem::password() const
{
return m_data[Key::Password].toString();
}
bool ServerItem::ssl() const
{
return m_data[Key::SSL].toBool();
}

@ -0,0 +1,29 @@
#ifndef SERVERITEM_H
#define SERVERITEM_H
#include <QList>
#include <QString>
#include <QJsonObject>
class ServerItem
{
public:
ServerItem(const QJsonObject& data, int row, ServerItem* parentItem);
QString name() const;
QString address() const;
QString password() const;
bool ssl() const;
ServerItem* parent() const { return m_parent; }
int row() const { return m_row; }
QList<ServerItem>& children() { return m_children; }
private:
QJsonObject m_data;
QList<ServerItem> m_children;
int m_row;
ServerItem* m_parent;
};
#endif // SERVERITEM_H

@ -1,133 +0,0 @@
/*
* IdealIRC - Internet Relay Chat client
* Copyright (C) 2021 Tom-Andre Barstad.
* This software is licensed under the Software Attribution License.
* See LICENSE for more information.
*/
#include "ServerMgr.h"
#include "config.h"
#include <fmt/format.h>
ServerMgr::ServerMgr(QObject *parent) :
QObject(parent),
ini( QString(LOCAL_PATH+"/servers.ini").toStdString() )
{
}
QStringList ServerMgr::networkList()
{
int count = ini.count();
QStringList r;
for (int i = 0; i < count; i++) {
const auto name = QString::fromStdString( ini.section(i) );
r.push_back(name);
}
return r;
}
QHash<QString,QString> ServerMgr::serverList(const QString& network)
{
const auto networkStr = network.toStdString();
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) );
r.insert(servername, serverdetails);
}
return r;
}
QString ServerMgr::defaultServer(const QString& network)
{
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;
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;
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))
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);
ini.write("NONE", item, value);
}
}
ini.remove(nameStr);
}
bool ServerMgr::addServer(const QString& name, const QString& host, const QString& pw, const QString& network)
{
QString details;
if (pw.isEmpty()) {
details = host;
}
else {
details = QString("%1|%2")
.arg(host)
.arg(pw);
}
const auto networkStr = network.toStdString();
if (!ini.exist(networkStr) && networkStr != "NONE")
return false;
ini.write(networkStr, name.toStdString(), details.toStdString());
return true;
}
void ServerMgr::delServer(const QString& name, const QString& network)
{
ini.remove(network.toStdString(), name.toStdString());
}
bool ServerMgr::hasNetwork(const QString& name)
{
return ini.exist(name.toStdString());
}
bool ServerMgr::hasServer(const QString& name, const QString& network)
{
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()) );
}

@ -1,66 +0,0 @@
/*
* IdealIRC - Internet Relay Chat client
* Copyright (C) 2021 Tom-Andre Barstad.
* This software is licensed under the Software Attribution License.
* See LICENSE for more information.
*/
#ifndef SERVERMGR_H
#define SERVERMGR_H
#include <QObject>
#include <QStringList>
#include <QHash>
#include "IniFile.h"
/**
* @brief Manages servers.ini
* @details
* This class is from IdealIRC 0.x series and is going to be removed/replaced at any time.
*/
class ServerMgr : public QObject
{
Q_OBJECT
public:
explicit ServerMgr(QObject *parent = nullptr);
// All networks in a string list (Also counts in the NONE network)
QStringList networkList();
// All servers from a network in a hash map <"name","server:port|passwd">
QHash<QString,QString> serverList(const QString& network = "NONE");
// Return default server of given network (The "NONE" network have no default!) - returns empty if no default server is set.
QString defaultServer(const QString& network);
// Add new network to servers.ini - returns false if network exist
bool addNetwork(const QString& name);
// Rename a network - returns false if new network name already exist
bool renameNetwork(const QString& o_name, const QString& n_name);
// Delete network
void delNetwork(const QString& name, bool keep_servers = false);
// Add (or update) a server to network - returns false if network doesn't exsist
bool addServer(const QString& name, const QString& host /*host:port*/, const QString& pw = "", const QString& network = "NONE");
// Delete a server from network
void delServer(const QString& name, const QString& network = "NONE");
// Check of we have the given network name
bool hasNetwork(const QString& name);
// Check if we have the given server name inside the network
bool hasServer(const QString& name, const QString& network = "NONE");
// Get server details
QString getServerDetails(const QString& name, const QString& network = "NONE");
private:
IniFile ini;
};
#endif // SERVERMGR_H

@ -1,366 +1,137 @@
/*
* IdealIRC - Internet Relay Chat client
* Copyright (C) 2021 Tom-Andre Barstad.
* Copyright (C) 2022 Tom-Andre Barstad.
* This software is licensed under the Software Attribution License.
* See LICENSE for more information.
*/
#include "ServerModel.h"
#include <QHashIterator>
#include "config.h"
#include <QJsonArray>
#include <QJsonValue>
#include <QFile>
#include <QDebug>
ServerModel::ServerModel(QObject *parent) :
QStandardItemModel(parent)
ServerModel::ServerModel(QObject* parent) :
QAbstractItemModel(parent)
{
resetModel();
}
QJsonDocument jsondoc;
QModelIndex ServerModel::indexFromHost(QString hostname)
{
return hostmap.value(hostname, QModelIndex());
}
const auto path{ QStringLiteral("%1/servers.json").arg(LOCAL_PATH) };
QFile f{ path };
if (f.open(QIODevice::ReadOnly)) {
const auto jsonData{ f.readAll() };
qDebug() << "Read" << jsonData.size() << "bytes from" << path;
jsondoc = QJsonDocument::fromJson(jsonData);
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" };
f.close();
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() };
}
if (!jsondoc.isObject())
qWarning() << "Parse error for servers.json";
else
createItems(jsondoc.object());
}
else
qWarning() << "Unable to open servers.json:" << path;
return {};
}
QModelIndex ServerModel::addNetwork(QString name, QString server)
void ServerModel::createItems(const QJsonObject& json)
{
QStandardItem *root = invisibleRootItem();
constexpr auto Servers = "Servers";
constexpr auto Networks = "Networks";
QStandardItem *pname = new QStandardItem(QIcon(":/options/gfx/network.png"), name);
QStandardItem *phost = new QStandardItem(server);
QList<QStandardItem*> list;
list << pname << phost;
const auto servers = json[Servers].toArray();
const auto networks = json[Networks].toArray();
root->appendRow(list);
hostmap.insert(server, pname->index());
netmap.insert(name, pname->index());
int rootRow{ 0 };
if (smgr.addNetwork(name))
smgr.addServer("DEFAULT", server, "", name);
for (const auto& server : servers) {
m_items << ServerItem(server.toObject(), rootRow++, nullptr);
}
return pname->index();
}
for (const auto& network : networks) {
const auto networkObj = network.toObject();
const auto childServers = networkObj[Servers].toArray();
void ServerModel::setNetworkServer(QString name, QString server)
{
QModelIndex current = netmap.value(name);
int row = current.row();
QModelIndex serverIndex = index(row, 1, current.parent());
m_items << ServerItem(networkObj, rootRow++, nullptr);
auto& networkItem = m_items.back();
QStandardItem *item = itemFromIndex(serverIndex);
item->setText(server);
smgr.addServer("DEFAULT", server, "", name);
int childRow{ 0 };
for (const auto& cs : childServers) {
auto& networkServers = networkItem.children();
networkServers << ServerItem(cs.toObject(), childRow++, &networkItem);
}
}
}
void ServerModel::renameNetwork(QString name, QString newname)
QVariant ServerModel::data(const QModelIndex& index, int role) const
{
QModelIndex current = netmap.value(name);
int row = current.row();
QModelIndex nameIndex = index(row, 0, current.parent());
QStandardItem *item = itemFromIndex(nameIndex);
item->setText(newname);
netmap.remove(name);
netmap.insert(newname, current);
smgr.renameNetwork(name, newname);
}
if (!index.isValid() || role != Qt::DisplayRole)
return {};
void ServerModel::delNetwork(QString name, bool keepServers)
{
smgr.delNetwork(name, keepServers);
resetModel();
}
auto* item = static_cast<const ServerItem*>(index.internalPointer());
QModelIndex ServerModel::addServer(QString name, QString server, QString network)
{
QStandardItem *parent;
if (index.column() == 0)
return item->name();
if (network.length() == 0)
network = "NONE";
else if (index.column() == 1)
return item->address();
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;
parent->appendRow(list);
hostmap.insert(server, indexFromItem(sname));
if (network == "NONE")
nonemap.insert(name, indexFromItem(sname));
smgr.addServer(name, server, "", network);
return indexFromItem(sname);
return {};
}
void ServerModel::setServer(QString name, QString server, QString password, QString network)
QVariant ServerModel::headerData(int section, Qt::Orientation orientation, int role) const
{
QStandardItem *parent;
if (network.length() == 0)
network = "NONE";
if (network == "NONE")
parent = invisibleRootItem();
else
parent = itemFromIndex( netmap.value(network) );
QString host = server;
if (host[0] == '$')
host = host.mid(1);
QModelIndex parentIdx = indexFromItem(parent); // Parent index
QModelIndex current; // Item's index
if (network != "NONE") {
for (int r = 0 ;; r++) {
QModelIndex idx = parentIdx.child(r, 0);
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
QStringList labels {
tr("Name"),
tr("Address")
};
if (! idx.isValid())
return; // No relevant child found, stop.
if (idx.data().toString() == name) {
current = idx;
break;
}
}
return labels[section];
}
else {
current = nonemap.value(name);
return {};
}
int row = current.row();
QModelIndex nameIndex = index(row, 0, current.parent());
QModelIndex serverIndex = index(row, 1, current.parent());
QStandardItem *serverItem = itemFromIndex(serverIndex);
serverItem->setText(host);
hostmap.insert(host, nameIndex);
smgr.addServer(name, server, password, network);
}
void ServerModel::renameServer(QString name, QString newname, QString network)
int ServerModel::rowCount(const QModelIndex& parent) const
{
QStandardItem *parent;
if (network.length() == 0)
network = "NONE";
if (network == "NONE")
parent = invisibleRootItem();
else
parent = itemFromIndex( netmap.value(network) );
QModelIndex parentIdx = indexFromItem(parent); // Parent index
QModelIndex current; // Item's index
if (network != "NONE") {
for (int r = 0 ;; r++) {
QModelIndex idx = parentIdx.child(r, 0);
if (! idx.isValid())
return; // No relevant child found, stop.
if (idx.data().toString() == name) {
current = idx;
break;
}
}
}
if (!parent.isValid())
return m_items.count();
else {
current = nonemap.value(name);
nonemap.remove(name);
nonemap.insert(newname, current);
auto* parentItem = static_cast<ServerItem*>(parent.internalPointer());
return parentItem->children().count();
}
int row = current.row();
QModelIndex serverIndex = index(row, 0, current.parent());
QStandardItem *item = itemFromIndex(serverIndex);
item->setText(newname);
QString details = smgr.getServerDetails(name, network);
smgr.delServer(name, network);
smgr.addServer(name, details, "", network);
}
void ServerModel::delServer(QString name, QString network)
QModelIndex ServerModel::index(int row, int column, const QModelIndex& parent) const
{
QStandardItem *parent;
if (network.length() == 0)
network = "NONE";
if (network == "NONE")
parent = invisibleRootItem();
else
parent = itemFromIndex( netmap.value(network) );
QModelIndex parentIdx = indexFromItem(parent); // Parent index
QModelIndex current; // Item's index
if (!hasIndex(row, column, parent))
return {};
if (network != "NONE") {
ServerItem* item{};
for (int r = 0 ;; r++) {
QModelIndex idx = parentIdx.child(r, 0);
if (! idx.isValid())
return; // No relevant child found, stop.
if (idx.data().toString() == name) {
current = idx;
break;
}
}
}
if (!parent.isValid())
item = &m_items[row];
else {
current = nonemap.value(name);
nonemap.remove(name);
auto* parentItem = static_cast<ServerItem*>(parent.internalPointer());
item = &parentItem->children()[row];
}
int row = current.row();
removeRow(row, current.parent());
smgr.delServer(name, network);
return createIndex(row, column, item);
}
void ServerModel::resetModel()
QModelIndex ServerModel::parent(const QModelIndex& index) const
{
clear();
QStandardItem *root = invisibleRootItem();
QStandardItem *i = new QStandardItem();
QStringList l;
l << tr("Name") << tr("Host");
setColumnCount(2);
setHorizontalHeaderItem(0, i);
setHorizontalHeaderLabels(l);
hostmap.clear();
netmap.clear();
nonemap.clear();
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 (!index.isValid())
return {};
QString host; // hostname with port, e.g. irc.network.org:6667
host = detail.split('|')[0];
auto* parentItem = static_cast<ServerItem*>(index.internalPointer())->parent();
if (!parentItem)
return {};
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;
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));
}
}
}
QStringList ServerModel::networkList()
{
return smgr.networkList();
}
QString ServerModel::details(QString name, QString network)
{
return smgr.getServerDetails(name, network);
return createIndex(parentItem->row(), 0, parentItem);
}

@ -1,6 +1,6 @@
/*
* IdealIRC - Internet Relay Chat client
* Copyright (C) 2021 Tom-Andre Barstad.
* Copyright (C) 2022 Tom-Andre Barstad.
* This software is licensed under the Software Attribution License.
* See LICENSE for more information.
*/
@ -8,43 +8,38 @@
#ifndef SERVERMODEL_H
#define SERVERMODEL_H
#include <QStandardItemModel>
#include "ServerMgr.h"
#include <QHash>
#include <QPair>
#include "ServerItem.h"
#include <QAbstractItemModel>
#include <QList>
#include <QJsonDocument>
#include <QJsonObject>
/**
* @brief Model of servers.ini
* @details
* This class is from IdealIRC 0.x series and is going to be removed/replaced at any time.
*/
class ServerModel : public QStandardItemModel
class ServerModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit ServerModel(QObject *parent = nullptr);
QModelIndex indexFromHost(QString hostname); // Hostname:Port
QPair<QString,QString> fromIndex(const QModelIndex& index);
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 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");
explicit ServerModel(QObject* parent = nullptr);
private:
ServerMgr smgr;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override
{
return 2;
};
QHash<QString,QModelIndex> hostmap; // host:port to index
QHash<QString,QModelIndex> netmap; // network to index
QHash<QString,QModelIndex> nonemap; // All names (servers) in NONE to index
QModelIndex index(int row, int column, const QModelIndex& parent = {}) const override;
QModelIndex parent(const QModelIndex& index) const override;
private:
void createItems(const QJsonObject& json);
mutable QList<ServerItem> m_items;
};
#endif // SERVERMODEL_H

Loading…
Cancel
Save