Storyscreen: split common WML parsing code into a helper class
This commit is contained in:
parent
3ff2816fee
commit
e97b992393
8 changed files with 200 additions and 198 deletions
|
@ -1042,6 +1042,8 @@
|
||||||
<Unit filename="../../src/statistics.hpp" />
|
<Unit filename="../../src/statistics.hpp" />
|
||||||
<Unit filename="../../src/storyscreen/controller.cpp" />
|
<Unit filename="../../src/storyscreen/controller.cpp" />
|
||||||
<Unit filename="../../src/storyscreen/controller.hpp" />
|
<Unit filename="../../src/storyscreen/controller.hpp" />
|
||||||
|
<Unit filename="../../src/storyscreen/parser.cpp" />
|
||||||
|
<Unit filename="../../src/storyscreen/parser.hpp" />
|
||||||
<Unit filename="../../src/storyscreen/part.cpp" />
|
<Unit filename="../../src/storyscreen/part.cpp" />
|
||||||
<Unit filename="../../src/storyscreen/part.hpp" />
|
<Unit filename="../../src/storyscreen/part.hpp" />
|
||||||
<Unit filename="../../src/synced_checkup.cpp" />
|
<Unit filename="../../src/synced_checkup.cpp" />
|
||||||
|
|
|
@ -359,6 +359,7 @@ settings.cpp
|
||||||
side_filter.cpp
|
side_filter.cpp
|
||||||
statistics.cpp
|
statistics.cpp
|
||||||
storyscreen/controller.cpp
|
storyscreen/controller.cpp
|
||||||
|
storyscreen/parser.cpp
|
||||||
storyscreen/part.cpp
|
storyscreen/part.cpp
|
||||||
synced_checkup.cpp
|
synced_checkup.cpp
|
||||||
synced_commands.cpp
|
synced_commands.cpp
|
||||||
|
|
|
@ -21,11 +21,6 @@
|
||||||
#include "storyscreen/controller.hpp"
|
#include "storyscreen/controller.hpp"
|
||||||
#include "storyscreen/part.hpp"
|
#include "storyscreen/part.hpp"
|
||||||
|
|
||||||
#include "game_data.hpp"
|
|
||||||
#include "game_events/conditional_wml.hpp"
|
|
||||||
#include "game_events/manager.hpp"
|
|
||||||
#include "game_events/pump.hpp"
|
|
||||||
#include "gettext.hpp"
|
|
||||||
#include "log.hpp"
|
#include "log.hpp"
|
||||||
#include "resources.hpp"
|
#include "resources.hpp"
|
||||||
#include "variable.hpp"
|
#include "variable.hpp"
|
||||||
|
@ -46,98 +41,16 @@ controller::controller(const vconfig& data, const std::string& scenario_name)
|
||||||
resolve_wml(data);
|
resolve_wml(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void controller::resolve_wml(const vconfig& cfg)
|
void controller::resolve_wml_helper(const std::string& key, const vconfig& node)
|
||||||
{
|
{
|
||||||
for(vconfig::all_children_iterator i = cfg.ordered_begin(); i != cfg.ordered_end(); ++i) {
|
if(key == "part" && !node.empty()) {
|
||||||
// i->first and i->second are goddamn temporaries; do not make references
|
part_pointer_type const story_part(new part(node));
|
||||||
const std::string key = i->first;
|
// Use scenario name as part title if the WML doesn't supply a custom one.
|
||||||
const vconfig node = i->second;
|
if((*story_part).show_title() && (*story_part).title().empty()) {
|
||||||
|
(*story_part).set_title(scenario_name_);
|
||||||
if(key == "part" && !node.empty()) {
|
|
||||||
part_pointer_type const story_part(new part(node));
|
|
||||||
// Use scenario name as part title if the WML doesn't supply a custom one.
|
|
||||||
if((*story_part).show_title() && (*story_part).title().empty()) {
|
|
||||||
(*story_part).set_title(scenario_name_);
|
|
||||||
}
|
|
||||||
|
|
||||||
parts_.push_back(story_part);
|
|
||||||
}
|
}
|
||||||
// [if]
|
|
||||||
else if(key == "if") {
|
|
||||||
// check if the [if] tag has a [then] child;
|
|
||||||
// if we try to execute a non-existing [then], we get a segfault
|
|
||||||
if(game_events::conditional_passed(node)) {
|
|
||||||
if(node.has_child("then")) {
|
|
||||||
resolve_wml(node.child("then"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// condition not passed, check [elseif] and [else]
|
|
||||||
else {
|
|
||||||
// get all [elseif] children and set a flag
|
|
||||||
vconfig::child_list elseif_children = node.get_children("elseif");
|
|
||||||
bool elseif_flag = false;
|
|
||||||
// for each [elseif]: test if it has a [then] child
|
|
||||||
// if the condition matches, execute [then] and raise flag
|
|
||||||
for(vconfig::child_list::const_iterator elseif = elseif_children.begin();
|
|
||||||
elseif != elseif_children.end(); ++elseif) {
|
|
||||||
if(game_events::conditional_passed(*elseif)) {
|
|
||||||
if(elseif->has_child("then")) {
|
|
||||||
resolve_wml(elseif->child("then"));
|
|
||||||
}
|
|
||||||
|
|
||||||
elseif_flag = true;
|
parts_.push_back(story_part);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we have an [else] tag and no [elseif] was successful (flag not raised), execute it
|
|
||||||
if(node.has_child("else") && !elseif_flag) {
|
|
||||||
resolve_wml(node.child("else"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// [switch]
|
|
||||||
else if(key == "switch") {
|
|
||||||
const std::string var_name = node["variable"];
|
|
||||||
const std::string var_actual_value = resources::gamedata->get_variable_const(var_name);
|
|
||||||
bool case_not_found = true;
|
|
||||||
|
|
||||||
for(vconfig::all_children_iterator j = node.ordered_begin(); j != node.ordered_end(); ++j) {
|
|
||||||
if(j->first != "case") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enter all matching cases.
|
|
||||||
const std::string var_expected_value = (j->second)["value"];
|
|
||||||
if(var_actual_value == var_expected_value) {
|
|
||||||
case_not_found = false;
|
|
||||||
resolve_wml(j->second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(case_not_found) {
|
|
||||||
for(vconfig::all_children_iterator j = node.ordered_begin(); j != node.ordered_end(); ++j) {
|
|
||||||
if(j->first != "else") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enter all elses.
|
|
||||||
resolve_wml(j->second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// [deprecated_message]
|
|
||||||
else if(key == "deprecated_message") {
|
|
||||||
// Won't appear until the scenario start event finishes.
|
|
||||||
lg::wml_error() << node["message"] << '\n';
|
|
||||||
}
|
|
||||||
// [wml_message]
|
|
||||||
else if(key == "wml_message") {
|
|
||||||
// As with [deprecated_message],
|
|
||||||
// it won't appear until the scenario start event is complete.
|
|
||||||
resources::game_events->pump().put_wml_message(
|
|
||||||
node["logger"], node["message"], node["in_chat"].to_bool(false));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,18 +21,17 @@
|
||||||
#define STORYSCREEN_CONTROLLER_HPP_INCLUDED
|
#define STORYSCREEN_CONTROLLER_HPP_INCLUDED
|
||||||
|
|
||||||
#include "events.hpp"
|
#include "events.hpp"
|
||||||
|
#include "storyscreen/parser.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class vconfig;
|
|
||||||
|
|
||||||
namespace storyscreen
|
namespace storyscreen
|
||||||
{
|
{
|
||||||
class part;
|
class part;
|
||||||
class floating_image;
|
class floating_image;
|
||||||
|
|
||||||
class controller
|
class controller : private story_parser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef std::shared_ptr<part> part_pointer_type;
|
typedef std::shared_ptr<part> part_pointer_type;
|
||||||
|
@ -50,8 +49,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Executes WML flow instructions and inserts parts.
|
/** Inherited from story_parser. */
|
||||||
void resolve_wml(const vconfig& cfg);
|
virtual void resolve_wml_helper(const std::string& key, const vconfig& node) override;
|
||||||
|
|
||||||
std::string scenario_name_;
|
std::string scenario_name_;
|
||||||
|
|
||||||
|
|
118
src/storyscreen/parser.cpp
Normal file
118
src/storyscreen/parser.cpp
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2009 - 2017 by Ignacio R. Morelle <shadowm2006@gmail.com>
|
||||||
|
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||||
|
|
||||||
|
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 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY.
|
||||||
|
|
||||||
|
See the COPYING file for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "storyscreen/parser.hpp"
|
||||||
|
|
||||||
|
#include "game_data.hpp"
|
||||||
|
#include "game_events/conditional_wml.hpp"
|
||||||
|
#include "game_events/manager.hpp"
|
||||||
|
#include "game_events/pump.hpp"
|
||||||
|
#include "log.hpp"
|
||||||
|
#include "resources.hpp"
|
||||||
|
#include "variable.hpp"
|
||||||
|
|
||||||
|
namespace storyscreen
|
||||||
|
{
|
||||||
|
|
||||||
|
void story_parser::resolve_wml(const vconfig& cfg)
|
||||||
|
{
|
||||||
|
// Execution flow/branching/[image]
|
||||||
|
for(vconfig::all_children_iterator i = cfg.ordered_begin(); i != cfg.ordered_end(); ++i) {
|
||||||
|
// i->first and i->second are goddamn temporaries; do not make references
|
||||||
|
const std::string key = i->first;
|
||||||
|
const vconfig node = i->second;
|
||||||
|
|
||||||
|
// Execute any special actions derived classes provide.
|
||||||
|
resolve_wml_helper(key, node);
|
||||||
|
|
||||||
|
// [if]
|
||||||
|
if(key == "if") {
|
||||||
|
// check if the [if] tag has a [then] child;
|
||||||
|
// if we try to execute a non-existing [then], we get a segfault
|
||||||
|
if(game_events::conditional_passed(node)) {
|
||||||
|
if(node.has_child("then")) {
|
||||||
|
resolve_wml(node.child("then"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// condition not passed, check [elseif] and [else]
|
||||||
|
else {
|
||||||
|
// get all [elseif] children and set a flag
|
||||||
|
vconfig::child_list elseif_children = node.get_children("elseif");
|
||||||
|
bool elseif_flag = false;
|
||||||
|
// for each [elseif]: test if it has a [then] child
|
||||||
|
// if the condition matches, execute [then] and raise flag
|
||||||
|
for(vconfig::child_list::const_iterator elseif = elseif_children.begin();
|
||||||
|
elseif != elseif_children.end(); ++elseif) {
|
||||||
|
if(game_events::conditional_passed(*elseif)) {
|
||||||
|
if(elseif->has_child("then")) {
|
||||||
|
resolve_wml(elseif->child("then"));
|
||||||
|
}
|
||||||
|
|
||||||
|
elseif_flag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have an [else] tag and no [elseif] was successful (flag not raised), execute it
|
||||||
|
if(node.has_child("else") && !elseif_flag) {
|
||||||
|
resolve_wml(node.child("else"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// [switch]
|
||||||
|
else if(key == "switch") {
|
||||||
|
const std::string var_name = node["variable"];
|
||||||
|
const std::string var_actual_value = resources::gamedata->get_variable_const(var_name);
|
||||||
|
bool case_not_found = true;
|
||||||
|
|
||||||
|
for(vconfig::all_children_iterator j = node.ordered_begin(); j != node.ordered_end(); ++j) {
|
||||||
|
if(j->first != "case") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enter all matching cases.
|
||||||
|
const std::string var_expected_value = (j->second)["value"];
|
||||||
|
if(var_actual_value == var_expected_value) {
|
||||||
|
case_not_found = false;
|
||||||
|
resolve_wml(j->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(case_not_found) {
|
||||||
|
for(vconfig::all_children_iterator j = node.ordered_begin(); j != node.ordered_end(); ++j) {
|
||||||
|
if(j->first != "else") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enter all elses.
|
||||||
|
resolve_wml(j->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// [deprecated_message]
|
||||||
|
else if(key == "deprecated_message") {
|
||||||
|
// Won't appear until the scenario start event finishes.
|
||||||
|
lg::wml_error() << node["message"] << '\n';
|
||||||
|
}
|
||||||
|
// [wml_message]
|
||||||
|
else if(key == "wml_message") {
|
||||||
|
// As with [deprecated_message],
|
||||||
|
// it won't appear until the scenario start event is complete.
|
||||||
|
resources::game_events->pump().put_wml_message(
|
||||||
|
node["logger"], node["message"], node["in_chat"].to_bool(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace storyscreen
|
48
src/storyscreen/parser.hpp
Normal file
48
src/storyscreen/parser.hpp
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2009 - 2017 by Ignacio R. Morelle <shadowm2006@gmail.com>
|
||||||
|
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||||
|
|
||||||
|
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 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY.
|
||||||
|
|
||||||
|
See the COPYING file for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STORYSCREEN_PARSER_HPP_INCLUDED
|
||||||
|
#define STORYSCREEN_PARSER_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class vconfig;
|
||||||
|
|
||||||
|
namespace storyscreen
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Small helper class to encapsulate the common logic for parsing storyscreen WML.
|
||||||
|
*/
|
||||||
|
class story_parser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
story_parser() = default;
|
||||||
|
|
||||||
|
story_parser(const story_parser&) = delete;
|
||||||
|
operator=(const story_parser&) = delete;
|
||||||
|
|
||||||
|
/** Takes care of initializing and branching properties. */
|
||||||
|
virtual void resolve_wml(const vconfig& cfg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* May be implemented by derived classes to perform additional actions
|
||||||
|
* When executing @ref resolve_wml.
|
||||||
|
*/
|
||||||
|
virtual void resolve_wml_helper(const std::string& key, const vconfig& node) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace storyscreen
|
||||||
|
|
||||||
|
#endif
|
|
@ -20,13 +20,6 @@
|
||||||
#include "storyscreen/part.hpp"
|
#include "storyscreen/part.hpp"
|
||||||
|
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "game_data.hpp"
|
|
||||||
#include "game_events/conditional_wml.hpp"
|
|
||||||
#include "game_events/manager.hpp"
|
|
||||||
#include "game_events/pump.hpp"
|
|
||||||
#include "log.hpp"
|
|
||||||
#include "resources.hpp"
|
|
||||||
#include "serialization/string_utils.hpp"
|
|
||||||
#include "variable.hpp"
|
#include "variable.hpp"
|
||||||
|
|
||||||
namespace storyscreen
|
namespace storyscreen
|
||||||
|
@ -246,96 +239,19 @@ void part::resolve_wml(const vconfig& cfg)
|
||||||
sound_ = cfg["sound"].str();
|
sound_ = cfg["sound"].str();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execution flow/branching/[image]
|
// Inherited
|
||||||
for(vconfig::all_children_iterator i = cfg.ordered_begin(); i != cfg.ordered_end(); ++i) {
|
story_parser::resolve_wml(cfg);
|
||||||
// i->first and i->second are goddamn temporaries; do not make references
|
}
|
||||||
const std::string key = i->first;
|
|
||||||
const vconfig node = i->second;
|
|
||||||
|
|
||||||
// [background_layer]
|
void part::resolve_wml_helper(const std::string& key, const vconfig& node)
|
||||||
if(key == "background_layer") {
|
{
|
||||||
background_layers_.push_back(node.get_parsed_config());
|
// [background_layer]
|
||||||
}
|
if(key == "background_layer") {
|
||||||
// [image]
|
background_layers_.push_back(node.get_parsed_config());
|
||||||
else if(key == "image") {
|
}
|
||||||
floating_images_.push_back(node.get_parsed_config());
|
// [image]
|
||||||
}
|
else if(key == "image") {
|
||||||
// [if]
|
floating_images_.push_back(node.get_parsed_config());
|
||||||
else if(key == "if") {
|
|
||||||
// check if the [if] tag has a [then] child;
|
|
||||||
// if we try to execute a non-existing [then], we get a segfault
|
|
||||||
if(game_events::conditional_passed(node)) {
|
|
||||||
if(node.has_child("then")) {
|
|
||||||
resolve_wml(node.child("then"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// condition not passed, check [elseif] and [else]
|
|
||||||
else {
|
|
||||||
// get all [elseif] children and set a flag
|
|
||||||
vconfig::child_list elseif_children = node.get_children("elseif");
|
|
||||||
bool elseif_flag = false;
|
|
||||||
// for each [elseif]: test if it has a [then] child
|
|
||||||
// if the condition matches, execute [then] and raise flag
|
|
||||||
for(vconfig::child_list::const_iterator elseif = elseif_children.begin();
|
|
||||||
elseif != elseif_children.end(); ++elseif) {
|
|
||||||
if(game_events::conditional_passed(*elseif)) {
|
|
||||||
if(elseif->has_child("then")) {
|
|
||||||
resolve_wml(elseif->child("then"));
|
|
||||||
}
|
|
||||||
|
|
||||||
elseif_flag = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we have an [else] tag and no [elseif] was successful (flag not raised), execute it
|
|
||||||
if(node.has_child("else") && !elseif_flag) {
|
|
||||||
resolve_wml(node.child("else"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// [switch]
|
|
||||||
else if(key == "switch") {
|
|
||||||
const std::string var_name = node["variable"];
|
|
||||||
const std::string var_actual_value = resources::gamedata->get_variable_const(var_name);
|
|
||||||
bool case_not_found = true;
|
|
||||||
|
|
||||||
for(vconfig::all_children_iterator j = node.ordered_begin(); j != node.ordered_end(); ++j) {
|
|
||||||
if(j->first != "case") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enter all matching cases.
|
|
||||||
const std::string var_expected_value = (j->second)["value"];
|
|
||||||
if(var_actual_value == var_expected_value) {
|
|
||||||
case_not_found = false;
|
|
||||||
resolve_wml(j->second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(case_not_found) {
|
|
||||||
for(vconfig::all_children_iterator j = node.ordered_begin(); j != node.ordered_end(); ++j) {
|
|
||||||
if(j->first != "else") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enter all elses.
|
|
||||||
resolve_wml(j->second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// [deprecated_message]
|
|
||||||
else if(key == "deprecated_message") {
|
|
||||||
// Won't appear until the scenario start event finishes.
|
|
||||||
lg::wml_error() << node["message"] << '\n';
|
|
||||||
}
|
|
||||||
// [wml_message]
|
|
||||||
else if(key == "wml_message") {
|
|
||||||
// As with [deprecated_message],
|
|
||||||
// it won't appear until the scenario start event is complete.
|
|
||||||
resources::game_events->pump().put_wml_message(
|
|
||||||
node["logger"], node["message"], node["in_chat"].to_bool(false));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
#ifndef STORYSCREEN_PART_HPP_INCLUDED
|
#ifndef STORYSCREEN_PART_HPP_INCLUDED
|
||||||
#define STORYSCREEN_PART_HPP_INCLUDED
|
#define STORYSCREEN_PART_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include "storyscreen/parser.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -220,7 +222,7 @@ private:
|
||||||
/**
|
/**
|
||||||
* Represents and contains information about a single storyscreen part.
|
* Represents and contains information about a single storyscreen part.
|
||||||
*/
|
*/
|
||||||
class part
|
class part : private story_parser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
@ -333,8 +335,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Takes care of initializing and branching properties. */
|
/** Inherited from story_parser. */
|
||||||
void resolve_wml(const vconfig& cfg);
|
virtual void resolve_wml(const vconfig& cfg) override;
|
||||||
|
|
||||||
|
/** Inherited from story_parser. */
|
||||||
|
virtual void resolve_wml_helper(const std::string& key, const vconfig& node) override;
|
||||||
|
|
||||||
static BLOCK_LOCATION string_tblock_loc(const std::string& s);
|
static BLOCK_LOCATION string_tblock_loc(const std::string& s);
|
||||||
static TEXT_ALIGNMENT string_title_align(const std::string& s);
|
static TEXT_ALIGNMENT string_title_align(const std::string& s);
|
||||||
|
|
Loading…
Add table
Reference in a new issue