From 67c12fe94e83b284f943597f684afdae4f337c22 Mon Sep 17 00:00:00 2001
From: 0tkl <118708188+0tkl@users.noreply.github.com>
Date: Fri, 16 Feb 2024 21:00:55 +0800
Subject: [PATCH] CPS customizable

---
 src/grid_column.cpp              | 50 +++++++++++++++++++++++++-------
 src/grid_column.h                |  1 +
 src/libresrc/default_config.json |  6 ++--
 src/preferences.cpp              | 12 ++++++--
 4 files changed, 55 insertions(+), 14 deletions(-)

diff --git a/src/grid_column.cpp b/src/grid_column.cpp
index 321a09439f..8af2b0fe25 100644
--- a/src/grid_column.cpp
+++ b/src/grid_column.cpp
@@ -289,6 +289,8 @@ class GridColumnCPS final : public GridColumn {
 	const agi::OptionValue *cps_warn = OPT_GET("Subtitle/Character Counter/CPS Warning Threshold");
 	const agi::OptionValue *cps_error = OPT_GET("Subtitle/Character Counter/CPS Error Threshold");
 	const agi::OptionValue *bg_color = OPT_GET("Colour/Subtitle Grid/CPS Error");
+	const agi::OptionValue *disp_fmt = OPT_GET("Subtitle/Character Counter/Display Format");
+	const agi::OptionValue *col_align = OPT_GET("Subtitle/Character Counter/Column Alignment");
 
 public:
 	COLUMN_HEADER(_("CPS"))
@@ -300,7 +302,7 @@ class GridColumnCPS final : public GridColumn {
 		return wxS("");
 	}
 
-	int CPS(const AssDialogue *d) const {
+	double CPS(const AssDialogue *d) const {
 		int duration = d->End - d->Start;
 		auto const& text = d->Text.get();
 
@@ -313,25 +315,51 @@ class GridColumnCPS final : public GridColumn {
 		if (ignore_punctuation->GetBool())
 			ignore |= agi::IGNORE_PUNCTUATION;
 
-		return agi::CharacterCount(text, ignore) * 1000 / duration;
+		return agi::CharacterCount(text, ignore) * 1000. / duration;
 	}
 
 	int Width(const agi::Context *c, WidthHelper &helper) const override {
-		return helper(wxS("999"));
+		return helper(wxS("99.9"));
 	}
 
 	void Paint(wxDC &dc, int x, int y, const AssDialogue *d, const agi::Context *) const override {
-		int cps = CPS(d);
-		if (cps < 0 || cps > 100) return;
+		double cps = CPS(d);
+		if (cps < 0) return;
+
+		double round_cps;
+		wxString str;
+		if (disp_fmt->GetInt() == 0 || (disp_fmt->GetInt() == 2 && cps >= 10)) {
+			round_cps = std::lround(cps);
+			if (round_cps >= 100) return;
+			str = std::to_wstring(int(round_cps));
+		} else {
+			round_cps = std::round(cps*10) / 10.0;
+			if (round_cps >= 100) return;
+			if (round_cps >= 10)
+				str = std::to_wstring(round_cps).substr(0, 4);
+			else
+				str = std::to_wstring(round_cps).substr(0, 3);
+		}
+
+		wxSize ext;
+		int w0 = dc.GetTextExtent(wxString(L"0")).GetWidth();
+		if (col_align->GetInt() == 0 || (disp_fmt->GetInt() != 2 && round_cps >= 10))
+			ext = dc.GetTextExtent(str);
+		else {
+			wxString str0;
+			if (cps >= 10)
+				str0 = str + wxString(L".0");
+			else
+				str0 = wxString(L"0") + str;
+			ext = dc.GetTextExtent(str0);
+		}
 
-		wxString str = std::to_wstring(cps);
-		wxSize ext = dc.GetTextExtent(str);
 		auto tc = dc.GetTextForeground();
 
-		int cps_min = cps_warn->GetInt();
-		int cps_max = std::max<int>(cps_min, cps_error->GetInt());
+		double cps_min = cps_warn->GetDouble();
+		double cps_max = std::max<float>(cps_min, cps_error->GetDouble());
 		if (cps > cps_min) {
-			double alpha = std::min((double)(cps - cps_min + 1) / (cps_max - cps_min + 1), 1.0);
+			double alpha = std::min((cps - cps_min + 1) / (cps_max - cps_min + 1), 1.0);
 			dc.SetBrush(wxBrush(blend(to_wx(bg_color->GetColor()), dc.GetBrush().GetColour(), alpha)));
 			dc.SetPen(*wxTRANSPARENT_PEN);
 			dc.DrawRectangle(x, y + 1, width, ext.GetHeight() + 3);
@@ -339,6 +367,8 @@ class GridColumnCPS final : public GridColumn {
 		}
 
 		x += (width + 2 - ext.GetWidth()) / 2;
+		if (col_align->GetInt() == 1 && round_cps < 10)
+			x += w0;
 		dc.DrawText(str, x, y + 2);
 		dc.SetTextForeground(tc);
 	}
diff --git a/src/grid_column.h b/src/grid_column.h
index fc215d0ba0..c3fbddd076 100644
--- a/src/grid_column.h
+++ b/src/grid_column.h
@@ -16,6 +16,7 @@
 
 #include "flyweight_hash.h"
 
+#include <cmath>
 #include <memory>
 #include <string>
 #include <vector>
diff --git a/src/libresrc/default_config.json b/src/libresrc/default_config.json
index 8fdcfb6ef4..e11c61f306 100644
--- a/src/libresrc/default_config.json
+++ b/src/libresrc/default_config.json
@@ -371,8 +371,10 @@
 		"Character Counter" : {
 			"Ignore Whitespace" : true,
 			"Ignore Punctuation" : true,
-			"CPS Warning Threshold" : 15,
-			"CPS Error Threshold" : 30
+			"CPS Warning Threshold" : 15.0,
+			"CPS Error Threshold" : 30.0,
+			"Display Format" : 0,
+			"Column Alignment": 0
 		},
 		"Character Limit" : 40,
 		"Default Resolution" : {
diff --git a/src/preferences.cpp b/src/preferences.cpp
index 0d20ff2035..2127b8325a 100644
--- a/src/preferences.cpp
+++ b/src/preferences.cpp
@@ -215,11 +215,19 @@ void Interface(wxTreebook *book, Preferences *parent) {
 
 	auto character_count = p->PageSizer(_("Character Counter"));
 	p->OptionAdd(character_count, _("Maximum characters per line"), "Subtitle/Character Limit", 0, 1000);
-	p->OptionAdd(character_count, _("Characters Per Second Warning Threshold"), "Subtitle/Character Counter/CPS Warning Threshold", 0, 1000);
-	p->OptionAdd(character_count, _("Characters Per Second Error Threshold"), "Subtitle/Character Counter/CPS Error Threshold", 0, 1000);
+	p->OptionAdd(character_count, _("Characters Per Second Warning Threshold"), "Subtitle/Character Counter/CPS Warning Threshold", 0.1, 1000., 0.1);
+	p->OptionAdd(character_count, _("Characters Per Second Error Threshold"), "Subtitle/Character Counter/CPS Error Threshold", 0.1, 1000., 0.1);
 	p->OptionAdd(character_count, _("Ignore whitespace"), "Subtitle/Character Counter/Ignore Whitespace");
 	p->OptionAdd(character_count, _("Ignore punctuation"), "Subtitle/Character Counter/Ignore Punctuation");
 
+	const wxString ccpsf_arr[3] = {_("Nearest integer"), _("Nearest 0.1"), _("2 sig figs")};
+	wxArrayString cpsf_res(3, ccpsf_arr);
+	p->OptionChoice(character_count, _("CPS display format"), cpsf_res, "Subtitle/Character Counter/Display Format");
+
+	const wxString calign_arr[2] = {_("Center"), _("Center with virtual 0")};
+	wxArrayString calign_res(2, calign_arr);
+	p->OptionChoice(character_count, _("CPS column alignment"), calign_res, "Subtitle/Character Counter/Column Alignment");
+
 	auto grid = p->PageSizer(_("Grid"));
 	p->OptionAdd(grid, _("Focus grid on click"), "Subtitle/Grid/Focus Allow");
 	p->OptionAdd(grid, _("Highlight visible subtitles"), "Subtitle/Grid/Highlight Subtitles in Frame");