New INI file parser.

master
Tomatix 5 years ago
parent cfc1635ce8
commit 1bfef7a257
  1. 9
      IConfig/IConfig.cpp
  2. 2
      IConfig/IConfigOptions.cpp
  3. 12
      IConfig/IConfigServers.cpp
  4. 111
      IConfig/ServerMgr.cpp
  5. 37
      IConfig/ServerMgr.h
  6. 407
      IdealIRC/ConfigMgr.cpp
  7. 31
      IdealIRC/ConfigMgr.h
  8. 779
      IdealIRC/IniFile.cpp
  9. 118
      IdealIRC/IniFile.h

@ -96,8 +96,10 @@ void IConfig::saveAll()
bool IConfig::askForSave()
{
bool changed = servers->isChanged() || options->isChanged() || logging->isChanged();
if (changed) {
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();
@ -154,8 +156,7 @@ void IConfig::on_btnDisconnect_clicked()
void IConfig::on_btnClose_clicked()
{
servers->unsetConnectToNewStatus();
if (askForSave())
close();
close();
}
void IConfig::closeEvent(QCloseEvent* evt)

@ -53,6 +53,8 @@ IConfigOptions::IConfigOptions(QWidget *parent) :
reload();
reset();
cf_Font = ui->edFont->currentFont();
}
IConfigOptions::~IConfigOptions()

@ -61,12 +61,12 @@ IConfigServers::~IConfigServers()
bool IConfigServers::isChanged() const
{
return cf_Realname != ui->edRealName->text()
|| cf_Username != ui->edUsername->text()
|| cf_Nickname != ui->edNickname->text()
|| cf_AltNickame != ui->edAltNickname->text()
|| cf_Server != ui->edServer->text()
|| cf_Password != ui->edServerPassword->text()
|| cf_SSL != ui->chkSSL->isChecked();
|| cf_Username != ui->edUsername->text()
|| cf_Nickname != ui->edNickname->text()
|| cf_AltNickame != ui->edAltNickname->text()
|| cf_Server != ui->edServer->text()
|| cf_Password != ui->edServerPassword->text()
|| cf_SSL != ui->chkSSL->isChecked();
}
bool IConfigServers::connectToNewStatus() const

@ -20,126 +20,127 @@
#include "ServerMgr.h"
#include "config.h"
#include <fmt/format.h>
ServerMgr::ServerMgr(QObject *parent) :
QObject(parent),
ini(LOCAL_PATH+"/servers.ini")
ini( QString(LOCAL_PATH+"/servers.ini").toStdString() )
{
}
QStringList ServerMgr::networkList()
{
int count = ini.countSections();
int count = ini.count();
QStringList r;
for (int i = 0; i < count; i++)
r.push_back( ini.section(i) );
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(QString network)
QHash<QString,QString> ServerMgr::serverList(const QString& network)
{
int count = ini.countItems(network);
const auto networkStr = network.toStdString();
int count = ini.count(networkStr);
QHash<QString,QString> r;
for (int i = 0; i < count; i++) {
QString servername = ini.key(network, i);
QString serverdetails = ini.value(network, i);
// Insert multi in case someone adds a server with same name in the list (f.ex. via editing servers.ini)
r.insertMulti(servername, serverdetails);
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(QString network)
QString ServerMgr::defaultServer(const QString& network)
{
return ini.value(network, "DEFAULT");
return QString::fromStdString( ini.read(network.toStdString(), "DEFAULT") );
}
bool ServerMgr::addNetwork(QString name)
bool ServerMgr::addNetwork(const QString& name)
{
if (name == "NONE")
return false;
const auto nameStr = name.toStdString();
if (! ini.appendSection(name))
if (nameStr == "NONE" || ini.exist(nameStr))
return false;
return ini.write(name, "DEFAULT", "server.name");
ini.write(nameStr, "DEFAULT", "server.name");
return true;
}
bool ServerMgr::renameNetwork(QString o_name, QString n_name)
bool ServerMgr::renameNetwork(const QString& o_name, const QString& n_name)
{
if ((o_name == "NONE") || (n_name == "NONE"))
return false;
return ini.renameSection(o_name, n_name);
auto err = ini.rename(o_name.toStdString(), n_name.toStdString());
return err == IniFile::Error::NoError;
}
bool ServerMgr::delNetwork(QString name, bool keep_servers)
void ServerMgr::delNetwork(const QString& name, bool keep_servers)
{
// If servers=true, we will keep the servers by moving them to the NONE section.
// Any servers which got a name existing in the NONE, will be renamed to oldnetname_servername.
if (! ini.sectionExists(name))
return false;
const auto nameStr = name.toStdString();
if (! ini.exist(nameStr))
return;
if (keep_servers == true) {
int max = ini.countItems(name);
if (keep_servers) {
int max = ini.count(nameStr);
for (int i = 0; i < max; i++) {
QString item = ini.key(name, i);
QString value = ini.value(name, i);
QString e_item = ini.value("NONE", item);
if (e_item.length() > 0) // item exists in NONE
item.prepend(name+"_");
auto item = fmt::format("{}_{}", name.toStdString(), ini.item(nameStr, i));
auto value = ini.read(nameStr, i);
ini.write("NONE", item, value);
}
}
ini.delSection(name);
return true;
ini.remove(nameStr);
}
bool ServerMgr::addServer(QString name, QString host, QString pw, QString network)
bool ServerMgr::addServer(const QString& name, const QString& host, const QString& pw, const QString& network)
{
if (pw.length() > 0)
pw.prepend('|');
QString details;
QString detail = QString("%1%2")
.arg(host)
.arg(pw);
if (pw.isEmpty()) {
details = host;
}
else {
details = QString("%1|%2")
.arg(host)
.arg(pw);
}
const auto networkStr = network.toStdString();
if ((! ini.sectionExists(network)) && (network != "NONE"))
if (!ini.exist(networkStr) && networkStr != "NONE")
return false;
ini.write(network, name, detail);
ini.write(networkStr, name.toStdString(), details.toStdString());
return true;
}
bool ServerMgr::delServer(QString name, QString network)
void ServerMgr::delServer(const QString& name, const QString& network)
{
return ini.delItem(network, name);
ini.remove(network.toStdString(), name.toStdString());
}
bool ServerMgr::hasNetwork(QString name)
bool ServerMgr::hasNetwork(const QString& name)
{
return ini.sectionExists(name);
return ini.exist(name.toStdString());
}
bool ServerMgr::hasServer(QString name, QString network)
bool ServerMgr::hasServer(const QString& name, const QString& network)
{
QString data = ini.value(network, name);
if (data.length() > 0)
return true;
else
return false;
return ini.exist(network.toStdString(), name.toStdString());
}
QString ServerMgr::getServerDetails(QString name, QString network)
QString ServerMgr::getServerDetails(const QString& name, const QString& network)
{
return ini.value(network, name);
return QString::fromStdString( ini.read(network.toStdString(), name.toStdString()) );
}

@ -38,28 +38,39 @@ class ServerMgr : public QObject
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(QString network = "NONE");
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(QString network);
QString defaultServer(const QString& network);
// Add new network to servers.ini - returns false if network exist
bool addNetwork(QString name);
// Rename a network - returns false if new network name already exist
bool renameNetwork(QString o_name, QString n_name);
// Delete network - false if network didn't exsist (useless result?)
bool delNetwork(QString name, bool keep_servers = false);
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(QString name, QString host /*host:port*/, QString pw = "", QString network = "NONE");
// Delete a server from network - false if network or server didn't exist
bool delServer(QString name, QString network = "NONE");
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(QString name);
bool hasNetwork(const QString& name);
// Check if we have the given server name inside the network
bool hasServer(QString name, QString network = "NONE");
bool hasServer(const QString& name, const QString& network = "NONE");
// Get server details
QString getServerDetails(QString name, QString network = "NONE");
QString getServerDetails(const QString& name, const QString& network = "NONE");
private:
IniFile ini;

@ -1,6 +1,104 @@
#include "ConfigMgr.h"
#include "config.h"
#include <QHashIterator>
#include <unordered_map>
namespace {
using DefaultMap = std::unordered_map<std::string,std::string>;
/*
* Default settings in case it is not found in the config file.
* scripts and prefix-colors have no default settings.
*/
#if defined(Q_OS_WIN32) | defined(Q_OS_WIN64)
constexpr auto DefaultFontName = "Fixedsys";
#else
constexpr auto DefaultFontName = "Monospace";
#endif
const DefaultMap defaultGeometry {
{ "X", "-1" },
{ "Y", "-1" },
{ "Width", "1024" },
{ "Height", "768" },
{ "Maximized", "0" }
};
const DefaultMap defaultConnection {
{ "SSL", "0" }
};
const DefaultMap defaultCommon {
{ "ShowOptions", "1" },
{ "Reconnect", "0" },
{ "RejoinChannelsOnConnect", "0" },
{ "ShowWhoisActiveWindow", "1" },
{ "ShowModeInMessage", "1" },
{ "TrayNotify", "1" },
{ "TrayNotifyDelay", "5" },
{ "ShowTimestamp", "1" },
{ "TimestampFormat", "[HH:mm]" },
{ "ManualKeepaliveEnabled", "0" },
{ "ManualKeepalive", "10" },
{ "Font", DefaultFontName },
{ "FontSize", "12" },
{ "BgImageEnabled", "0" },
{ "BgImageOpacity", "100" },
{ "SSLSelfsigned", "0" },
{ "SSLCNMismatch", "0" },
{ "ButtonBarPosition", "N" }
};
const DefaultMap defaultColor {
{ "Action", "#840084" },
{ "CTCP", "#FF0000" },
{ "Highlight", "#848400" },
{ "Invite", "#008400" },
{ "Join", "#008400" },
{ "Kick", "#008400" },
{ "Mode", "#008400" },
{ "Nick", "#008400" },
{ "Normal", "#000000" },
{ "Notice", "#840000" },
{ "OwnText", "#008484" },
{ "Part", "#008400" },
{ "ProgramInfo", "#000084" },
{ "Quit", "#000084" },
{ "ServerInfo", "#008400" },
{ "Topic", "#008400" },
{ "Wallops", "#FF0000" },
{ "TextviewBackground", "#FFFFFF" },
{ "InputBackground", "#FFFFFF" },
{ "InputForeground", "#000000" },
{ "ListboxBackground", "#FFFFFF" },
{ "ListboxForeground", "#000000" },
{ "Links", "#0000FF" },
{ "WindowButtonNormal", "#000000" },
{ "WindowButtonActivity", "#000088" },
{ "WindowButtonMessage", "#0000FF" },
{ "WindowButtonAttention", "#FF0000" }
};
const DefaultMap defaultLogging {
{ "Channels", "0" },
{ "Privates", "0" },
{ "Path", "Path" }
};
const std::string& getDefault(const DefaultMap& map, const std::string& key)
{
static const std::string empty;
try {
return map.at(key);
}
catch (const std::out_of_range&) {
return empty;
}
}
} // anonymous namespace
ConfigMgr& ConfigMgr::instance()
{
@ -10,293 +108,168 @@ ConfigMgr& ConfigMgr::instance()
// 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);
}
: ini( QString(LOCAL_PATH+"/iirc.ini").toStdString() )
{}
void ConfigMgr::save()
{
IniFile ini(LOCAL_PATH+"/iirc.ini");
saveGeometry(ini);
saveConnection(ini);
saveCommon(ini);
saveColor(ini);
savePrefixColor(ini);
saveLogging(ini);
saveScripts(ini);
ini.flush();
emit saved();
}
QString ConfigMgr::geometry(const QString& key) const
{
return geometryData.value(key, "");
const auto keystdstr = key.toStdString();
const auto& defaultValue = getDefault(defaultGeometry, keystdstr);
return QString::fromStdString( ini.read("Geometry", keystdstr, defaultValue) );
}
QString ConfigMgr::connection(const QString& key) const
{
return connectionData.value(key, "");
const auto keystdstr = key.toStdString();
const auto& defaultValue = getDefault(defaultConnection, keystdstr);
return QString::fromStdString( ini.read("Connection", keystdstr, defaultValue) );
}
QString ConfigMgr::common(const QString& key) const
{
return commonData.value(key, "");
const auto keystdstr = key.toStdString();
const auto& defaultValue = getDefault(defaultCommon, keystdstr);
return QString::fromStdString( ini.read("Common", keystdstr, defaultValue) );
}
QString ConfigMgr::color(const QString& key) const
{
return colorData.value(key, "");
const auto keystdstr = key.toStdString();
const auto& defaultValue = getDefault(defaultColor, keystdstr);
return QString::fromStdString( ini.read("Color", keystdstr, defaultValue) );
}
std::optional<QColor> ConfigMgr::prefixColor(const QChar prefix) const
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;
const std::string key = std::to_string(prefix.toLatin1());
if (!ini.exist("PrefixColor", key))
return {};
const auto colorStr = QString::fromStdString( ini.read("PrefixColor", key) );
return QColor(colorStr);
}
QHash<QString, QString> ConfigMgr::color() const
{
return colorData;
QHash<QString, QString> ret;
const int sectSize = ini.count("Color");
for (int i = 0; i < sectSize; ++i) {
const auto key = QString::fromStdString( ini.item("Color", i) );
const auto val = QString::fromStdString( ini.read("Color", i) );
ret.insert(key, val);
}
return ret;
}
QVector<std::pair<QChar, QColor> > ConfigMgr::prefixColor() const
{
return prefixColorData;
QVector<std::pair<QChar, QColor>> ret;
const int sectSize = ini.count("PrefixColor");
for (int i = 0; i < sectSize; ++i) {
const auto code = ini.item("PrefixColor", i);
const auto first = std::stoi(code);
const auto second = QString::fromStdString( ini.read("PrefixColor", i) );
ret.push_back( std::make_pair(first, second) );
}
return ret;
}
QString ConfigMgr::logging(const QString& key) const
{
return loggingData.value(key, "");
const auto keystdstr = key.toStdString();
const auto& defaultValue = getDefault(defaultLogging, keystdstr);
return QString::fromStdString( ini.read("Logging", keystdstr, defaultValue) );
}
const QStringList& ConfigMgr::scripts() const
QStringList ConfigMgr::scripts() const
{
return scriptsData;
QStringList ret;
const int sectSize = ini.count("Scripts");
for (int i = 0; i < sectSize; ++i) {
const auto path = ini.read("Scripts", i);
ret.push_back( QString::fromStdString(path) );
}
return ret;
}
void ConfigMgr::setGeometry(const QString& key, const QString& value)
{
if (geometryData.contains(key))
geometryData.insert(key, value);
ini.write("Geometry", key.toStdString(), value.toStdString());
}
void ConfigMgr::setConnection(const QString& key, const QString& value)
{
if (connectionData.contains(key))
connectionData.insert(key, value);
ini.write("Connection", key.toStdString(), value.toStdString());
}
void ConfigMgr::setCommon(const QString& key, const QString& value)
{
if (commonData.contains(key))
commonData.insert(key, value);
ini.write("Common", key.toStdString(), value.toStdString());
}
void ConfigMgr::setLogging(const QString& key, const QString& value)
{
if (loggingData.contains(key))
loggingData.insert(key, value);
ini.write("Logging", key.toStdString(), value.toStdString());
}
void ConfigMgr::setColorPalette(const QHash<QString, QString>& palette)
{
colorData = palette;
QHashIterator<QString,QString> it(palette);
while (it.hasNext()) {
it.next();
const auto& key = it.key();
const auto& val = it.value();
ini.write("Color", key.toStdString(), val.toStdString());
}
}
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"));
geometryData.insert("Maximized", ini.value("Geometry", "Maximized", "0"));
}
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("ManualKeepaliveEnabled", ini.value("Common", "ManualKeepaliveEnabled", "0"));
commonData.insert("ManualKeepalive", ini.value("Common", "ManualKeepalive", "10"));
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"));
commonData.insert("ButtonBarPosition", ini.value("Common", "ButtonBarPosition", "N"));
}
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"));
colorData.insert("WindowButtonNormal", ini.value("Color", "WindowButtonNormal", "#000000"));
colorData.insert("WindowButtonActivity", ini.value("Color", "WindowButtonActivity", "#000088"));
colorData.insert("WindowButtonMessage", ini.value("Color", "WindowButtonMessage", "#0000FF"));
colorData.insert("WindowButtonAttention", ini.value("Color", "WindowButtonAttention", "#FF0000"));
}
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());
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::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)
void ConfigMgr::addScript(const QString& path)
{
QHashIterator<QString,QString> it(commonData);
while (it.hasNext()) {
it.next();
ini.write("Common", it.key(), it.value());
}
}
/*
* Scripts are stored to ini file with a number for key.
* Find the highest available number and use that.
*/
QStringList scripts;
const int sectSize = ini.count("Scripts");
for (int i = 0; i < sectSize; ++i) {
scripts << QString::fromStdString( ini.read("Scripts", i) );
}
scripts << path;
void ConfigMgr::saveColor(IniFile& ini)
{
QHashIterator<QString,QString> it(colorData);
while (it.hasNext()) {
it.next();
ini.write("Color", it.key(), it.value());
}
}
ini.remove("Scripts");
void ConfigMgr::savePrefixColor(IniFile& ini)
{
for (const auto& pair : prefixColorData) {
int keynum = pair.first.toLatin1();
ini.write("PrefixColor", QString::number(keynum), pair.second.name());
}
int n = 1;
for (const auto& script : scripts) {
ini.write("Scripts", std::to_string(n), script.toStdString());
++n;
}
}
void ConfigMgr::saveLogging(IniFile& ini)
void ConfigMgr::delScript(const QString& path)
{
QHashIterator<QString,QString> it(loggingData);
while (it.hasNext()) {
it.next();
ini.write("Logging", it.key(), it.value());
}
}
std::string n;
const int sectSize = ini.count("Scripts");
for (int i = 0; i < sectSize; ++i) {
n = ini.item("Scripts", i);
if (ini.read("Scripts", i) == path.toStdString())
break;
}
void ConfigMgr::saveScripts(IniFile& ini)
{
ini.delSection("Scripts");
for (int i = 0; i < scriptsData.length(); ++i)
ini.write("Scripts", QString::number(i), scriptsData[i]);
if (!n.empty())
ini.remove("Scripts", n);
}

