Skip to content

Commit

Permalink
Help Viewer: Parse help markup to Pango markup... mostly.
Browse files Browse the repository at this point in the history
The help parser now outputs a config rather than a vector of strings of
which some should be taken literally and others parsed as WML.
  • Loading branch information
CelticMinstrel committed Oct 24, 2018
1 parent 02b472b commit 3ddd567
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 45 deletions.
67 changes: 66 additions & 1 deletion src/gui/dialogs/help_browser.cpp
Expand Up @@ -37,6 +37,8 @@

#include "help/help.hpp"
#include "help/help_impl.hpp"
#include "font/pango/escape.hpp"
#include "font/pango/hyperlink.hpp"

namespace gui2
{
Expand Down Expand Up @@ -85,6 +87,69 @@ void help_browser::add_topic(window& window, const std::string& topic_id, const
topic_tree.add_node("topic", data).set_id(std::string(expands ? "+" : "-") + topic_id);
}

static std::string format_help_text(const config& cfg) {
std::stringstream ss;
for(auto& item : cfg.all_children_range()) {
if(item.key == "text") {
ss << font::escape_text(item.cfg["text"]);
} else if(item.key == "ref") {
if(item.cfg["dst"].empty()) {
std::stringstream msg;
msg << "Ref markup must have dst attribute. Please submit a bug"
" report if you have not modified the game files yourself. Erroneous config: " << cfg;
throw help::parse_error(msg.str());
};
// TODO: Get the proper link shade from somewhere
ss << font::format_as_link(font::escape_text(item.cfg["text"]), "#aaaa00");
} else if(item.key == "img") {
if(item.cfg["src"].empty()) {
throw help::parse_error("Img markup must have src attribute.");
}
// For now, just output a placeholder so we know an image is supposed to be there.
ss << "[img]" << font::escape_text(item.cfg["src"]) << "[/img]";
} else if(item.key == "bold") {
if(item.cfg["text"].empty()) {
throw help::parse_error("Bold markup must have text attribute.");
}
ss << "<b>" << font::escape_text(item.cfg["text"]) << "</b>";
} else if(item.key == "italic") {
if(item.cfg["text"].empty()) {
throw help::parse_error("Italic markup must have text attribute.");
}
ss << "<i>" << font::escape_text(item.cfg["text"]) << "</i>";
} else if(item.key == "header") {
if(item.cfg["text"].empty()) {
throw help::parse_error("Header markup must have text attribute.");
}
ss << "<big>" << font::escape_text(item.cfg["text"]) << "</big>";
} else if(item.key == "jump") {
// Seems like this creates something invisible?
if(item.cfg["amount"].empty() && item.cfg["to"].empty()) {
throw help::parse_error("Jump markup must have either a to or an amount attribute.");
}
} else if(item.key == "format") {
if(item.cfg["text"].empty()) {
throw help::parse_error("Header markup must have text attribute.");
}
ss << "<span";
if(item.cfg.has_attribute("font_size")) {
ss << " size='" << item.cfg["font_size"].to_int(font::SIZE_NORMAL) << "'";
}
if(item.cfg.has_attribute("color")) {
ss << " color='" << help::string_to_color(item.cfg["color"]).to_hex_string() << "'";
}
if(item.cfg["bold"]) {
ss << " font_weight='bold'";
}
if(item.cfg["italic"]) {
ss << " font_style='italic'";
}
ss << '>' << font::escape_text(item.cfg["text"]) << "</span>";
}
}
return ss.str();
}

void help_browser::on_topic_select(window& window)
{
multi_page& topic_pages = find_widget<multi_page>(&window, "topic_text_pages", false);
Expand Down Expand Up @@ -118,7 +183,7 @@ void help_browser::on_topic_select(window& window)
std::map<std::string, string_map> data;
string_map item;

item["label"] = utils::join(topic->text.parsed_text(), "");
item["label"] = format_help_text(topic->text.parsed_text());
data.emplace("topic_text", item);

parsed_pages_.emplace(topic_id, topic_pages.get_page_count());
Expand Down
26 changes: 18 additions & 8 deletions src/help/help_impl.cpp
Expand Up @@ -32,6 +32,7 @@
#include "units/race.hpp" // for unit_race, etc
#include "resources.hpp" // for tod_manager, config_manager
#include "sdl/surface.hpp" // for surface
#include "serialization/parser.hpp"
#include "serialization/string_utils.hpp" // for split, quoted_split, etc
#include "serialization/unicode_cast.hpp" // for unicode_cast
#include "serialization/utf8_exception.hpp" // for char_t, etc
Expand All @@ -44,6 +45,7 @@
#include "units/types.hpp" // for unit_type, unit_type_data, etc
#include "serialization/unicode.hpp" // for iterator
#include "color.hpp"
#include "config_assign.hpp"

#include <cassert> // for assert
#include <algorithm> // for sort, find, transform, etc
Expand Down Expand Up @@ -344,7 +346,7 @@ topic_text &topic_text::operator=(topic_generator *g)
return *this;
}

const std::vector<std::string>& topic_text::parsed_text() const
const config& topic_text::parsed_text() const
{
if (generator_) {
parsed_text_ = parse_text((*generator_)());
Expand Down Expand Up @@ -1168,9 +1170,9 @@ const section *find_section(const section &sec, const std::string &id)
return nullptr;
}

std::vector<std::string> parse_text(const std::string &text)
config parse_text(const std::string &text)
{
std::vector<std::string> res;
config res;
bool last_char_escape = false;
const char escape_char = '\\';
std::stringstream ss;
Expand All @@ -1188,7 +1190,7 @@ std::vector<std::string> parse_text(const std::string &text)
ss << c;
}
else {
res.push_back(ss.str());
res.add_child("text", config_of("text", ss.str()));
ss.str("");
state = ELEMENT_NAME;
}
Expand All @@ -1215,10 +1217,18 @@ std::vector<std::string> parse_text(const std::string &text)
msg << "Unterminated element: " << element_name;
throw parse_error(msg.str());
}
s.str("");
const std::string contents = text.substr(pos + 1, end_pos - pos - 1);
const std::string element = convert_to_wml(element_name, contents);
res.push_back(element);
s.str(convert_to_wml(element_name, contents));
s.seekg(0);
try {
config cfg;
read(cfg, s);
res.append_children(cfg);
} catch(config::error& e) {
std::stringstream msg;
msg << "Error when parsing help markup as WML: '" << e.message << "'";
throw parse_error(msg.str());
}
pos = end_pos + end_element_name.size() - 1;
state = OTHER;
}
Expand All @@ -1236,7 +1246,7 @@ std::vector<std::string> parse_text(const std::string &text)
}
if (!ss.str().empty()) {
// Add the last string.
res.push_back(ss.str());
res.add_child("text", config_of("text", ss.str()));
}
return res;
}
Expand Down
6 changes: 3 additions & 3 deletions src/help/help_impl.hpp
Expand Up @@ -80,7 +80,7 @@ class text_topic_generator: public topic_generator {
/// contained in generator_.
class topic_text
{
mutable std::vector< std::string > parsed_text_;
mutable config parsed_text_;
mutable topic_generator *generator_;
public:
~topic_text();
Expand All @@ -104,7 +104,7 @@ class topic_text
topic_text &operator=(topic_generator *g);
topic_text(const topic_text& t);

const std::vector<std::string>& parsed_text() const;
const config& parsed_text() const;
};

/// A topic contains a title, an id and some text.
Expand Down Expand Up @@ -287,7 +287,7 @@ const section *find_section(const section &sec, const std::string &id);
/// Parse a text string. Return a vector with the different parts of the
/// text. Each markup item is a separate part while the text between
/// markups are separate parts.
std::vector<std::string> parse_text(const std::string &text);
config parse_text(const std::string &text);

/// Convert the contents to wml attributes, surrounded within
/// [element_name]...[/element_name]. Return the resulting WML.
Expand Down
51 changes: 18 additions & 33 deletions src/help/help_text_area.cpp
Expand Up @@ -113,41 +113,22 @@ void help_text_area::set_items()
down_one_line();
}
// Parse and add the text.
const std::vector<std::string>& parsed_items = shown_topic_->text.parsed_text();
std::vector<std::string>::const_iterator it;
for (it = parsed_items.begin(); it != parsed_items.end(); ++it) {
if (!(*it).empty() && (*it)[0] == '[') {
// Should be parsed as WML.
try {
config cfg;
std::istringstream stream(*it);
read(cfg, stream);

const config& parsed_items = shown_topic_->text.parsed_text();
for(auto& item : parsed_items.all_children_range()) {
#define TRY(name) do { \
if (config &child = cfg.child(#name)) \
handle_##name##_cfg(child); \
} while (0)

TRY(ref);
TRY(img);
TRY(bold);
TRY(italic);
TRY(header);
TRY(jump);
TRY(format);

if (config &child = item.cfg.child(#name)) \
handle_##name##_cfg(child); \
} while (0)

TRY(text);
TRY(ref);
TRY(img);
TRY(bold);
TRY(italic);
TRY(header);
TRY(jump);
TRY(format);
#undef TRY

}
catch (config::error& e) {
std::stringstream msg;
msg << "Error when parsing help markup as WML: '" << e.message << "'";
throw parse_error(msg.str());
}
}
else {
add_text_item(*it);
}
}
down_one_line(); // End the last line.
int h = height();
Expand Down Expand Up @@ -290,6 +271,10 @@ void help_text_area::handle_format_cfg(const config &cfg)
add_text_item(text, "", false, font_size, bold, italic, color);
}

void help_text_area::handle_text_cfg(const config& cfg) {
add_text_item(cfg["text"]);
}

void help_text_area::add_text_item(const std::string& text, const std::string& ref_dst,
bool broken_link, int _font_size, bool bold, bool italic,
color_t text_color
Expand Down
1 change: 1 addition & 0 deletions src/help/help_text_area.hpp
Expand Up @@ -104,6 +104,7 @@ class help_text_area : public gui::scrollarea
void handle_header_cfg(const config &cfg);
void handle_jump_cfg(const config &cfg);
void handle_format_cfg(const config &cfg);
void handle_text_cfg(const config &cfg);

void draw_contents();

Expand Down

0 comments on commit 3ddd567

Please sign in to comment.