diff --git a/Mods/vcmi/config/vcmi/english.json b/Mods/vcmi/config/vcmi/english.json index b66770c34..576496bc1 100644 --- a/Mods/vcmi/config/vcmi/english.json +++ b/Mods/vcmi/config/vcmi/english.json @@ -163,8 +163,7 @@ "vcmi.systemOptions.townsGroup" : "Town Screen", "vcmi.statisticWindow.statistic" : "Statistic", - "vcmi.statisticWindow.csvSave" : "Save as CSV", - "vcmi.statisticWindow.csvSaved" : "CSV saved!", + "vcmi.statisticWindow.tsvCopy" : "Data to clipboard", "vcmi.statisticWindow.selectView" : "Select view", "vcmi.statisticWindow.value" : "Value", "vcmi.statisticWindow.title.overview" : "Overview", diff --git a/Mods/vcmi/config/vcmi/german.json b/Mods/vcmi/config/vcmi/german.json index eedf3d44e..effa242e0 100644 --- a/Mods/vcmi/config/vcmi/german.json +++ b/Mods/vcmi/config/vcmi/german.json @@ -163,8 +163,7 @@ "vcmi.systemOptions.townsGroup" : "Stadt-Bildschirm", "vcmi.statisticWindow.statistic" : "Statistik", - "vcmi.statisticWindow.csvSave" : "Speichere als CSV", - "vcmi.statisticWindow.csvSaved" : "CSV gespeichert!", + "vcmi.statisticWindow.tsvCopy" : "Daten in Zwischenabl.", "vcmi.statisticWindow.selectView" : "Ansicht wählen", "vcmi.statisticWindow.value" : "Wert", "vcmi.statisticWindow.title.overview" : "Überblick", diff --git a/client/eventsSDL/InputHandler.cpp b/client/eventsSDL/InputHandler.cpp index 52d3776a2..bb9aab6bd 100644 --- a/client/eventsSDL/InputHandler.cpp +++ b/client/eventsSDL/InputHandler.cpp @@ -32,6 +32,7 @@ #include #include +#include InputHandler::InputHandler() : enableMouse(settings["input"]["enableMouse"].Bool()) @@ -142,6 +143,11 @@ InputMode InputHandler::getCurrentInputMode() return currentInputMode; } +void InputHandler::copyToClipBoard(std::string text) +{ + SDL_SetClipboardText(text.c_str()); +} + std::vector InputHandler::acquireEvents() { boost::unique_lock lock(eventsMutex); diff --git a/client/eventsSDL/InputHandler.h b/client/eventsSDL/InputHandler.h index 0b930498f..bcee9d0ea 100644 --- a/client/eventsSDL/InputHandler.h +++ b/client/eventsSDL/InputHandler.h @@ -103,4 +103,6 @@ public: bool isKeyboardShiftDown() const; InputMode getCurrentInputMode(); + + void copyToClipBoard(std::string text); }; diff --git a/client/mainmenu/CStatisticScreen.cpp b/client/mainmenu/CStatisticScreen.cpp index 1d7c72955..a78f78bed 100644 --- a/client/mainmenu/CStatisticScreen.cpp +++ b/client/mainmenu/CStatisticScreen.cpp @@ -15,6 +15,7 @@ #include "../gui/CGuiHandler.h" #include "../gui/WindowHandler.h" +#include "../eventsSDL/InputHandler.h" #include "../gui/Shortcut.h" #include "../render/Graphics.h" @@ -74,23 +75,20 @@ CStatisticScreen::CStatisticScreen(StatisticDataSet stat) }); buttonSelect->setTextOverlay(CGI->generaltexth->translate("vcmi.statisticWindow.selectView"), EFonts::FONT_SMALL, Colors::YELLOW); - buttonCsvSave = std::make_shared(Point(150, 564), AnimationPath::builtin("GSPBUT2"), CButton::tooltip(), [this](bool on){ - std::string path = statistic.writeCsv(); - CInfoWindow::showInfoDialog(CGI->generaltexth->translate("vcmi.statisticWindow.csvSaved") + "\n\n" + path, {}); - }); - buttonCsvSave->setTextOverlay(CGI->generaltexth->translate("vcmi.statisticWindow.csvSave"), EFonts::FONT_SMALL, Colors::YELLOW); + buttonCsvSave = std::make_shared(Point(150, 564), AnimationPath::builtin("GSPBUT2"), CButton::tooltip(), [this](bool on){ GH.input().copyToClipBoard(statistic.toCsv("\t")); }); + buttonCsvSave->setTextOverlay(CGI->generaltexth->translate("vcmi.statisticWindow.tsvCopy"), EFonts::FONT_SMALL, Colors::YELLOW); mainContent = getContent(OVERVIEW, EGameResID::NONE); } -std::map> CStatisticScreen::extractData(StatisticDataSet stat, std::function selector) +TData CStatisticScreen::extractData(StatisticDataSet stat, std::function selector) { auto tmpData = stat.data; std::sort(tmpData.begin(), tmpData.end(), [](StatisticDataSetEntry v1, StatisticDataSetEntry v2){ return v1.player == v2.player ? v1.day < v2.day : v1.player < v2.player; }); PlayerColor tmpColor = PlayerColor::NEUTRAL; std::vector tmpColorSet; - std::map> plotData; + TData plotData; for(auto & val : tmpData) { if(tmpColor != val.player) @@ -114,8 +112,8 @@ std::map> CStatisticScreen::extractData(StatisticD std::shared_ptr CStatisticScreen::getContent(Content c, EGameResID res) { - LineChart::TData plotData; - LineChart::TIcons icons; + TData plotData; + TIcons icons; switch (c) { diff --git a/client/mainmenu/CStatisticScreen.h b/client/mainmenu/CStatisticScreen.h index 96d6ebc13..8ffe07f63 100644 --- a/client/mainmenu/CStatisticScreen.h +++ b/client/mainmenu/CStatisticScreen.h @@ -20,6 +20,9 @@ class ComboBox; class CSlider; class IImage; +using TData = std::map>; +using TIcons = std::map>>>; + class CStatisticScreen : public CWindowObject { enum Content { @@ -61,7 +64,7 @@ class CStatisticScreen : public CWindowObject std::shared_ptr mainContent; Rect contentArea; - std::map> extractData(StatisticDataSet stat, std::function selector); + TData extractData(StatisticDataSet stat, std::function selector); std::shared_ptr getContent(Content c, EGameResID res); public: CStatisticScreen(StatisticDataSet stat); @@ -116,9 +119,6 @@ class LineChart : public CIntObject void updateStatusBar(const Point & cursorPosition); public: - using TData = std::map>; - using TIcons = std::map>>>; - LineChart(Rect position, std::string title, TData data, TIcons icons, float maxY); void mouseMoved(const Point & cursorPosition, const Point & lastUpdateDistance) override; diff --git a/lib/gameState/GameStatistics.cpp b/lib/gameState/GameStatistics.cpp index 35721f4a1..c66f00035 100644 --- a/lib/gameState/GameStatistics.cpp +++ b/lib/gameState/GameStatistics.cpp @@ -81,98 +81,98 @@ StatisticDataSetEntry StatisticDataSet::createEntry(const PlayerState * ps, cons return data; } -std::string StatisticDataSet::toCsv() +std::string StatisticDataSet::toCsv(std::string sep) { std::stringstream ss; auto resources = std::vector{EGameResID::GOLD, EGameResID::WOOD, EGameResID::MERCURY, EGameResID::ORE, EGameResID::SULFUR, EGameResID::CRYSTAL, EGameResID::GEMS}; - ss << "Map" << ";"; - ss << "Timestamp" << ";"; - ss << "Day" << ";"; - ss << "Player" << ";"; - ss << "PlayerName" << ";"; - ss << "Team" << ";"; - ss << "IsHuman" << ";"; - ss << "Status" << ";"; - ss << "NumberHeroes" << ";"; - ss << "NumberTowns" << ";"; - ss << "NumberArtifacts" << ";"; - ss << "NumberDwellings" << ";"; - ss << "ArmyStrength" << ";"; - ss << "TotalExperience" << ";"; - ss << "Income" << ";"; - ss << "MapExploredRatio" << ";"; - ss << "ObeliskVisitedRatio" << ";"; - ss << "TownBuiltRatio" << ";"; - ss << "HasGrail" << ";"; - ss << "Score" << ";"; - ss << "MaxHeroLevel" << ";"; - ss << "NumBattlesNeutral" << ";"; - ss << "NumBattlesPlayer" << ";"; - ss << "NumWinBattlesNeutral" << ";"; - ss << "NumWinBattlesPlayer" << ";"; - ss << "NumHeroSurrendered" << ";"; - ss << "NumHeroEscaped" << ";"; - ss << "EventCapturedTown" << ";"; - ss << "EventDefeatedStrongestHero" << ";"; - ss << "EventRansackingDragonUtopia" << ";"; + ss << "Map" << sep; + ss << "Timestamp" << sep; + ss << "Day" << sep; + ss << "Player" << sep; + ss << "PlayerName" << sep; + ss << "Team" << sep; + ss << "IsHuman" << sep; + ss << "Status" << sep; + ss << "NumberHeroes" << sep; + ss << "NumberTowns" << sep; + ss << "NumberArtifacts" << sep; + ss << "NumberDwellings" << sep; + ss << "ArmyStrength" << sep; + ss << "TotalExperience" << sep; + ss << "Income" << sep; + ss << "MapExploredRatio" << sep; + ss << "ObeliskVisitedRatio" << sep; + ss << "TownBuiltRatio" << sep; + ss << "HasGrail" << sep; + ss << "Score" << sep; + ss << "MaxHeroLevel" << sep; + ss << "NumBattlesNeutral" << sep; + ss << "NumBattlesPlayer" << sep; + ss << "NumWinBattlesNeutral" << sep; + ss << "NumWinBattlesPlayer" << sep; + ss << "NumHeroSurrendered" << sep; + ss << "NumHeroEscaped" << sep; + ss << "EventCapturedTown" << sep; + ss << "EventDefeatedStrongestHero" << sep; + ss << "EventRansackingDragonUtopia" << sep; ss << "MovementPointsUsed"; for(auto & resource : resources) - ss << ";" << GameConstants::RESOURCE_NAMES[resource]; + ss << sep << GameConstants::RESOURCE_NAMES[resource]; for(auto & resource : resources) - ss << ";" << GameConstants::RESOURCE_NAMES[resource] + "Mines"; + ss << sep << GameConstants::RESOURCE_NAMES[resource] + "Mines"; for(auto & resource : resources) - ss << ";" << GameConstants::RESOURCE_NAMES[resource] + "SpentResourcesForArmy"; + ss << sep << GameConstants::RESOURCE_NAMES[resource] + "SpentResourcesForArmy"; for(auto & resource : resources) - ss << ";" << GameConstants::RESOURCE_NAMES[resource] + "SpentResourcesForBuildings"; + ss << sep << GameConstants::RESOURCE_NAMES[resource] + "SpentResourcesForBuildings"; for(auto & resource : resources) - ss << ";" << GameConstants::RESOURCE_NAMES[resource] + "TradeVolume"; + ss << sep << GameConstants::RESOURCE_NAMES[resource] + "TradeVolume"; ss << "\r\n"; for(auto & entry : data) { - ss << entry.map << ";"; - ss << vstd::getFormattedDateTime(entry.timestamp, "%Y-%m-%dT%H:%M:%S") << ";"; - ss << entry.day << ";"; - ss << GameConstants::PLAYER_COLOR_NAMES[entry.player] << ";"; - ss << entry.playerName << ";"; - ss << entry.team.getNum() << ";"; - ss << entry.isHuman << ";"; - ss << static_cast(entry.status) << ";"; - ss << entry.numberHeroes << ";"; - ss << entry.numberTowns << ";"; - ss << entry.numberArtifacts << ";"; - ss << entry.numberDwellings << ";"; - ss << entry.armyStrength << ";"; - ss << entry.totalExperience << ";"; - ss << entry.income << ";"; - ss << entry.mapExploredRatio << ";"; - ss << entry.obeliskVisitedRatio << ";"; - ss << entry.townBuiltRatio << ";"; - ss << entry.hasGrail << ";"; - ss << entry.score << ";"; - ss << entry.maxHeroLevel << ";"; - ss << entry.numBattlesNeutral << ";"; - ss << entry.numBattlesPlayer << ";"; - ss << entry.numWinBattlesNeutral << ";"; - ss << entry.numWinBattlesPlayer << ";"; - ss << entry.numHeroSurrendered << ";"; - ss << entry.numHeroEscaped << ";"; - ss << entry.eventCapturedTown << ";"; - ss << entry.eventDefeatedStrongestHero << ";"; - ss << entry.eventRansackingDragonUtopia << ";"; + ss << entry.map << sep; + ss << vstd::getFormattedDateTime(entry.timestamp, "%Y-%m-%dT%H:%M:%S") << sep; + ss << entry.day << sep; + ss << GameConstants::PLAYER_COLOR_NAMES[entry.player] << sep; + ss << entry.playerName << sep; + ss << entry.team.getNum() << sep; + ss << entry.isHuman << sep; + ss << static_cast(entry.status) << sep; + ss << entry.numberHeroes << sep; + ss << entry.numberTowns << sep; + ss << entry.numberArtifacts << sep; + ss << entry.numberDwellings << sep; + ss << entry.armyStrength << sep; + ss << entry.totalExperience << sep; + ss << entry.income << sep; + ss << entry.mapExploredRatio << sep; + ss << entry.obeliskVisitedRatio << sep; + ss << entry.townBuiltRatio << sep; + ss << entry.hasGrail << sep; + ss << entry.score << sep; + ss << entry.maxHeroLevel << sep; + ss << entry.numBattlesNeutral << sep; + ss << entry.numBattlesPlayer << sep; + ss << entry.numWinBattlesNeutral << sep; + ss << entry.numWinBattlesPlayer << sep; + ss << entry.numHeroSurrendered << sep; + ss << entry.numHeroEscaped << sep; + ss << entry.eventCapturedTown << sep; + ss << entry.eventDefeatedStrongestHero << sep; + ss << entry.eventRansackingDragonUtopia << sep; ss << entry.movementPointsUsed; for(auto & resource : resources) - ss << ";" << entry.resources[resource]; + ss << sep << entry.resources[resource]; for(auto & resource : resources) - ss << ";" << entry.numMines[resource]; + ss << sep << entry.numMines[resource]; for(auto & resource : resources) - ss << ";" << entry.spentResourcesForArmy[resource]; + ss << sep << entry.spentResourcesForArmy[resource]; for(auto & resource : resources) - ss << ";" << entry.spentResourcesForBuildings[resource]; + ss << sep << entry.spentResourcesForBuildings[resource]; for(auto & resource : resources) - ss << ";" << entry.tradeVolume[resource]; + ss << sep << entry.tradeVolume[resource]; ss << "\r\n"; } @@ -186,7 +186,7 @@ std::string StatisticDataSet::writeCsv() const boost::filesystem::path filePath = outPath / (vstd::getDateTimeISO8601Basic(std::time(nullptr)) + ".csv"); std::ofstream file(filePath.c_str()); - std::string csv = toCsv(); + std::string csv = toCsv(";"); file << csv; return filePath.string(); diff --git a/lib/gameState/GameStatistics.h b/lib/gameState/GameStatistics.h index 19d627813..8ccf734f8 100644 --- a/lib/gameState/GameStatistics.h +++ b/lib/gameState/GameStatistics.h @@ -104,7 +104,7 @@ class DLL_LINKAGE StatisticDataSet public: void add(StatisticDataSetEntry entry); static StatisticDataSetEntry createEntry(const PlayerState * ps, const CGameState * gs); - std::string toCsv(); + std::string toCsv(std::string sep); std::string writeCsv(); struct PlayerAccumulatedValueStorage // holds some actual values needed for stats