diff --git a/icons.qrc b/icons.qrc
new file mode 100644
index 0000000..2b8fe2f
--- /dev/null
+++ b/icons.qrc
@@ -0,0 +1,8 @@
+
+
+ icons/open-file-48.png
+ icons/logout-52.png
+ icons/save-50.png
+ icons/save-as-64.png
+
+
diff --git a/icons/logout-52.png b/icons/logout-52.png
new file mode 100644
index 0000000..83af84b
Binary files /dev/null and b/icons/logout-52.png differ
diff --git a/icons/open-file-48.png b/icons/open-file-48.png
new file mode 100644
index 0000000..b9f0031
Binary files /dev/null and b/icons/open-file-48.png differ
diff --git a/icons/save-50.png b/icons/save-50.png
new file mode 100644
index 0000000..f78bd73
Binary files /dev/null and b/icons/save-50.png differ
diff --git a/icons/save-as-64.png b/icons/save-as-64.png
new file mode 100644
index 0000000..3e9ca55
Binary files /dev/null and b/icons/save-as-64.png differ
diff --git a/ini_utils.pro b/ini_utils.pro
new file mode 100644
index 0000000..86c23e1
--- /dev/null
+++ b/ini_utils.pro
@@ -0,0 +1,32 @@
+QT += core gui
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+CONFIG += c++17
+
+# You can make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+ main.cpp \
+ widget.cpp
+
+HEADERS += \
+ widget.h
+
+FORMS += \
+ widget.ui
+
+TRANSLATIONS += \
+ ini_utils_ru_RU.ts
+CONFIG += lrelease
+CONFIG += embed_translations
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
+
+RESOURCES += \
+ icons.qrc
diff --git a/ini_utils_ru_RU.ts b/ini_utils_ru_RU.ts
new file mode 100644
index 0000000..743f57f
--- /dev/null
+++ b/ini_utils_ru_RU.ts
@@ -0,0 +1,3 @@
+
+
+
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..bb95124
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,23 @@
+#include "widget.h"
+
+#include
+#include
+#include
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+
+ QTranslator translator;
+ const QStringList uiLanguages = QLocale::system().uiLanguages();
+ for (const QString &locale : uiLanguages) {
+ const QString baseName = "ini_utils_" + QLocale(locale).name();
+ if (translator.load(":/i18n/" + baseName)) {
+ a.installTranslator(&translator);
+ break;
+ }
+ }
+ Widget w;
+ w.show();
+ return a.exec();
+}
diff --git a/widget.cpp b/widget.cpp
new file mode 100644
index 0000000..9b53246
--- /dev/null
+++ b/widget.cpp
@@ -0,0 +1,197 @@
+#include "widget.h"
+#include "ui_widget.h"
+
+#include
+
+myTable::myTable(const records &hash):QTableWidget() {
+ fill(hash);
+}
+void myTable::fill(const records &hash) {
+ setRowCount(hash.size());
+ setColumnCount(2);
+ int ii=0;
+ for (const auto &rec: hash ) {
+ QTableWidgetItem *item1 = new QTableWidgetItem(rec.first);
+ QTableWidgetItem *item2 = new QTableWidgetItem(rec.second);
+ setItem(ii, 0, item1);
+ setItem(ii, 1, item2);
+ ++ii;
+ }
+}
+Widget::Widget(QWidget *parent)
+ : QWidget(parent), ui(new Ui::Widget)
+{
+ ui->setupUi(this);
+ ui->lineEdit->hide();
+ ui->pushButton->setIcon(QIcon(":/res/icons/open-file-48.png"));
+ ui->pushButton->setIconSize(QSize(28, 28));
+ ui->pushButton->setToolTip("Открыть INI файл...");
+ ui->pushButton_2->setIcon(QIcon(":/res/icons/logout-52.png"));
+ ui->pushButton_2->setIconSize(QSize(26, 22));
+ ui->pushButton_2->setToolTip("Выход");
+ ui->pushButton_3->setIcon(QIcon(":/res/icons/save-as-64.png"));
+ ui->pushButton_3->setIconSize(QSize(28, 28));
+ ui->pushButton_3->setToolTip("Сохранить как...");
+ ui->pushButton_3->setEnabled(false);
+ ui->pushButton_4->setIcon(QIcon(":/res/icons/save-50.png"));
+ ui->pushButton_4->setIconSize(QSize(26, 26));
+ ui->pushButton_4->setToolTip("Сохранить");
+ ui->pushButton_4->setEnabled(false);
+ ui->tabWidget->hide();
+
+ setWindowIcon(QIcon());
+}
+
+Widget::~Widget()
+{
+ delete ui;
+}
+
+
+void Widget::on_pushButton_clicked()
+{
+ QString pathStr = ui->lineEdit->text();
+ if (pathStr.isEmpty()) pathStr = QDir::currentPath();
+ QString fileName = QFileDialog::getOpenFileName(this,
+ tr("Выберите файл"),
+ pathStr,
+ tr("INI файлы (*.ini)"));
+ if (!fileName.isEmpty()) {
+ ui->lineEdit->setText(fileName);
+ QTimer::singleShot(200, this, SLOT(genTabs()));
+ }
+}
+
+void Widget::genTabs() {
+ ui->tabWidget->hide();
+ tabs.clear();
+ QTabWidget *tw = ui->tabWidget;
+ for (int i=tw->count()-1; i>=0; i--) {
+ QWidget *w = tw->widget(i);
+ tw->removeTab(i);
+ if (w!= nullptr) delete w;
+ }
+ tw->clear();
+ this->setWindowTitle(ui->lineEdit->text());
+ QFile file(ui->lineEdit->text());
+ if (!file.open(QIODevice::ReadOnly)) {
+ QMessageBox::critical(this, "Ошибка", "Ошибка открытия файла");
+ ui->pushButton_3->setEnabled(false);
+ ui->pushButton_4->setEnabled(false);
+ return;
+ }
+
+ ui->pushButton_3->setEnabled(true);
+ ui->pushButton_4->setEnabled(true);
+
+ proceedFile(file);
+ if (file.isOpen()) file.close();
+
+ const QIcon nullIcon;
+
+ for (int i=0; iaddTab(local_tab, nullIcon, tabs[i].m_name);
+ }
+ tw->setCurrentIndex(0);
+ tw->show();
+
+}
+
+void Widget::on_lineEdit_returnPressed()
+{
+ QTimer::singleShot(200, this, SLOT(genTabs()));
+}
+
+void Widget::on_pushButton_2_clicked()
+{
+ close();
+}
+
+void Widget::proceedFile(QFile &file) {
+ int numLine = 0;
+ comments.clear();
+ while (!file.atEnd()) {
+ numLine++;
+ QByteArray line = file.readLine();
+ QString str = QString::fromLocal8Bit(line.data());
+ if (str.length()>1 && str.right(2) == "\r\n") str.chop(2);
+ while (!str.isEmpty() && str.back()=='\t') str.chop(1);
+ if (str.isEmpty()) continue;
+ if (str.front()==';') {
+ comments.insert(numLine, str);
+ continue;
+ }
+ if (str.front() == '[') {
+ tabs.push_back(tabChapter(str));
+ continue;
+ }
+ QString strKey = str.section('=', 0, 0);
+ QString strData = str.section('=', 1);
+ QPair pair(strKey, strData);
+ tabs.back().m_hash.push_back(pair);
+ }
+}
+
+void Widget::on_pushButton_4_clicked()
+{
+// saveBtn
+ QString path = ui->lineEdit->text();
+ saveDataToFile(path);
+}
+
+void Widget::on_pushButton_3_clicked()
+{
+// saveAsBtn
+ QString pathStr = ui->lineEdit->text();
+ if (pathStr.isEmpty()) pathStr = QDir::currentPath();
+ QString fileName = QFileDialog::getSaveFileName(this,
+ tr("Выберите файл"),
+ pathStr,
+ tr("INI файлы (*.ini)"));
+ if (!fileName.isEmpty()) {
+ saveDataToFile(fileName);
+ }
+}
+
+void Widget::saveDataToFile(QString &name) {
+ QFile fd(name);
+ if (!fd.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ QMessageBox::critical(this, "Ошибка файла", "Ошибка открытия файла на запись");
+ }
+
+ int numLine = 1;
+
+ while (comments.find(numLine)!=comments.end()) {
+ fd.write(comments[numLine++].toLocal8Bit());
+ fd.write("\n");
+ }
+
+ for (int i=0; itabWidget->count(); ++i) {
+ fd.write(ui->tabWidget->tabText(i).toLocal8Bit());
+ fd.write("\n");
+ numLine++;
+ while (comments.find(numLine)!=comments.end()) {
+ fd.write(comments[numLine++].toLocal8Bit());
+ fd.write("\n");
+ }
+
+ myTable *tab = (myTable *)ui->tabWidget->widget(i);
+ for (int j=0; jrowCount(); j++) {
+ QString str = tab->item(j, 0)->text() +
+ QString("=") + tab->item(j, 1)->text() + "\n";
+ fd.write(str.toLocal8Bit());
+ numLine++;
+ while (comments.find(numLine)!=comments.end()) {
+ fd.write(comments[numLine++].toLocal8Bit());
+ fd.write("\n");
+ }
+ }
+ fd.write("\n");
+ numLine++;
+
+ }
+
+ if (fd.isOpen()) fd.close();
+ setWindowTitle(name);
+}
diff --git a/widget.h b/widget.h
new file mode 100644
index 0000000..9b6d555
--- /dev/null
+++ b/widget.h
@@ -0,0 +1,63 @@
+#ifndef WIDGET_H
+#define WIDGET_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+QT_BEGIN_NAMESPACE
+namespace Ui { class Widget; }
+QT_END_NAMESPACE
+
+using record = QPair;
+using records = QVector;
+
+class tabChapter {
+public:
+ tabChapter();
+ tabChapter(QString _name) : m_name(_name) {}
+ QString m_name;
+ records m_hash;
+};
+
+class myTable : public QTableWidget
+{
+ Q_OBJECT
+public:
+ myTable(const records &hash = {});
+private:
+ void fill(const records &hash);
+};
+
+class Widget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ Widget(QWidget *parent = nullptr);
+ ~Widget();
+
+private slots:
+ void on_pushButton_clicked();
+ void genTabs();
+ void on_lineEdit_returnPressed();
+
+ void on_pushButton_2_clicked();
+
+ void on_pushButton_4_clicked();
+
+ void on_pushButton_3_clicked();
+
+private:
+ Ui::Widget *ui;
+ void proceedFile(QFile &file);
+ QVector tabs;
+ QHash comments;
+ void saveDataToFile(QString &fileName);
+};
+#endif // WIDGET_H
diff --git a/widget.ui b/widget.ui
new file mode 100644
index 0000000..f2444b1
--- /dev/null
+++ b/widget.ui
@@ -0,0 +1,118 @@
+
+
+ Widget
+
+
+
+ 0
+ 0
+ 574
+ 792
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 574
+ 792
+
+
+
+
+ 574
+ 792
+
+
+
+ ini_UTILS
+
+
+
+ false
+
+
+
+ 30
+ 70
+ 521
+ 24
+
+
+
+
+
+
+
+
+
+ 30
+ 20
+ 51
+ 41
+
+
+
+
+
+
+
+
+
+ 210
+ 20
+ 51
+ 41
+
+
+
+
+
+
+
+
+
+ 149
+ 20
+ 51
+ 41
+
+
+
+
+
+
+
+
+
+ 89
+ 20
+ 51
+ 41
+
+
+
+
+
+
+
+
+
+ 30
+ 70
+ 530
+ 711
+
+
+
+ -1
+
+
+
+
+
+