From 680ff9770a3fa75a2f053aab13671b2bb4278b42 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C5=82awomir=20Lach?= <slawek@lach.art.pl>
Date: Mon, 16 May 2022 15:32:56 +0200
Subject: [PATCH] =?UTF-8?q?!OSDN:=20#TICKET:=2045892:=20S=C5=82awomir=20La?=
 =?UTF-8?q?ch=20<slawek@lach.art.pl>?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Implementing ruledit tab for counters

diff --git a/meson.build b/meson.build
index e3d59b38e5..c93b701051 100644
--- a/meson.build
+++ b/meson.build
@@ -3494,6 +3494,7 @@ mocced_ruledit = qt_mod.preprocess(
    'tools/ruledit/req_vec_fix.h',
    'tools/ruledit/requirers_dlg.h',
    'tools/ruledit/ruledit_qt.h',
+   'tools/ruledit/tab_counters.h',
    'tools/ruledit/tab_enablers.h',
    'tools/ruledit/tab_extras.h',
    'tools/ruledit/tab_misc.h',
@@ -3518,6 +3519,7 @@ executable('freeciv-ruledit',
   'tools/ruledit/ruledit.cpp',
   'tools/ruledit/ruledit_qt.cpp',
   'tools/ruledit/tab_building.cpp',
+  'tools/ruledit/tab_counters.cpp',
   'tools/ruledit/tab_enablers.cpp',
   'tools/ruledit/tab_extras.cpp',
   'tools/ruledit/tab_good.cpp',
diff --git a/tools/ruledit/Makefile.am b/tools/ruledit/Makefile.am
index 44d0288c1e..5ac03ae97e 100644
--- a/tools/ruledit/Makefile.am
+++ b/tools/ruledit/Makefile.am
@@ -33,6 +33,7 @@ MOC_FILES = \
             meta_requirers_dlg.cpp \
             meta_ruledit_qt.cpp \
             meta_tab_building.cpp \
+            meta_tab_counters.cpp \
             meta_tab_enablers.cpp \
 	    meta_tab_extras.cpp	\
 	    meta_tab_good.cpp	\
@@ -55,6 +56,8 @@ freeciv_ruledit_SOURCES =	\
 		effect_edit.h	\
 		tab_building.cpp \
 		tab_building.h	\
+		tab_counters.cpp \
+		tab_counters.h \
 		tab_enablers.cpp \
 		tab_enablers.h	\
 		tab_extras.cpp	\
diff --git a/tools/ruledit/ruledit_qt.cpp b/tools/ruledit/ruledit_qt.cpp
index 967966dfb7..5b0559dadf 100644
--- a/tools/ruledit/ruledit_qt.cpp
+++ b/tools/ruledit/ruledit_qt.cpp
@@ -46,6 +46,7 @@
 #include "req_vec_fix.h"
 #include "ruledit.h"
 #include "tab_building.h"
+#include "tab_counters.h"
 #include "tab_enablers.h"
 #include "tab_extras.h"
 #include "tab_good.h"
@@ -185,6 +186,8 @@ void ruledit_gui::setup(QWidget *central_in)
   stack->addTab(terrains, QString::fromUtf8(R__("Terrains")));
   multipliers = new tab_multiplier(this);
   stack->addTab(multipliers, QString::fromUtf8(R__("Multipliers")));
+  counter = new tab_counter(this);
+  stack->addTab(counter, QString::fromUtf8(R__("Counters")));
   nation = new tab_nation(this);
   stack->addTab(nation, QString::fromUtf8(R__("Nations")));
 
@@ -250,6 +253,7 @@ void ruledit_gui::launch_now()
     extras->refresh();
     multipliers->refresh();
     terrains->refresh();
+    counter->refresh();
     main_layout->setCurrentIndex(1);
   } else {
     display_msg(R__("Ruleset loading failed!"));
diff --git a/tools/ruledit/ruledit_qt.h b/tools/ruledit/ruledit_qt.h
index 464d443083..e65192b316 100644
--- a/tools/ruledit/ruledit_qt.h
+++ b/tools/ruledit/ruledit_qt.h
@@ -30,6 +30,7 @@ class QStackedLayout;
 
 class requirers_dlg;
 class tab_building;
+class tab_counter;
 class tab_good;
 class tab_gov;
 class tab_misc;
@@ -130,6 +131,7 @@ signals:
     QStackedLayout *main_layout;
 
     tab_building *bldg;
+    tab_counter *counter;
     tab_misc *misc;
     tab_tech *tech;
     tab_unit *unit;
diff --git a/tools/ruledit/tab_counters.cpp b/tools/ruledit/tab_counters.cpp
new file mode 100644
index 0000000000..88378f4890
--- /dev/null
+++ b/tools/ruledit/tab_counters.cpp
@@ -0,0 +1,389 @@
+/***********************************************************************
+ 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 <QtGlobal>
+#include <QGridLayout>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QToolButton>
+
+// utility
+#include "fcintl.h"
+#include "log.h"
+
+// common
+#include "counters.h"
+#include "fc_types.h"
+#include "game.h"
+
+// ruledit
+#include "ruledit.h"
+#include "ruledit_qt.h"
+#include "validity.h"
+
+#include "tab_counters.h"
+
+/**********************************************************************//**
+  Setup tab_counter object
+**************************************************************************/
+tab_counter::tab_counter(ruledit_gui *ui_in) : QWidget()
+{
+#define empty_widget (nullptr)
+/* We use const array of pointers and hopes compiler
+ * will unroll loop
+ */
+#define widgets_row(...) { \
+                           column = 0; \
+                           QWidget * const wid[] = { __VA_ARGS__ }; \
+                           for (unsigned int i = 0; \
+                                i < sizeof(wid) / sizeof(wid[0]); i++)\
+                                { \
+                                  if (wid[i]) \
+                                    counter_layout->addWidget(wid[i], \
+                                                              row, \
+                                                              column);\
+                                    column++;\
+                                };\
+                                row++;\
+                          }
+
+  QVBoxLayout *main_layout = new QVBoxLayout(this);
+  QGridLayout *counter_layout = new QGridLayout();
+  QLabel *label;
+  QPushButton *add_button;
+  QPushButton *delete_button;
+  int row = 0;
+  int column = 0;
+
+  ui = ui_in;
+  selected = nullptr;
+
+  counter_list = new QListWidget(this);
+
+  connect(counter_list, SIGNAL(itemSelectionChanged()), this, SLOT(select_counter()));
+  main_layout->addWidget(counter_list);
+
+  counter_layout->setSizeConstraint(QLayout::SetMaximumSize);
+
+  label = new QLabel(QString::fromUtf8(R__("Rule Name")));
+  label->setParent(this);
+  rname = new QLineEdit(this);
+  rname->setText(R__("None"));
+  connect(rname, SIGNAL(returnPressed()), this, SLOT(name_given()));
+  widgets_row(label, empty_widget, rname);
+
+  label = new QLabel(QString::fromUtf8(R__("Name")));
+  label->setParent(this);
+
+  name = new QLineEdit(this);
+  name->setText(R__("None"));
+  connect(name, SIGNAL(returnPressed()), this, SLOT(name_given()));
+  same_name = new QCheckBox(this);
+  connect(same_name, SIGNAL(toggled(bool)), this, SLOT(same_name_toggle(bool)));
+  same_name->setChecked(true);
+  widgets_row(label, same_name, name);
+
+  label = new QLabel(QString::fromUtf8(R__("Default Value")), this);
+  label->setParent(this);
+  def = new QSpinBox(this);
+  def->setMaximum(INT_MAX);
+  def->setMinimum(INT_MIN);
+  connect(def, SIGNAL(valueChanged(int)), this, SLOT(default_given(int)));
+  widgets_row(label, empty_widget, def);
+
+
+  label = new QLabel(QString::fromUtf8(R__("Checkpoint")), this);
+  label->setParent(this);
+  checkpoint = new QSpinBox(this);
+  checkpoint->setMaximum(INT_MAX);
+  checkpoint->setMinimum(INT_MIN);
+  connect(checkpoint, SIGNAL(valueChanged(int)), this, SLOT(checkpoint_given(int)));
+  widgets_row(label, empty_widget, checkpoint);
+
+  {
+    enum counter_behaviour current;
+    type = new QComboBox(this);
+
+    label = new QLabel(QString::fromUtf8(R__("Behavior")));
+    label->setParent(this);
+
+    for (current = counter_behaviour_begin(); current > counter_behaviour_end(); current = counter_behaviour_next(current)) {
+      QVariant value(current);
+
+      type->addItem(counter_behaviour_name(current), value);
+    };
+
+    connect(type, SIGNAL(activated(int)), this, SLOT(counter_behaviour_selected(int)));
+    widgets_row(label, empty_widget, type);
+  }
+
+  effects_button = new QPushButton(QString::fromUtf8(R__("Effects")), this);
+  connect(effects_button, SIGNAL(pressed()), this, SLOT(edit_effects()));
+  widgets_row(empty_widget, empty_widget, effects_button);
+
+  add_button = new QPushButton(QString::fromUtf8(R__("Add Counter")), this);
+  connect(add_button, SIGNAL(pressed()), this, SLOT(add_now()));
+
+  delete_button = new QPushButton(QString::fromUtf8(R__("Remove this Counter")), this);
+  connect(delete_button, SIGNAL(pressed()), this, SLOT(delete_now()));
+
+  widgets_row(empty_widget, empty_widget, add_button, delete_button);
+  show_experimental(add_button);
+  show_experimental(delete_button);
+
+  refresh();
+  update_counter_info(nullptr);
+
+  main_layout->addLayout(counter_layout);
+
+  setLayout(main_layout);
+#undef empty_widget
+#undef widgets_row
+}
+
+/**********************************************************************//**
+  Called when counter behaviour is set by user
+**************************************************************************/
+void tab_counter::counter_behaviour_selected(int item)
+{
+  QVariant item_data;
+
+  if (nullptr == selected) {
+
+    return;
+  }
+  item_data = type->currentData();
+  selected->type =  (enum counter_behaviour) item_data.toInt();
+
+  update_counter_info(selected);
+  refresh();
+}
+
+/**********************************************************************//**
+  Refresh the information.
+**************************************************************************/
+void tab_counter::refresh()
+{
+  counter_list->clear();
+
+  counters_re_iterate(pcount) {
+      QListWidgetItem *item = new QListWidgetItem(counter_rule_name(pcount));
+
+      counter_list->insertItem(counter_index(pcount), item);
+  } counters_re_iterate_end;
+}
+
+/**********************************************************************//**
+  Update info of the counter
+**************************************************************************/
+void tab_counter::update_counter_info(struct counter *counter)
+{
+  selected = counter;
+
+  if (selected != nullptr) {
+    QString dispn = QString::fromUtf8(untranslated_name(&(counter->name)));
+    QString rulen = QString::fromUtf8(rule_name_get(&(counter->name)));
+
+    name->setText(dispn);
+    rname->setText(rulen);
+
+    if (dispn == rulen) {
+      name->setEnabled(false);
+    } else {
+      name->setEnabled(true);
+    }
+
+    checkpoint->setValue(selected->checkpoint);
+    def->setValue(selected->def);
+  } else {
+    name->setText(R__("None"));
+    rname->setText(R__("None"));
+    name->setEnabled(false);
+  }
+}
+
+/**********************************************************************//**
+  User selected counter from the list.
+**************************************************************************/
+void tab_counter::select_counter()
+{
+  char *cname;
+
+  QList<QListWidgetItem *> select_list = counter_list->selectedItems();
+
+  if (!select_list.isEmpty()) {
+    QByteArray tn_bytes;
+
+    tn_bytes = select_list.at(0)->text().toUtf8();
+
+    cname = tn_bytes.data();
+    update_counter_info(counter_by_rule_name(cname));
+  }
+}
+
+
+/**********************************************************************//**
+ User entered checkpoint value
+**************************************************************************/
+void tab_counter::checkpoint_given(int val)
+{
+  if (nullptr != selected) {
+
+    selected->checkpoint = val;
+  }
+}
+
+/**********************************************************************//**
+ User entered default value
+**************************************************************************/
+void tab_counter::default_given(int val)
+{
+  if (nullptr != selected) {
+
+    selected->def = val;
+  }
+}
+
+/**********************************************************************//**
+  User entered name for counter
+**************************************************************************/
+void tab_counter::name_given()
+{
+  if (selected != nullptr) {
+    QByteArray name_bytes;
+    QByteArray rname_bytes;
+
+    city_counters_iterate(pcounter) {
+      if (pcounter != selected) {
+        rname_bytes = rname->text().toUtf8();
+        if (!strcmp(counter_rule_name(pcounter), rname_bytes.data())) {
+          ui->display_msg(R__("A counter with that rule name already exists!"));
+          return;
+        }
+      }
+    } city_counters_iterate_end;
+
+    name_bytes = name->text().toUtf8();
+    rname_bytes = rname->text().toUtf8();
+    names_set(&(selected->name), 0,
+              name_bytes.data(),
+              rname_bytes.data());
+    refresh();
+  }
+}
+
+/**********************************************************************//**
+  User requested counter deletion
+**************************************************************************/
+void tab_counter::delete_now()
+{
+
+  if (nullptr != selected) {
+    requirers_dlg *requirers;
+
+    requirers = ui->create_requirers(counter_rule_name(selected));
+    if (is_counter_needed(selected, &ruledit_qt_display_requirers, requirers)) {
+      return;
+    }
+
+    selected->ruledit_disabled = true;
+
+    refresh();
+    update_counter_info(nullptr);
+  }
+}
+
+/**********************************************************************//**
+  Initialize new counter for use.
+**************************************************************************/
+bool tab_counter::initialize_new_counter(struct counter *counter)
+{
+  counter->checkpoint = 0;
+  counter->def = 0;
+  counter->target = CTGT_CITY;
+  counter->type = CB_CITY_OWNED_TURNS;
+  counter->index = counters_get_city_counters_count() - 1;
+  counter->ruledit_disabled = FALSE;
+
+  name_set(&counter->name, 0, "New counter");
+
+  return true;
+}
+
+/**********************************************************************//**
+  User requested new counter
+**************************************************************************/
+void tab_counter::add_now()
+{
+   struct counter *new_counter;
+
+  // Try to reuse freed counter slot
+  city_counters_iterate(pcount) {
+    if (pcount->type == COUNTER_BEHAVIOUR_LAST
+        || pcount->ruledit_disabled) {
+      if (initialize_new_counter(pcount)) {
+        update_counter_info(pcount);
+        refresh();
+      }
+      return;
+    }
+  } city_counters_iterate_end;
+
+  // Try to add completely new counter
+  if (counters_get_city_counters_count() >= MAX_COUNTERS) {
+    return;
+  }
+
+  // num_counter_types must be big enough to hold new counter or
+  // counter_by_number() fails.
+  game.control.num_counters++;
+  new_counter = counter_by_id(counters_get_city_counters_count());
+  if (initialize_new_counter(new_counter)) {
+    update_counter_info(new_counter);
+    attach_city_counter(new_counter);
+    refresh();
+  }
+}
+
+/**********************************************************************//**
+  Toggled whether rule_name and name should be kept identical
+**************************************************************************/
+void tab_counter::same_name_toggle(bool checked)
+{
+  name->setEnabled(!checked);
+  if (checked) {
+    name->setText(rname->text());
+  }
+}
+
+/**********************************************************************//**
+  User wants to edit effects
+**************************************************************************/
+void tab_counter::edit_effects()
+{
+  if (selected != nullptr) {
+    struct universal uni;
+
+    uni.value.counter = selected;
+    uni.kind = VUT_COUNTER;
+
+    ui->open_effect_edit(QString::fromUtf8(counter_rule_name(selected)),
+                         &uni, EFMC_NORMAL);
+  }
+}
diff --git a/tools/ruledit/tab_counters.h b/tools/ruledit/tab_counters.h
new file mode 100644
index 0000000000..8d379d6456
--- /dev/null
+++ b/tools/ruledit/tab_counters.h
@@ -0,0 +1,75 @@
+/***********************************************************************
+ 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__TAB_COUNTER_H
+#define FC__TAB_COUNTER_H
+
+#ifdef HAVE_CONFIG_H
+#include <fc_config.h>
+#endif
+
+// Qt
+#include <QCheckBox>
+#include <QComboBox>
+#include <QListWidget>
+#include <QMenu>
+#include <QPushButton>
+#include <QSpinBox>
+#include <QWidget>
+
+// common
+#include "counters.h"
+
+class ruledit_gui;
+
+class tab_counter : public QWidget
+{
+  Q_OBJECT
+
+  public:
+    explicit tab_counter(ruledit_gui *ui_in);
+    void refresh();
+    static void techs_to_menu(QMenu *fill_menu);
+    static QString counter_name(struct counter *padv);
+
+  private:
+    ruledit_gui *ui;
+
+    QLineEdit *name;
+    QLineEdit *rname;
+    QSpinBox *checkpoint;
+    QSpinBox *def;
+    QComboBox *type;
+    QCheckBox *same_name;
+    QPushButton *effects_button;
+
+    QListWidget *counter_list;
+
+    struct counter *selected;
+
+  private slots:
+    void update_counter_info(struct counter *counter);
+    void checkpoint_given(int val);
+    void default_given(int val);
+    void name_given();
+    void select_counter();
+    void add_now();
+    void delete_now();
+    void same_name_toggle(bool checked);
+    void edit_effects();
+    bool initialize_new_counter(struct counter *padv);
+    void counter_behaviour_selected(int item);
+};
+
+
+#endif // FC__TAB_COUNTER_H
diff --git a/tools/ruleutil/rulesave.c b/tools/ruleutil/rulesave.c
index 836d41b462..ee6cb9f81c 100644
--- a/tools/ruleutil/rulesave.c
+++ b/tools/ruleutil/rulesave.c
@@ -1787,7 +1787,7 @@ static bool save_game_ruleset(const char *filename, const char *name)
   comment_counters(sfile);
 
   sect_idx = 0;
-  city_counters_iterate(pcounter) {
+  counters_re_iterate(pcounter) {
     char path[512];
 
     fc_snprintf(path, sizeof(path), "counter_%d", sect_idx++);
@@ -1799,7 +1799,7 @@ static bool save_game_ruleset(const char *filename, const char *name)
 
     secfile_insert_str(sfile, counter_behaviour_name(pcounter->type), "%s.type", path);
 
-  } city_counters_iterate_end;
+  } counters_re_iterate_end;
 
   locks = FALSE;
   settings_iterate(SSET_ALL, pset) {
diff --git a/translations/ruledit/POTFILES.in b/translations/ruledit/POTFILES.in
index 7678c8d1e0..5050794268 100644
--- a/translations/ruledit/POTFILES.in
+++ b/translations/ruledit/POTFILES.in
@@ -9,6 +9,7 @@ tools/ruledit/requirers_dlg.cpp
 tools/ruledit/ruledit.cpp
 tools/ruledit/ruledit_qt.cpp
 tools/ruledit/tab_building.cpp
+tools/ruledit/tab_counters.cpp
 tools/ruledit/tab_enablers.cpp
 tools/ruledit/tab_extras.cpp
 tools/ruledit/tab_good.cpp
-- 
2.39.1

