From 0bf0d1acda4d14976782a2c879565639d4adaaf3 Mon Sep 17 00:00:00 2001
From: Marko Lindqvist <cazfi74@gmail.com>
Date: Sun, 24 Mar 2024 23:30:46 +0200
Subject: [PATCH 51/51] Ruledit: Add techs' value editor

See RM #358

Signed-off-by: Marko Lindqvist <cazfi74@gmail.com>
---
 common/tech.c                |   1 +
 common/tech.h                |   5 +-
 meson.build                  |   2 +
 tools/ruledit/Makefile.am    |   5 +-
 tools/ruledit/edit_tech.cpp  | 198 +++++++++++++++++++++++++++++++++++
 tools/ruledit/edit_tech.h    |  62 +++++++++++
 tools/ruledit/tab_tech.cpp   |  56 +++++++---
 tools/ruledit/tab_tech.h     |   1 +
 tools/ruledit/values_dlg.cpp |   2 +-
 9 files changed, 314 insertions(+), 18 deletions(-)
 create mode 100644 tools/ruledit/edit_tech.cpp
 create mode 100644 tools/ruledit/edit_tech.h

diff --git a/common/tech.c b/common/tech.c
index bae1394fb0..68cd501359 100644
--- a/common/tech.c
+++ b/common/tech.c
@@ -475,6 +475,7 @@ void techs_init(void)
   memset(advances, 0, sizeof(advances));
   for (i = 0; i < ARRAY_SIZE(advances); i++) {
     advances[i].item_number = i;
+    advances[i].ruledit_dlg = NULL;
     advances[i].cost = -1;
     advances[i].inherited_root_req = FALSE;
     advances[i].tclass = 0;
diff --git a/common/tech.h b/common/tech.h
index 56ece0517e..a67bae8e93 100644
--- a/common/tech.h
+++ b/common/tech.h
@@ -124,8 +124,9 @@ struct tech_class {
 struct advance {
   Tech_type_id item_number;
   struct name_translation name;
-  char graphic_str[MAX_LEN_NAME];	/* which named sprite to use */
-  char graphic_alt[MAX_LEN_NAME];	/* alternate icon name */
+  void *ruledit_dlg;
+  char graphic_str[MAX_LEN_NAME];       /* Which named sprite to use */
+  char graphic_alt[MAX_LEN_NAME];       /* Alternate icon name */
   struct tech_class *tclass;
 
   struct advance *require[AR_SIZE];
diff --git a/meson.build b/meson.build
index 7fac6370e4..f86abad755 100644
--- a/meson.build
+++ b/meson.build
@@ -4264,6 +4264,7 @@ mocced_ruledit = qt_mod.preprocess(
    'tools/ruledit/conversion_log.h',
    'tools/ruledit/edit_extra.h',
    'tools/ruledit/edit_impr.h',
+   'tools/ruledit/edit_tech.h',
    'tools/ruledit/edit_terrain.h',
    'tools/ruledit/edit_utype.h',
    'tools/ruledit/effect_edit.h',
@@ -4292,6 +4293,7 @@ executable('freeciv-ruledit',
   'tools/ruledit/conversion_log.cpp',
   'tools/ruledit/edit_extra.cpp',
   'tools/ruledit/edit_impr.cpp',
+  'tools/ruledit/edit_tech.cpp',
   'tools/ruledit/edit_terrain.cpp',
   'tools/ruledit/edit_utype.cpp',
   'tools/ruledit/effect_edit.cpp',
diff --git a/tools/ruledit/Makefile.am b/tools/ruledit/Makefile.am
index 376c70d4b8..68ed2907a4 100644
--- a/tools/ruledit/Makefile.am
+++ b/tools/ruledit/Makefile.am
@@ -28,7 +28,8 @@ freeciv_ruledit_CXXFLAGS = $(ruledit_cxxflags)
 MOC_FILES = \
             meta_conversion_log.cpp \
             meta_edit_extra.cpp	\
-            meta_edit_impr.cpp \
+            meta_edit_impr.cpp	\
+            meta_edit_tech.cpp	\
             meta_edit_terrain.cpp	\
             meta_edit_utype.cpp \
 	    meta_effect_edit.cpp	\
@@ -59,6 +60,8 @@ freeciv_ruledit_SOURCES =	\
 		edit_extra.h	\
 		edit_impr.cpp	\
 		edit_impr.h	\
+		edit_tech.cpp	\
+		edit_tech.h	\
 		edit_terrain.cpp	\
 		edit_terrain.h	\
 		edit_utype.cpp	\
diff --git a/tools/ruledit/edit_tech.cpp b/tools/ruledit/edit_tech.cpp
new file mode 100644
index 0000000000..a1178b9a24
--- /dev/null
+++ b/tools/ruledit/edit_tech.cpp
@@ -0,0 +1,198 @@
+/***********************************************************************
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <fc_config.h>
+#endif
+
+// Qt
+#include <QCheckBox>
+#include <QGridLayout>
+#include <QLineEdit>
+#include <QMenu>
+#include <QPushButton>
+#include <QSpinBox>
+#include <QToolButton>
+
+// common
+#include "tech.h"
+
+// ruledit
+#include "helpeditor.h"
+#include "ruledit.h"
+#include "ruledit_qt.h"
+
+#include "edit_tech.h"
+
+
+#define FLAGROWS 7
+
+/**********************************************************************//**
+  Setup edit_tech object
+**************************************************************************/
+edit_tech::edit_tech(ruledit_gui *ui_in, struct advance *tech_in) : values_dlg()
+{
+  QHBoxLayout *main_layout = new QHBoxLayout(this);
+  QGridLayout *tech_layout = new QGridLayout();
+  QLabel *label;
+  QPushButton *button;
+  int row;
+  int rowcount;
+  int column;
+
+  ui = ui_in;
+  tech = tech_in;
+
+  flag_layout = new QGridLayout();
+
+  setWindowTitle(QString::fromUtf8(advance_rule_name(tech)));
+
+  label = new QLabel(QString::fromUtf8(R__("Cost")));
+  label->setParent(this);
+
+  cost = new QSpinBox(this);
+  cost->setRange(0, 10000);
+  connect(cost, SIGNAL(valueChanged(int)), this, SLOT(set_cost_value(int)));
+
+  row = 0;
+  tech_layout->addWidget(label, row, 0);
+  tech_layout->addWidget(cost, row++, 1);
+
+  label = new QLabel(QString::fromUtf8(R__("Graphics tag")));
+  label->setParent(this);
+
+  gfx_tag = new QLineEdit(this);
+  connect(gfx_tag, SIGNAL(returnPressed()), this, SLOT(gfx_tag_given()));
+
+  tech_layout->addWidget(label, row, 0);
+  tech_layout->addWidget(gfx_tag, row++, 1);
+
+  label = new QLabel(QString::fromUtf8(R__("Alt graphics tag")));
+  label->setParent(this);
+
+  gfx_tag_alt = new QLineEdit(this);
+  connect(gfx_tag_alt, SIGNAL(returnPressed()), this, SLOT(gfx_tag_alt_given()));
+
+  tech_layout->addWidget(label, row, 0);
+  tech_layout->addWidget(gfx_tag_alt, row++, 1);
+
+  button = new QPushButton(QString::fromUtf8(R__("Helptext")), this);
+  connect(button, SIGNAL(pressed()), this, SLOT(helptext()));
+  tech_layout->addWidget(button, row++, 1);
+
+  rowcount = 0;
+  column = 0;
+  for (int i = 0; i < TF_COUNT; i++) {
+    enum tech_flag_id flag = (enum tech_flag_id)i;
+    QCheckBox *check = new QCheckBox();
+
+    label = new QLabel(tech_flag_id_name(flag));
+    flag_layout->addWidget(label, rowcount, column + 1);
+
+    check->setChecked(BV_ISSET(tech->flags, flag));
+    flag_layout->addWidget(check, rowcount, column);
+
+    if (++rowcount >= FLAGROWS) {
+      column += 2;
+      rowcount = 0;
+    }
+  }
+
+  refresh();
+
+  main_layout->addLayout(tech_layout);
+  main_layout->addLayout(flag_layout);
+
+  setLayout(main_layout);
+}
+
+/**********************************************************************//**
+  User is closing dialog.
+**************************************************************************/
+void edit_tech::closeEvent(QCloseEvent *cevent)
+{
+  int rowcount;
+  int column;
+
+  close_help();
+
+  // Save values from text fields.
+  gfx_tag_given();
+  gfx_tag_alt_given();
+
+  BV_CLR_ALL(tech->flags);
+  rowcount = 0;
+  column = 0;
+  for (int i = 0; i < TF_COUNT; i++) {
+    QCheckBox *check = static_cast<QCheckBox *>(flag_layout->itemAtPosition(rowcount, column)->widget());
+
+    if (check->isChecked()) {
+      BV_SET(tech->flags, i);
+    }
+
+    if (++rowcount >= FLAGROWS) {
+      rowcount = 0;
+      column += 2;
+    }
+  }
+
+  tech->ruledit_dlg = nullptr;
+}
+
+/**********************************************************************//**
+  Refresh the information.
+**************************************************************************/
+void edit_tech::refresh()
+{
+  cost->setValue(tech->cost);
+  gfx_tag->setText(tech->graphic_str);
+  gfx_tag_alt->setText(tech->graphic_alt);
+}
+
+/**********************************************************************//**
+  Read cost value from spinbox
+**************************************************************************/
+void edit_tech::set_cost_value(int value)
+{
+  tech->cost = value;
+
+  refresh();
+}
+
+/**********************************************************************//**
+  User entered new graphics tag.
+**************************************************************************/
+void edit_tech::gfx_tag_given()
+{
+  QByteArray tag_bytes = gfx_tag->text().toUtf8();
+
+  sz_strlcpy(tech->graphic_str, tag_bytes);
+}
+
+/**********************************************************************//**
+  User entered new alternative graphics tag.
+**************************************************************************/
+void edit_tech::gfx_tag_alt_given()
+{
+  QByteArray tag_bytes = gfx_tag_alt->text().toUtf8();
+
+  sz_strlcpy(tech->graphic_alt, tag_bytes);
+}
+
+/**********************************************************************//**
+  User pressed helptext button
+**************************************************************************/
+void edit_tech::helptext()
+{
+  open_help(tech->helptext);
+}
diff --git a/tools/ruledit/edit_tech.h b/tools/ruledit/edit_tech.h
new file mode 100644
index 0000000000..16ee06a481
--- /dev/null
+++ b/tools/ruledit/edit_tech.h
@@ -0,0 +1,62 @@
+/***********************************************************************
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+
+#ifndef FC__EDIT_TECH_H
+#define FC__EDIT_TECH_H
+
+#ifdef HAVE_CONFIG_H
+#include <fc_config.h>
+#endif
+
+// Qt
+#include <QDialog>
+
+// ruledit
+#include "values_dlg.h"
+
+class QGridLayout;
+class QLineEdit;
+class QSpinBox;
+class QToolButton;
+
+class ruledit_gui;
+
+class edit_tech : public values_dlg
+{
+  Q_OBJECT
+
+  public:
+    explicit edit_tech(ruledit_gui *ui_in, struct advance *tech_in);
+    void refresh();
+
+  private:
+    ruledit_gui *ui;
+    struct advance *tech;
+    QSpinBox *cost;
+    QLineEdit *gfx_tag;
+    QLineEdit *gfx_tag_alt;
+
+    QGridLayout *flag_layout;
+
+  protected:
+    void closeEvent(QCloseEvent *cevent);
+
+  private slots:
+    void set_cost_value(int value);
+    void gfx_tag_given();
+    void gfx_tag_alt_given();
+    void helptext();
+};
+
+
+#endif // FC__EDIT_TECH_H
diff --git a/tools/ruledit/tab_tech.cpp b/tools/ruledit/tab_tech.cpp
index 2b600eeeba..2b11c5eb52 100644
--- a/tools/ruledit/tab_tech.cpp
+++ b/tools/ruledit/tab_tech.cpp
@@ -33,6 +33,7 @@
 #include "tech.h"
 
 // ruledit
+#include "edit_tech.h"
 #include "ruledit.h"
 #include "ruledit_qt.h"
 #include "validity.h"
@@ -50,6 +51,8 @@ tab_tech::tab_tech(ruledit_gui *ui_in) : QWidget()
   QPushButton *effects_button;
   QPushButton *add_button;
   QPushButton *delete_button;
+  QPushButton *edit_button;
+  int row = 0;
 
   ui = ui_in;
   selected = 0;
@@ -66,8 +69,8 @@ tab_tech::tab_tech(ruledit_gui *ui_in) : QWidget()
   rname = new QLineEdit(this);
   rname->setText(R__("None"));
   connect(rname, SIGNAL(returnPressed()), this, SLOT(name_given()));
-  tech_layout->addWidget(label, 0, 0);
-  tech_layout->addWidget(rname, 0, 2);
+  tech_layout->addWidget(label, row, 0);
+  tech_layout->addWidget(rname, row++, 2);
 
   label = new QLabel(QString::fromUtf8(R__("Name")));
   label->setParent(this);
@@ -76,9 +79,13 @@ tab_tech::tab_tech(ruledit_gui *ui_in) : QWidget()
   name = new QLineEdit(this);
   name->setText(R__("None"));
   connect(name, SIGNAL(returnPressed()), this, SLOT(name_given()));
-  tech_layout->addWidget(label, 1, 0);
-  tech_layout->addWidget(same_name, 1, 1);
-  tech_layout->addWidget(name, 1, 2);
+  tech_layout->addWidget(label, row, 0);
+  tech_layout->addWidget(same_name, row, 1);
+  tech_layout->addWidget(name, row++, 2);
+
+  edit_button = new QPushButton(QString::fromUtf8(R__("Edit Values")), this);
+  connect(edit_button, SIGNAL(pressed()), this, SLOT(edit_now()));
+  tech_layout->addWidget(edit_button, row++, 2);
 
   label = new QLabel(QString::fromUtf8(R__("Req1")));
   label->setParent(this);
@@ -86,16 +93,16 @@ tab_tech::tab_tech(ruledit_gui *ui_in) : QWidget()
   req1_button->setParent(this);
   req1 = prepare_req_button(req1_button, AR_ONE);
   connect(req1_button, SIGNAL(pressed()), this, SLOT(req1_jump()));
-  tech_layout->addWidget(label, 2, 0);
-  tech_layout->addWidget(req1_button, 2, 2);
+  tech_layout->addWidget(label, row, 0);
+  tech_layout->addWidget(req1_button, row++, 2);
 
   label = new QLabel(QString::fromUtf8(R__("Req2")));
   label->setParent(this);
   req2_button = new QToolButton();
   req2 = prepare_req_button(req2_button, AR_TWO);
   connect(req2_button, SIGNAL(pressed()), this, SLOT(req2_jump()));
-  tech_layout->addWidget(label, 3, 0);
-  tech_layout->addWidget(req2_button, 3, 2);
+  tech_layout->addWidget(label, row, 0);
+  tech_layout->addWidget(req2_button, row++, 2);
 
   label = new QLabel(QString::fromUtf8(R__("Root Req")));
   label->setParent(this);
@@ -103,21 +110,21 @@ tab_tech::tab_tech(ruledit_gui *ui_in) : QWidget()
   root_req_button->setParent(this);
   root_req = prepare_req_button(root_req_button, AR_ROOT);
   connect(root_req_button, SIGNAL(pressed()), this, SLOT(root_req_jump()));
-  tech_layout->addWidget(label, 4, 0);
-  tech_layout->addWidget(root_req_button, 4, 2);
+  tech_layout->addWidget(label, row, 0);
+  tech_layout->addWidget(root_req_button, row++, 2);
 
   effects_button = new QPushButton(QString::fromUtf8(R__("Effects")), this);
   connect(effects_button, SIGNAL(pressed()), this, SLOT(edit_effects()));
-  tech_layout->addWidget(effects_button, 5, 2);
+  tech_layout->addWidget(effects_button, row++, 2);
 
   add_button = new QPushButton(QString::fromUtf8(R__("Add tech")), this);
   connect(add_button, SIGNAL(pressed()), this, SLOT(add_now()));
-  tech_layout->addWidget(add_button, 6, 0);
+  tech_layout->addWidget(add_button, row++, 0);
   show_experimental(add_button);
 
   delete_button = new QPushButton(QString::fromUtf8(R__("Remove this tech")), this);
   connect(delete_button, SIGNAL(pressed()), this, SLOT(delete_now()));
-  tech_layout->addWidget(delete_button, 6, 2);
+  tech_layout->addWidget(delete_button, row++, 2);
   show_experimental(delete_button);
 
   refresh();
@@ -385,6 +392,10 @@ void tab_tech::delete_now()
 
     selected->require[AR_ONE] = A_NEVER;
 
+    if (selected->ruledit_dlg != nullptr) {
+      ((edit_tech *)selected->ruledit_dlg)->done(0);
+    }
+
     refresh();
     update_tech_info(nullptr);
   }
@@ -471,3 +482,20 @@ void tab_tech::edit_effects()
                          &uni, EFMC_NORMAL);
   }
 }
+
+/**********************************************************************//**
+  User requested tech edit dialog
+**************************************************************************/
+void tab_tech::edit_now()
+{
+  if (selected != nullptr) {
+    if (selected->ruledit_dlg == nullptr) {
+      edit_tech *edit = new edit_tech(ui, selected);
+
+      edit->show();
+      selected->ruledit_dlg = edit;
+    } else {
+      ((edit_tech *)selected->ruledit_dlg)->raise();
+    }
+  }
+}
diff --git a/tools/ruledit/tab_tech.h b/tools/ruledit/tab_tech.h
index 664e6596d2..282221ab01 100644
--- a/tools/ruledit/tab_tech.h
+++ b/tools/ruledit/tab_tech.h
@@ -74,6 +74,7 @@ class tab_tech : public QWidget
     void root_req_menu(QAction *action);
     void add_now();
     void delete_now();
+    void edit_now();
     void same_name_toggle(bool checked);
     void edit_effects();
 };
diff --git a/tools/ruledit/values_dlg.cpp b/tools/ruledit/values_dlg.cpp
index eec2f8cf5d..af19d6ca67 100644
--- a/tools/ruledit/values_dlg.cpp
+++ b/tools/ruledit/values_dlg.cpp
@@ -34,7 +34,7 @@ values_dlg::values_dlg() : QDialog()
 **************************************************************************/
 void values_dlg::open_help(struct strvec *helptext)
 {
-  if (help == nullptr) {
+  if (help == nullptr && helptext != nullptr) {
     help = new helpeditor(this, helptext);
 
     help->show();
-- 
2.43.0

