The complete source code of IdealIRC http://www.idealirc.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
idealirc/IConfig/ServerModel.cpp

368 lines
9.8 KiB

/*
* IdealIRC - Internet Relay Chat client
* Copyright (C) 2022 Tom-Andre Barstad.
* This software is licensed under the Software Attribution License.
* See LICENSE for more information.
*/
#include "ServerModel.h"
#include "config.h"
#include <QJsonArray>
#include <QFile>
#include <QDebug>
#include <QTimer>
#include <QIcon>
namespace {
QJsonObject createJsonObject(const QString& name, const QString& address, const QString& password, bool isSsl)
{
namespace Key = ServerJsonKey;
const auto hostport{ address.split(':') };
const auto host{ hostport[0] };
const auto port{ hostport.count() > 1 ? hostport[1].toShort()
: (isSsl ? 6697 : 6667) };
QJsonObject obj;
obj[Key::Name] = name;
obj[Key::Host] = host;
obj[Key::Port] = port;
obj[Key::SSL] = isSsl;
obj[Key::Password] = password;
return obj;
}
}
ServerModel::ServerModel(QObject* parent) :
QAbstractItemModel(parent)
{
QJsonDocument jsondoc;
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);
f.close();
if (!jsondoc.isObject())
qWarning() << "Parse error for servers.json";
else
createItems(jsondoc.object());
}
else
qWarning() << "Unable to open servers.json:" << path;
}
void ServerModel::saveToJsonFile(const QString& fileName) const
{
}
Qt::ItemFlags ServerModel::flags(const QModelIndex& index) const
{
/* Don't care about invalid indexes */
if (!index.isValid())
return Qt::NoItemFlags;
/* Columns containing widgets for editing */
if (index.column() > 1)
return Qt::ItemIsEnabled | Qt::ItemIsEditable;
/* Directly editable columns */
Qt::ItemFlags flags{ Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled };
const auto* item = static_cast<const ServerItem*>(index.internalPointer());
if (!item->isNetwork())
flags |= Qt::ItemNeverHasChildren;
return flags;
}
QVariant ServerModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid())
return {};
if (role == Qt::DisplayRole || role == Qt::EditRole) {
const auto* item = static_cast<const ServerItem*>(index.internalPointer());
if (index.column() == 0)
return item->name();
else if (index.column() == 1)
return item->address();
else
return {};
}
if (role == Qt::DecorationRole && index.column() == 0) {
const auto* item = static_cast<const ServerItem*>(index.internalPointer());
if (item->isNetwork())
return QIcon(":/Icons/network.png");
else
return QIcon(":/Icons/serverwindow.png");
}
return {};
}
QVariant ServerModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
QStringList labels {
tr("Name"),
tr("Address"),
"",
""
};
return labels[section];
}
else {
return {};
}
}
bool ServerModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (!index.isValid() || role != Qt::EditRole)
return false;
auto* item = static_cast<ServerItem*>(index.internalPointer());
bool ret{ true };
switch (index.column()) {
case 0:
if (value.toString().isEmpty())
ret = false;
else
item->setName(value.toString());
break;
case 1:
if (value.toString().isEmpty())
ret = false;
else
item->setAddress(value.toString());
break;
default:
ret = false;
}
return ret;
}
int ServerModel::rowCount(const QModelIndex& parent) const
{
if (!parent.isValid())
return m_items.count();
else {
auto* parentItem = static_cast<ServerItem*>(parent.internalPointer());
return parentItem->children().count();
}
}
QModelIndex ServerModel::index(int row, int column, const QModelIndex& parent) const
{
if (!hasIndex(row, column, parent))
return {};
ServerItem* item{};
if (!parent.isValid())
item = &m_items[row];
else {
auto* parentItem = static_cast<ServerItem*>(parent.internalPointer());
item = &parentItem->children()[row];
}
return createIndex(row, column, item);
}
QModelIndex ServerModel::parent(const QModelIndex& index) const
{
if (!index.isValid())
return {};
auto* parentItem = static_cast<ServerItem*>(index.internalPointer())->parent();
if (!parentItem)
return {};
return createIndex(parentItem->row(), 0, parentItem);
}
void ServerModel::addServer(const QString& name, const QString& address, const QString& password, bool isSsl, int networkIdx)
{
QList<ServerItem>* items{};
ServerItem* parentNetworkItem{};
int pos{};
QModelIndex modelIdx;
/* Network server */
if (networkIdx > -1) {
modelIdx = networkIndex(networkIdx);
parentNetworkItem = static_cast<ServerItem*>(modelIdx.internalPointer());
items = &(parentNetworkItem->children());
pos = items->count();
}
/* Non-network server */
else {
items = &m_items;
for (auto& item : m_items) {
if (item.isNetwork())
item.incRow(); // Increment row number for each network, since any non-network server is inserted before these.
else
++pos; // Finds the end position of non-network servers. Add server there (bottom of non-networks; before networks are listed.)
}
}
const auto obj{ createJsonObject(name, address, password, isSsl) };
beginInsertRows(modelIdx, pos, pos);
items->insert(pos, ServerItem(obj, pos, parentNetworkItem, false));
endInsertRows();
emit newEntry(
createIndex(pos, 2, &((*items)[pos])), // SSL
createIndex(pos, 3, &((*items)[pos])) // Password
);
/*
* A hack to make new child items appear if parent's expanded.
* This also "solves" when a new/empty network gets its first entry, the network item (parent) won't get the "expand/collapse" decoration.
* Doing this should not be necessary, so there is an issue with this ServerModel, somewhere...
*/
QTimer::singleShot(1, [this] {
auto pos = m_items.size();
beginInsertRows(QModelIndex{}, pos, pos);
m_items << ServerItem({}, pos, nullptr, false);
endInsertRows();
QTimer::singleShot(1, [this] {
auto pos = m_items.size() - 1;
beginRemoveRows(QModelIndex{}, pos, pos);
m_items.removeLast();
endRemoveRows();
});
});
}
void ServerModel::addNetwork(const QString& name, const QString& address, const QString& password, bool isSsl)
{
const auto obj{ createJsonObject(name, address, password, isSsl) };
const auto idx{ m_items.count() };
beginInsertRows(QModelIndex{}, idx, idx);
m_items << ServerItem(obj, idx, nullptr, true);
endInsertRows();
emit newEntry(
createIndex(idx, 2, &(m_items.back())), // SSL
createIndex(idx, 3, &(m_items.back())) // Password
);
}
void ServerModel::deleteEntry(const QModelIndex& index)
{
auto* item = static_cast<ServerItem*>(index.internalPointer());
auto* parent = item->parent();
QList<ServerItem>* items{};
if (parent == nullptr)
items = &m_items;
else
items = &(parent->children());
beginRemoveRows(index.parent(), index.row(), index.row());
items->removeAt(item->row());
for (int i = item->row(); i < items->count(); ++i)
(*items)[i].decRow();
endRemoveRows();
}
QStringList ServerModel::getNetworks() const
{
QStringList networks;
for (const auto& item : m_items) {
if (item.isNetwork())
networks << item.name();
}
return networks;
}
QList<QModelIndex> ServerModel::getEditorColumns()
{
QList<QModelIndex> indexList;
for (auto& topItem : m_items) {
indexList << createIndex(topItem.row(), 2, &topItem);
indexList << createIndex(topItem.row(), 3, &topItem);
if (topItem.isNetwork()) {
auto& items = topItem.children();
for (auto& item : items) {
indexList << createIndex(item.row(), 2, &item);
indexList << createIndex(item.row(), 3, &item);
}
}
}
return indexList;
}
QModelIndex ServerModel::networkIndex(int row, int col)
{
int rc{ 0 };
for (auto& item : m_items) {
if (!item.isNetwork())
continue;
if (rc == row)
return createIndex(row, col, &item);
else
++rc;
}
return {};
}
void ServerModel::createItems(const QJsonObject& json)
{
namespace Key = ServerJsonKey;
const auto servers = json[Key::Servers].toArray();
const auto networks = json[Key::Networks].toArray();
int rootRow{ 0 };
for (const auto& server : servers) {
m_items << ServerItem(server.toObject(), rootRow++, nullptr, false);
}
for (const auto& network : networks) {
const auto networkObj = network.toObject();
const auto childServers = networkObj[Key::Servers].toArray();
m_items << ServerItem(networkObj, rootRow++, nullptr, true);
auto& networkItem = m_items.back();
int childRow{ 0 };
for (const auto& cs : childServers) {
auto& networkServers = networkItem.children();
networkServers << ServerItem(cs.toObject(), childRow++, &networkItem, false);
}
}
}