@ -16,19 +16,17 @@ class ConfigMgr : public QObject
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;
std::optional<QColor> prefixColor(const QChar& prefix) const;
QHash<QString,QString> color() const;
QVector<std::pair<QChar,QColor>> prefixColor() const;
QVector<std::pair<QChar,QColor>> prefixColor() const; // QVector of pairs preserves the order.
QString logging(const QString& key) const;
const QStringList& scripts() const;
QStringList scripts() const;
void setGeometry(const QString& key, const QString& value);
void setConnection(const QString& key, const QString& value);
@ -41,28 +39,7 @@ public:
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;
IniFile ini;
signals:
void saved();

@ -1,6 +1,6 @@
/*
* IdealIRC - Internet Relay Chat client
* Copyright (C) 2019 Tom-Andre Barstad
* Copyright (C) 2020 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
@ -15,525 +15,456 @@
* 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 <QStringList>
#include <iostream>
#include "IniFile.h"
#include <vector>
#include <algorithm>
#include <fstream>
IniFile::IniFile(QString filename)
{
file = new QFile(filename);
if (QFile::exists(filename) == false) {
file->open(QIODevice::WriteOnly);
file->close();
}
}
void IniFile::clearNewline(char *data)
namespace {
/*
* Not unicode friendly.
* Used to make case-insensitive comparisons of keys and section names.
*/
bool strEquals(const std::string& l, const std::string& r)
{
int i = 0;
while (true) {
if (data[i] == '\0')
break;
if (data[i] == '\n') {
data[i] = '\0';
break;
return std::equal(l.begin(), l.end(), r.begin(),
[](char cl, char cb) {
return toupper(cl) == toupper(cb);
}
i++;
}
);
}
QString IniFile::value(QString Section, QString Item, QString defaultValue)
{
if (! file->open(QIODevice::ReadOnly | QIODevice::Text))
return defaultValue;
Section = QStringLiteral("[%1]").arg(Section);
Item.append('=');
void trimFrontAndBack(std::string& str) {
while (!str.empty() && std::isspace(str.front()))
str.erase(str.begin());
while (!str.empty() && std::isspace(str.back()))
str.erase(str.end() - 1);
}
char buf[1024];
memset(buf, 0, sizeof(buf));
bool sectFound = false;
}
while (file->readLine(buf, sizeof(buf)) != -1) {
clearNewline(buf);
QString line(buf);
struct Item
{
std::string key;
std::string val;
};
if (line.isEmpty())
continue;
struct Section
{
std::string name;
std::vector<Item> items;
};
if (line.left(Section.length()).toUpper() == Section.toUpper()) {
sectFound = true;
continue;
}
if ((sectFound) && (line.left(Item.length()).toUpper() == Item.toUpper())) {
file->close();
return line.mid(Item.length());
}
struct IniFilePriv
{
IniFile::Error setError(IniFile::Error e)
{
lastError = e;
return e;
}
file->close();
return defaultValue;
}
IniFile::Error lastError{ IniFile::Error::NoError };
std::string filename;
std::vector<Section> sections;
};
QString IniFile::value(QString Section, int ItemPos, QString defaultValue)
IniFile::IniFile(const std::string& filename)
: mp(std::make_unique<IniFilePriv>())
{
if (! file->open(QIODevice::ReadOnly | QIODevice::Text))
return defaultValue;
Section = QStringLiteral("[%1]").arg(Section);
mp->filename = filename;
reload();
}
char buf[1024];
memset(buf, 0, sizeof(buf));
bool sectFound = false;
int i = 0;
IniFile::IniFile(IniFile&& other) noexcept
: mp(std::move(other.mp))
{
}
while (file->readLine(buf, sizeof(buf)) != -1) {
clearNewline(buf);
QString line(buf);
IniFile::~IniFile() = default;
if (line.isEmpty())
continue;
IniFile& IniFile::operator=(IniFile&& other) noexcept
{
mp = std::move(other.mp);
return *this;
}
if (line.left(Section.length()).toUpper() == Section.toUpper()) {
sectFound = true;
continue;
}
IniFile::Error IniFile::error() const
{
return mp->lastError;
}
if ((sectFound) && (line.contains(QChar('=')))) {
if (i == ItemPos) {
// Read out value.
i = line.indexOf('=');
file->close();
return line.mid(i+1);
}
i++;
std::size_t IniFile::count() const
{
return mp->sections.size();
}
std::size_t IniFile::count(const std::string& section) const
{
auto it = std::find_if(mp->sections.begin(), mp->sections.end(),
[section](const Section& sn){
return strEquals(sn.name, section);
}
}
);
file->close();
return defaultValue;
return it != mp->sections.end()
? it->items.size() : 0;
}
QString IniFile::section(int SectionPos, QString defaultSectionName)
std::string IniFile::read(const std::string& section, const std::string& item, const std::string& defaultValue) const
{
if (! file->open(QIODevice::ReadOnly | QIODevice::Text))
return defaultSectionName;
char buf[1024];
memset(buf, 0, sizeof(buf));
int i = 0;
while (file->readLine(buf, sizeof(buf)) != -1) {
clearNewline(buf);
QString line(buf);
if (line == "")
continue;
auto s_it = std::find_if(mp->sections.begin(), mp->sections.end(),
[section](const Section& sn){
return strEquals(sn.name, section);
}
);
if (line.startsWith('[') && line.endsWith(']')) {
if (i == SectionPos) {
file->close();
return line.mid(1, line.length()-2);
}
if (s_it == mp->sections.end())
return defaultValue;
i++;
const auto& items = s_it->items;
auto i_it = std::find_if(items.begin(), items.end(),
[item](const Item& i){
return strEquals(i.key, item);
}
}
);
file->close();
return defaultSectionName;
if (i_it != items.end())
return i_it->val;
else
return defaultValue;
}
QString IniFile::key(QString Section, int ItemPos, QString defaultKey)
std::string IniFile::read(const std::string& section, int itemPos, const std::string& defaultValue) const
{
if (! file->open(QIODevice::ReadOnly | QIODevice::Text))
return defaultKey;
Section = QStringLiteral("[%1]").arg(Section);
char buf[1024];
memset(buf, 0, sizeof(buf));
bool sectFound = false;
int i = 0;
while (file->readLine(buf, sizeof(buf)) != -1) {
clearNewline(buf);
QString line(buf);
if (line.isEmpty())
continue;
if (line.left(Section.length()).toUpper() == Section.toUpper()) {
sectFound = true;
continue;
}
if ((sectFound) && (line.contains(QChar('=')))) {
if (i == ItemPos) {
// Read out item.
i = line.indexOf('=');
file->close();
return line.mid(0,i);
}
i++;
}
auto s_it = std::find_if(mp->sections.begin(), mp->sections.end(),
[section](const Section& sn){
return strEquals(sn.name, section);
}
);
if (s_it == mp->sections.end())
return defaultValue;
try {
const auto& items = s_it->items;
return items.at(itemPos).val;
}
catch (const std::out_of_range&) {
return defaultValue;
}
file->close();
return defaultKey;
}
bool IniFile::write(QString Section, QString Item, QString Value)
std::string IniFile::read(int sectionPos, const std::string& item, const std::string& defaultValue) const
{
Section = QStringLiteral("[%1]").arg(Section);
Item.append('=');
QStringList sl;
char buf[1024];
memset(buf, 0, sizeof(buf));
bool sectFound = false;
bool finished = false;
if (! file->open(QIODevice::ReadOnly | QIODevice::Text))
return false;
while (file->readLine(buf, sizeof(buf)) != -1) {
clearNewline(buf);
QString line(buf);
if (line.isEmpty())
continue;
sl.append(line);
if (finished)
continue;
if (line.left(Section.length()).toUpper() == Section.toUpper()) {
sectFound = true;
continue;
}
// Found existing item, overwriting.
if ((sectFound) && (line.left(Item.length()).toUpper() == Item.toUpper())) {
sl.removeAt(sl.count()-1); // Remove the last insertion, we get a new one here...
sl.append(Item + Value);
finished = true;
continue;
}
if ((sectFound) && (! finished) && (line.left(1)) == "[") {
// We have found our section, but not the item, as we reached end of the section.
sl.removeAt(sl.count()-1); // Remove the last insertion, we get a new one here...
sl.append(Item + Value);
sl.append(line);
finished = true;
continue;
}
try {
const auto& items = mp->sections[sectionPos].items;
auto i_it = std::find_if(items.begin(), items.end(),
[item](const Item& i){
return strEquals(i.key, item);
}
);
if (i_it != items.end())
return i_it->val;
else
return defaultValue;
}
if ((sectFound == true) && (finished == false))
sl.append(Item + Value); // We have found our section, but we reached EOF. Insert new item.
if (sectFound == false) {
// Section weren't found, we make a new one at the end, and our item there.
sl.append(Section);
sl.append(Item + Value);
catch (const std::out_of_range&) {
return defaultValue;
}
}
file->close();
if (! file->open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
return false;
for (int i = 0; i <= sl.count()-1; i++) {
QByteArray out;
out.append(sl[i]);
out.append('\n');
file->write(out);
std::string IniFile::read(int sectionPos, int itemPos, const std::string& defaultValue) const
{
try {
const auto& items = mp->sections[sectionPos].items;
return items.at(itemPos).val;
}
catch (const std::out_of_range&) {
return defaultValue;
}
file->close();
return true;
}
int IniFile::countItems(QString Section)
std::string IniFile::item(const std::string& section, int itemPos) const
{
if (! file->open(QIODevice::ReadOnly | QIODevice::Text))
return 0;
Section = QStringLiteral("[%1]").arg(Section);
char buf[1024];
memset(buf, 0, sizeof(buf));
bool sectFound = false;
int count = 0;
while (file->readLine(buf, sizeof(buf)) != -1) {
clearNewline(buf);
QString line(buf);
if (line.isEmpty())
continue;
if (line.left(Section.length()).toUpper() == Section.toUpper()) {
sectFound = true;
continue;
}
if (sectFound) {
if (line.left(1) == "[")
break;
if (line.contains('='))
count++;
}
}
file->close();
return count;
auto s_it = std::find_if(mp->sections.begin(), mp->sections.end(),
[section](const Section& sn){
return strEquals(sn.name, section);
}
);
if (s_it == mp->sections.end())
return {};
try {
const auto& items = s_it->items;
return items.at(itemPos).key;
}
catch (const std::out_of_range&) {
return {};
}
}
int IniFile::countSections()
std::string IniFile::item(int sectionPos, int itemPos) const
{
if (! file->open(QIODevice::ReadOnly | QIODevice::Text))
return 0;
char buf[1024];
memset(buf, 0, sizeof(buf));
int count = 0;
while (file->readLine(buf, sizeof(buf)) != -1) {
clearNewline(buf);
QString line(buf);
if (line.isEmpty())
continue;
if (line.startsWith('[') && line.endsWith(']'))
count++;
try {
const auto& items = mp->sections[sectionPos].items;
return items.at(itemPos).key;
}
catch (const std::out_of_range&) {
return {};
}
file->close();
return count;
}
bool IniFile::delSection(QString Section)
std::string IniFile::section(int sectionPos) const
{
Section = QStringLiteral("[%1]").arg(Section);
QStringList sl;
char buf[1024];
memset(buf, 0, sizeof(buf));
bool sectFound = false;
if (! file->open(QIODevice::ReadOnly | QIODevice::Text))
return false;
while (file->readLine(buf, sizeof(buf)) != -1) {
clearNewline(buf);
QString line(buf);
if (line.isEmpty())
continue;
if (line.left(Section.length()).toUpper() == Section.toUpper()) {
sectFound = true;
continue;
}
if (sectFound) {
if (line.left(1) == "[")
sectFound = false;
}
if (! sectFound)
sl.push_back(line);
try {
const auto& section = mp->sections[sectionPos];
return section.name;
}
file->close();
if (! file->open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
return false;
for (int i = 0; i <= sl.count()-1; i++) {
QByteArray out;
out.append(sl[i]);
out.append('\n');
file->write(out);
catch (const std::out_of_range&) {
return {};
}
file->close();
return true;
}
bool IniFile::delItem(QString Section, QString Item)
void IniFile::write(const std::string& section)
{
Section = QStringLiteral("[%1]").arg(Section);
Item.append('=');
QStringList sl;
char buf[1024];
memset(buf, 0, sizeof(buf));
bool sectFound = false;
bool finished = false;
if (! file->open(QIODevice::ReadOnly | QIODevice::Text))
return false;
while (file->readLine(buf, sizeof(buf)) != -1) {
clearNewline(buf);
QString line(buf);
if (exist(section))
return;
if (line.isEmpty())
continue;
auto& s = mp->sections.emplace_back();
s.name = section;
}
sl.append(line);
void IniFile::write(const std::string& section, const std::string& item, const std::string& value)
{
auto s_it = std::find_if(mp->sections.begin(), mp->sections.end(),
[section](const Section& sn){
return strEquals(sn.name, section);
}
);
if (s_it == mp->sections.end()) {
mp->sections.emplace_back();
s_it = mp->sections.end() - 1;
s_it->name = section;
}
if (finished)
continue;
auto& items = s_it->items;
auto i_it = std::find_if(items.begin(), items.end(),
[item](const Item& i){
return strEquals(i.key, item);
}
);
if (i_it == items.end()) {
s_it->items.emplace_back();
i_it = s_it->items.end() - 1;
i_it->key = item;
}
if (line.left(Section.length()).toUpper() == Section.toUpper()) {
sectFound = true;
continue;
}
i_it->val = value;
}
// Found item
if ((sectFound) && (line.left(Item.length()).toUpper() == Item.toUpper())) {
sl.removeAt(sl.count()-1); // Remove the last insertion
finished = true;
continue;
}
IniFile::Error IniFile::rename(const std::string& section, const std::string& newSection)
{
if (exist(newSection))
return mp->setError(IniFile::Error::NewAlreadyExists);
if ((sectFound) && (! finished) && (line.left(1)) == "[") {
// We have found our section, but not the item, as we reached end of the section.
// Just close file reading and do not touch the file at all.
file->close();
return false; // False because we didn't do anything
}
}
auto s_it = std::find_if(mp->sections.begin(), mp->sections.end(),
[section](const Section& sn){
return strEquals(sn.name, section);
}
);
if ((sectFound == true) && (finished == false)) {
// We have found our section, but we reached EOF. Don't do anything
file->close();
return false;
if (s_it == mp->sections.end()) {
return mp->setError(IniFile::Error::OldDoesntExist);
}
if (sectFound == false) {
// Section weren't found, just stop.
file->close();
return false;
else {
s_it->name = newSection;
}
file->close();
if (! file->open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
return false;
return mp->setError(IniFile::Error::NoError);
}
for (int i = 0; i <= sl.count()-1; i++) {
QByteArray out;
out.append(sl[i]);
out.append('\n');
file->write(out);
IniFile::Error IniFile::rename(const std::string& section, const std::string& item, const std::string& newItem)
{
if (exist(section, newItem))
return mp->setError(IniFile::Error::NewAlreadyExists);
auto s_it = std::find_if(mp->sections.begin(), mp->sections.end(),
[section](const Section& sn){
return strEquals(sn.name, section);
}
);
if (s_it == mp->sections.end())
return mp->setError(IniFile::Error::SectionDontExist);
auto& items = s_it->items;
auto i_it = std::find_if(items.begin(), items.end(),
[item](const Item& i){
return strEquals(i.key, item);
}
);
if (i_it == items.end()) {
return mp->setError(IniFile::Error::OldDoesntExist);
}
file->close();
return true;
else {
i_it->key = newItem;
}
return mp->setError(IniFile::Error::NoError);
}
bool IniFile::sectionExists(QString section)
IniFile::Error IniFile::remove(const std::string& section)
{
section = QStringLiteral("[%1]").arg(section);
if (! file->open(QIODevice::ReadOnly | QIODevice::Text))
return false;
char d[64];
memset(d, 0, 64);
while (file->readLine(d, 64) > -1) {
clearNewline(d);
QString ln(d);
if (section.toUpper() == ln.toUpper()) {
file->close();
return true;
}
auto s_it = std::find_if(mp->sections.begin(), mp->sections.end(),
[section](const Section& sn){
return strEquals(sn.name, section);
}
);
if (s_it == mp->sections.end()) {
return mp->setError(IniFile::Error::SectionDontExist);
}
else {
mp->sections.erase(s_it);
}
file->close();
return false;
return mp->setError(IniFile::Error::NoError);
}
bool IniFile::appendSection(QString Section)
IniFile::Error IniFile::remove(const std::string& section, const std::string& item)
{
if (sectionExists(Section))
return false;
if (! file->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append))
return false;
auto s_it = std::find_if(mp->sections.begin(), mp->sections.end(),
[section](const Section& sn){
return strEquals(sn.name, section);
}
);
if (s_it == mp->sections.end())
return mp->setError(IniFile::Error::SectionDontExist);
auto& items = s_it->items;
auto i_it = std::find_if(items.begin(), items.end(),
[item](const Item& i){
return strEquals(i.key, item);
}
);
if (i_it == items.end()) {
return mp->setError(IniFile::Error::ItemDontExist);
}
else {
items.erase(i_it);
}
QString out = QStringLiteral("[%1]\n")
.arg(Section);
return mp->setError(IniFile::Error::NoError);
}
file->write(out.toLocal8Bit());
file->close();
bool IniFile::exist(const std::string& section) const
{
auto s_it = std::find_if(mp->sections.begin(), mp->sections.end(),
[section](const Section& sn){
return strEquals(sn.name, section);
}
);
return true;
return s_it != mp->sections.end();
}
bool IniFile::renameSection(QString OldName, QString NewName)
bool IniFile::exist(const std::string& section, const std::string& item) const
{
OldName = QStringLiteral("[%1]").arg(OldName);
auto s_it = std::find_if(mp->sections.begin(), mp->sections.end(),
[section](const Section& sn){
return strEquals(sn.name, section);
}
);
NewName = QStringLiteral("[%1]").arg(NewName);
if (s_it == mp->sections.end())
return false;
QStringList sl;
auto& items = s_it->items;
auto i_it = std::find_if(items.begin(), items.end(),
[item](const Item& i){
return strEquals(i.key, item);
}
);
char buf[1024];
memset(buf, 0, sizeof(buf));
bool finished = false;
return i_it != items.end();
}
if (! file->open(QIODevice::ReadOnly | QIODevice::Text))
return false;
IniFile::Error IniFile::flush()
{
#if defined(_WIN32) || defined(_WIN64)
constexpr auto EOL = "\r\n";
#else
constexpr auto EOL = "\n";
#endif
std::string data;
for (const auto& section : mp->sections) {
if (!data.empty())
data += EOL;
data += '[';
data += section.name;
data += ']';
data += EOL;
for (const auto& item : section.items) {
data += item.key;
data += '=';
data += item.val;
data += EOL;
}
}
while (file->readLine(buf, sizeof(buf)) != -1) {
clearNewline(buf);
QString line(buf);
std::ofstream file(mp->filename);
if (!file.is_open())
return mp->setError(IniFile::Error::CannotWriteFile);
file << data;
file.close();
return mp->setError(IniFile::Error::NoError);
}
if (line.isEmpty())
continue;
IniFile::Error IniFile::reload()
{
std::ifstream file(mp->filename);
if (!file.is_open())
return mp->setError(IniFile::Error::CannotOpenFile);
sl.append(line);
std::string section;
std::string line;
while (std::getline(file, line)) {
if (line.back() == 0x0D)
line.pop_back(); // Windows ending CRLF, CR is read, but we drop it.
if (finished)
trimFrontAndBack(line);
if (line.empty())
continue;
if (line.toUpper() == OldName.toUpper()) {
sl.pop_back(); // The very last item inserted is actually OldName. Remove.
sl.append(NewName);
finished = true;
/* Sections */
if (line.front() == '[' && line.back() == ']') {
section = line.substr(1, line.size() - 2);
}
}
/* Items */
else if (auto p = line.find('='); p != std::string::npos && !section.empty()) {
const auto item = line.substr(0, p);
const auto value = line.substr(p + 1);
write(section, item, value);
}
if (finished == false) {
// Section weren't found, just stop.
file->close();
return false;
/* Garbage; malformed items or items outside of section */
else {
}
}
file.close();
file->close();
if (! file->open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
return false;
for (int i = 0; i <= sl.count()-1; i++) {
QByteArray out;
out.append(sl[i]);
out.append('\n');
file->write(out);
}
file->close();
return true;
return mp->setError(IniFile::Error::NoError);
}

@ -1,6 +1,6 @@
/*
* IdealIRC - Internet Relay Chat client
* Copyright (C) 2019 Tom-Andre Barstad
* Copyright (C) 2020 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
@ -15,43 +15,107 @@
* 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.
*
* ***
* This class is from the 0.x version and is quite ready for re-writing.
* TODO
* Refactor to use const-references
* Refactor to use tabs instead of spaces
* -> In fact, rewrite this entire class. There's so much wrong with it! <-
* ***
*/
#ifndef INIFILE_H
#define INIFILE_H
#include <QFile>
#include <string>
#include <memory>
struct IniFilePriv;
class IniFile
{
public:
explicit IniFile(QString filename);
~IniFile() { file->deleteLater(); }
QString value(QString Section, QString Item, QString defaultValue = "");
QString value(QString Section, int ItemPos, QString defaultValue = "");
QString section(int SectionPos, QString defaultSectionName = "");
QString key(QString Section, int ItemPos, QString defaultKey = "");
bool write(QString Section, QString Item, QString Value);
int countItems(QString section);
int countSections();
bool delSection(QString Section);
bool delItem(QString Section, QString Item);
bool sectionExists(QString section);
bool appendSection(QString Section);
bool renameSection(QString OldName, QString NewName);
explicit IniFile(const std::string& filename);
IniFile(IniFile&& other) noexcept;
IniFile(IniFile&) = delete;
~IniFile();
IniFile& operator=(IniFile&) = delete;
IniFile& operator=(IniFile&& other) noexcept;
private:
void clearNewline(char *data);
QFile *file;
enum class Error {
NoError,
CannotOpenFile,
CannotWriteFile,
/* Errors for renaming and removing sections and items. */
SectionDontExist,
ItemDontExist,
OldDoesntExist,
NewAlreadyExists
};
//! @brief Last encountered error.
Error error() const;
//! @brief Number of all sections.
std::size_t count() const;
//! @brief Number of all items in section. Returns zero even for non-existing sections.
std::size_t count(const std::string& section) const;
//! @brief Read value by key name.
std::string read(const std::string& section, const std::string& item, const std::string& defaultValue = "") const;
//! @brief Read value by item position.
std::string read(const std::string& section, int itemPos, const std::string& defaultValue = "") const;
//! @brief Read value by key name and section position.
std::string read(int sectionPos, const std::string& item, const std::string& defaultValue = "") const;
//! @brief Read value by item position and section position.
std::string read(int sectionPos, int itemPos, const std::string& defaultValue = "") const;
//! @brief Read key name by item position. Non-existing returns empty string.
std::string item(const std::string& section, int itemPos) const;
//! @brief Read key name by item position and section position. Non-existing returns empty string.
std::string item(int sectionPos, int itemPos) const;
//! @brief Read section name by its position. Non-existing returns empty string.
std::string section(int sectionPos) const;
//! @brief Creates an empty section. Existing section will not be touched.
void write(const std::string& section);
//! @brief Write value. Existing is updated, non-existing will be created.
void write(const std::string& section, const std::string& item, const std::string& value); // TODO template 'value'?
//! @brief Renames a section. May return "OldDontExist", "NewAlreadyExists" or "NoError".
Error rename(const std::string& section, const std::string& newSection);
//! @brief Renames an item. May return "SectionDontExist", "OldDontExist", "NewAlreadyExists" or "NoError".
Error rename(const std::string& section, const std::string& item, const std::string& newItem);
//! @brief Deletes entire section. May return "SectionDontExist", or "NoError".
Error remove(const std::string& section);
//! @brief Deletes an item. May return "SectionDontExist", "ItemDontExist", or "NoError".
Error remove(const std::string& section, const std::string& item);
//! @brief Checks if section exists.
bool exist(const std::string& section) const;
//! @brief Checks if an item exists.
bool exist(const std::string& section, const std::string& item) const;
//! @brief Write changes to the file.
Error flush();
//! @brief Reloads contents from the file.
Error reload();
private:
std::unique_ptr<IniFilePriv> mp;
};
#endif // INIFILE_H

Loading…
Cancel
Save