diff --git a/application/editormode.cpp b/application/editormode.cpp index 952697fa..8b31acf4 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -527,7 +527,7 @@ editor_mode::editor_mode() { std::string reason; if (Node->from_template() || false == scene::Layers.editable(Node, &reason)) { - ui()->set_status(format(STR_C("\"%s\" can't be edited: %s"), (Node->name().empty() ? std::string{STR_C("(no name)")} : Node->name()).c_str(), reason.c_str()), true); + ui()->set_status(format(STR_C("\"%s\" can't be edited: %s"), name_of(*Node).c_str(), reason.c_str()), true); return; } m_track_window_open = false; @@ -6594,7 +6594,7 @@ void editor_mode::on_mouse_button(int const Button, int const Action, int const } else if (node->from_template() || false == scene::Layers.editable(node, &reason)) { - ui()->set_status(format(STR_C("\"%s\" can't be edited: %s"), (node->name().empty() ? std::string{STR_C("(no name)")} : node->name()).c_str(), reason.c_str()), true); + ui()->set_status(format(STR_C("\"%s\" can't be edited: %s"), name_of(*node).c_str(), reason.c_str()), true); node = nullptr; } } diff --git a/application/editormodedigitize.cpp b/application/editormodedigitize.cpp index 9eb5f83e..99ea6d26 100644 --- a/application/editormodedigitize.cpp +++ b/application/editormodedigitize.cpp @@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "application/editormode.h" +#include "application/editormodeshared.h" #include "application/editoruilayer.h" #include "editor/editorAlignmentStore.hpp" #include "editor/editorConnections.hpp" @@ -328,7 +329,7 @@ void editor_mode::digitize_crossovers(digitize_report &Report, bool const Write) if (spacing < kSpacingLeast || spacing > kSpacingMost) continue; ++Report.crossovers; - auto const name = [](TTrack const *Track) { return Track->name().empty() ? STR("(no name)") : Track->name(); }; + auto const name = [](TTrack const *Track) { return name_of(*Track); }; auto const note = [&](std::string const &Why) { Report.notes.push_back(name(first) + " + " + name(second) + ": " + Why); }; if (false == switch_store::connections(*first).empty()) { diff --git a/application/editormodeinfra.cpp b/application/editormodeinfra.cpp index 2644d07c..b8bdfd2e 100644 --- a/application/editormodeinfra.cpp +++ b/application/editormodeinfra.cpp @@ -134,13 +134,6 @@ struct include_place std::string category; // of the description of the template }; -bool numeric(std::string const &Text, double &Value) -{ - char *end{nullptr}; - Value = std::strtod(Text.c_str(), &end); - return false == Text.empty() && *end == '\0'; -} - bool include_place_of(scene::instance_handle const Instance, include_place &Place) { if (false == scene::Layers.tracked(Instance)) diff --git a/application/editormodejoints.cpp b/application/editormodejoints.cpp index a9afd279..6b135db1 100644 --- a/application/editormodejoints.cpp +++ b/application/editormodejoints.cpp @@ -137,11 +137,9 @@ ImU32 kind_colour(kind const Kind) } } -std::string name_of(TTrack const *Track) +std::string label_of(TTrack const *Track) { - if (Track == nullptr) - return "-"; - return Track->name().empty() ? STR("(no name)") : Track->name(); + return Track == nullptr ? std::string{"-"} : ::name_of(*Track); } } // namespace @@ -538,7 +536,7 @@ void joints_tool::render_options() { ImGui::PushStyleColor(ImGuiCol_Button, ImGui::ColorConvertU32ToFloat4(family_colour(2, 0.55f))); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertU32ToFloat4(family_colour(2, 0.75f))); - if (ImGui::Button(STR_C("Check"), ImVec2(-1.0f, std::round(ImGui::GetFrameHeight() * 1.3f)))) + if (ImGui::Button(STR_C("Check"), ImVec2(-1.0f, tall_button_height()))) joints_start(); ImGui::PopStyleColor(2); item_tooltip("Finds the gaps between the ends of the tracks, the steps, the kinks and the jumps of the cant and of the grade"); @@ -632,9 +630,9 @@ void joints_tool::render_options() ImGui::NextColumn(); ImGui::TextUnformatted(issue.fixed ? STR_C("fixed") : value_text(issue).c_str()); ImGui::NextColumn(); - ImGui::TextUnformatted(name_of(issue.track).c_str()); + ImGui::TextUnformatted(label_of(issue.track).c_str()); ImGui::NextColumn(); - ImGui::TextUnformatted(name_of(issue.other).c_str()); + ImGui::TextUnformatted(label_of(issue.other).c_str()); ImGui::NextColumn(); } ImGui::Columns(1); @@ -642,7 +640,7 @@ void joints_tool::render_options() if (chosen) { auto &issue{state.issues[state.selected]}; - ImGui::Text("%s %s: %s / %s", STR_C(kind_name(issue.type)), value_text(issue).c_str(), name_of(issue.track).c_str(), name_of(issue.other).c_str()); + ImGui::Text("%s %s: %s / %s", STR_C(kind_name(issue.type)), value_text(issue).c_str(), label_of(issue.track).c_str(), label_of(issue.other).c_str()); if (false == issue.fixed && issue.type != kind::open && issue.type != kind::model && ImGui::Button(STR_C("Fix this one"))) joints_fix({state.selected}); } diff --git a/application/editormodelay.cpp b/application/editormodelay.cpp index f6cbb46b..45cf586d 100644 --- a/application/editormodelay.cpp +++ b/application/editormodelay.cpp @@ -1159,7 +1159,7 @@ glm::dvec3 editor_mode::lay_angle_snap(glm::dvec3 const &Mouse, std::string &Lab ratio = n; } } - auto const name{axis->name().empty() ? std::string{STR_C("(no name)")} : axis->name()}; + auto const name{name_of(*axis)}; auto const degrees{glm::degrees(std::abs(std::remainder(angle, glm::pi())))}; Label = ratio > 0.0 ? format(STR_C("1:%g to %s"), ratio, name.c_str()) : degrees < 0.1 ? format(STR_C("parallel to %s"), name.c_str()) : format(STR_C("%.0f deg to %s"), degrees, name.c_str()); } diff --git a/application/editormodelineside.cpp b/application/editormodelineside.cpp index 56e93528..61073625 100644 --- a/application/editormodelineside.cpp +++ b/application/editormodelineside.cpp @@ -35,6 +35,7 @@ namespace using geometry::bezier; using geometry::plan_distance; +using geometry::heading_of; using geometry::plan_of; double const kSampleStep{2.0}; // m between the samples of the line @@ -47,19 +48,6 @@ int const kRailCategory{1}; char const *const kHektoStyles[] = {"nowy", "stary", "zgnity"}; char const *const kFoulingModels[] = {"ip/pkp/w17.inc", "ip/pkp/w17_new.inc", "ip/pkp/w17_old.inc", "ip/pkp/w17_dwuteo.inc"}; -std::string normalized(std::string Text) -{ - std::transform(Text.begin(), Text.end(), Text.begin(), [](unsigned char const Character) { return static_cast(Character == '\\' ? '/' : std::tolower(Character)); }); - return Text; -} - -bool numeric(std::string const &Text, double &Value) -{ - char *end{nullptr}; - Value = std::strtod(Text.c_str(), &end); - return false == Text.empty() && *end == '\0'; -} - std::string number(double const Value, int const Decimals) { auto text{format("%.*f", Decimals, Value)}; @@ -68,12 +56,6 @@ std::string number(double const Value, int const Decimals) return text; } -double heading_of(glm::dvec2 const &Direction) -{ - auto const degrees{glm::degrees(std::atan2(Direction.x, Direction.y))}; - return degrees < 0.0 ? degrees + 360.0 : degrees; -} - // index of the sample at or before the chainage std::size_t sample_index(std::vector const &Samples, double const Chainage) { @@ -238,7 +220,7 @@ std::vector editor_mode::standing_templates(std: auto const &included{scene::Layers.instance(handle)}; if (included.removed || included.dead || included.file == nullptr) continue; - auto const file{normalized(*included.file)}; + auto const file{editor_includes::normalized(*included.file)}; if (false == Accept(file) || included.context.rotation != glm::vec3{0.f} || included.context.scale != glm::vec3{1.f}) continue; std::string target; @@ -649,11 +631,8 @@ void hekto_tool::render_options() tool.posts.empty() ? "-" : km_text(std::max(tool.posts.front().hectometres, tool.posts.back().hectometres)).c_str(), tool.found.size(), static_cast(wrong)); if (missing > 0) { - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.55f, 0.25f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.25f, 0.65f, 0.3f, 1.0f)); - if (ImGui::Button(format(STR_P("Place %d post", "Place %d posts", static_cast(missing)), static_cast(missing)).c_str(), ImVec2(-1.0f, std::round(ImGui::GetFrameHeight() * 1.3f)))) + if (main_button(format(STR_P("Place %d post", "Place %d posts", static_cast(missing)), static_cast(missing)).c_str(), ImVec2(-1.0f, tall_button_height()))) hekto_place(); - ImGui::PopStyleColor(2); } if (duplicates > 0) { @@ -938,14 +917,11 @@ void fouling_tool::render_options() ImGui::TextWrapped("%s", tool.status.c_str()); auto const missing{std::count_if(tool.points.begin(), tool.points.end(), [](fouling_point const &Point) { return Point.standing < 0; })}; auto const wrong{std::count_if(tool.found.begin(), tool.found.end(), [](fouling_found const &Found) { return Found.wrong; })}; - auto const tall{std::round(ImGui::GetFrameHeight() * 1.3f)}; + auto const tall{tall_button_height()}; if (missing > 0) { - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.55f, 0.25f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.25f, 0.65f, 0.3f, 1.0f)); - if (ImGui::Button(format(STR_P("Place %d marker", "Place %d markers", static_cast(missing)), static_cast(missing)).c_str(), ImVec2(-1.0f, tall))) + if (main_button(format(STR_P("Place %d marker", "Place %d markers", static_cast(missing)), static_cast(missing)).c_str(), ImVec2(-1.0f, tall))) place(); - ImGui::PopStyleColor(2); } if (wrong > 0) { @@ -1069,14 +1045,7 @@ editor_selftest_result hekto_tool::selftest(std::string const &Command, std::ist { if (Command == "hekto") { - std::map values; - std::string pair; - while (Words >> pair) - { - auto const equals{pair.find('=')}; - if (equals != std::string::npos) - values[pair.substr(0, equals)] = pair.substr(equals + 1); - } + auto values{selftest_values(Words)}; auto *track{simulation::Paths.find(values["track"])}; if (track == nullptr) { @@ -1135,14 +1104,7 @@ editor_selftest_result fouling_tool::selftest(std::string const &Command, std::i { if (Command == "fouling") { - std::map values; - std::string pair; - while (Words >> pair) - { - auto const equals{pair.find('=')}; - if (equals != std::string::npos) - values[pair.substr(0, equals)] = pair.substr(equals + 1); - } + auto values{selftest_values(Words)}; auto &tool{m_fouling}; if (values["show"] == "1") { diff --git a/application/editormodeparallel.cpp b/application/editormodeparallel.cpp index f5ac7c5d..9253d51f 100644 --- a/application/editormodeparallel.cpp +++ b/application/editormodeparallel.cpp @@ -122,11 +122,6 @@ bool side_picker(int &Side, double const Spacing) return changed; } -std::string name_of(TTrack const &Track) -{ - return Track.name().empty() ? STR("(no name)") : Track.name(); -} - segment_data reversed(segment_data const &Path) { segment_data result{Path}; @@ -416,11 +411,8 @@ void parallel_tool::render_options() } else if (false == tool.pieces.empty()) { - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.55f, 0.25f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.25f, 0.65f, 0.3f, 1.0f)); - if (ImGui::Button(format(STR_P("Lay %zu track, %.1f m", "Lay %zu tracks, %.1f m", static_cast(tool.pieces.size())), tool.pieces.size(), tool.length).c_str(), ImVec2(-1.0f, std::round(ImGui::GetFrameHeight() * 1.3f)))) + if (main_button(format(STR_P("Lay %zu track, %.1f m", "Lay %zu tracks, %.1f m", static_cast(tool.pieces.size())), tool.pieces.size(), tool.length).c_str(), ImVec2(-1.0f, tall_button_height()))) parallel_build(); - ImGui::PopStyleColor(2); item_tooltip("A new track beside the one selected, as in the preview; the selected stays as it is. Ctrl+Z takes it back"); } if (false == tool.status.empty()) diff --git a/application/editormodeprofile.cpp b/application/editormodeprofile.cpp index cc076c1e..75df8b70 100644 --- a/application/editormodeprofile.cpp +++ b/application/editormodeprofile.cpp @@ -1493,7 +1493,7 @@ void editor_mode::profile_apply(bool const Terrain) state.status = format(STR_P("Grade line applied: %zu path changed, %zu added, largest departure %.3f m", "Grade line applied: %zu paths changed, %zu added, largest departure %.3f m", changed), changed, added, state.departure); if (false == scene::Layers.empty()) state.status += STR_C("\nThe paths and the design (//$p lines) go to the scenery files on save"); - auto const name = [](TTrack const *Track) { return Track->name().empty() ? std::string{STR_C("(no name)")} : Track->name(); }; + auto const name = [](TTrack const *Track) { return name_of(*Track); }; auto const closed{std::count_if(gaps.begin(), gaps.end(), [](auto const &Gap) { return Gap.closed; })}; if (closed > 0) state.status += STR_C("\nPaths off the route brought to its height:"); diff --git a/application/editormoderoute.cpp b/application/editormoderoute.cpp index fc46f264..c1c651fd 100644 --- a/application/editormoderoute.cpp +++ b/application/editormoderoute.cpp @@ -381,7 +381,7 @@ int editor_mode::digitize_parts(editor_track::chain const &Chain, std::string *R if (count == 1) { ++Skipped; - reason = (Chain.tracks.front()->name().empty() ? std::string{STR_C("(no name)")} : Chain.tracks.front()->name()) + ": " + reason; + reason = name_of(*Chain.tracks.front()) + ": " + reason; WriteLog("Editor: digitizing - " + reason, logtype::generic); if (Reason != nullptr && Reason->empty()) *Reason = reason; @@ -805,7 +805,7 @@ bool editor_mode::route_relay_through_switches(editor_track::chain const &Chain, { auto &route{m_route}; auto const &result{route.result}; - auto const name = [](TTrack const *Track) { return Track->name().empty() ? std::string{STR_C("(no name)")} : Track->name(); }; + auto const name = [](TTrack const *Track) { return name_of(*Track); }; auto const plan = [](glm::dvec3 const &Vector) { return glm::normalize(plan_of(Vector)); }; struct placement { @@ -1657,14 +1657,11 @@ void editor_mode::render_route_result() if (result.valid) { - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.55f, 0.25f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.25f, 0.65f, 0.3f, 1.0f)); - if (ImGui::Button(STR_C("Apply to the scenery"), ImVec2(-1.0f, 28.0f))) + if (main_button(STR_C("Apply to the scenery"), ImVec2(-1.0f, 28.0f))) { route_apply(); first_step_done(first_step::plan_apply); } - ImGui::PopStyleColor(2); } else { diff --git a/application/editormodeselection.cpp b/application/editormodeselection.cpp index 732bd721..42d3d5d5 100644 --- a/application/editormodeselection.cpp +++ b/application/editormodeselection.cpp @@ -11,6 +11,7 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "application/editormode.h" +#include "application/editormodeshared.h" #include "application/editoruilayer.h" #include "application/editorprojection.h" #include "editor/editorFormat.hpp" @@ -271,7 +272,7 @@ void editor_mode::group_selection() { if (reason.empty()) reason = STR("it's a part of a scenery template"); - ui()->set_status(format(STR_C("\"%s\" can't be grouped: %s"), node->name().empty() ? STR_C("(no name)") : node->name().c_str(), reason.c_str()), true); + ui()->set_status(format(STR_C("\"%s\" can't be grouped: %s"), name_of(*node).c_str(), reason.c_str()), true); return; } } @@ -334,7 +335,7 @@ void editor_mode::render_context_menu() if (count > 1) ImGui::TextDisabled(STR_C("%zu models selected"), count); else if (selected) - ImGui::TextDisabled("%s", m_node->name().empty() ? STR_C("(no name)") : m_node->name().c_str()); + ImGui::TextDisabled("%s", name_of(*m_node).c_str()); else ImGui::TextDisabled("%s", STR_C("Nothing selected")); ImGui::Separator(); diff --git a/application/editormodeselftestsignal.cpp b/application/editormodeselftestsignal.cpp index dac9c63d..fe64c7ef 100644 --- a/application/editormodeselftestsignal.cpp +++ b/application/editormodeselftestsignal.cpp @@ -52,14 +52,7 @@ editor_mode::selftest_result editor_mode::selftest_signal(std::string const &com { if (command == "signal" || command == "signalmove" || command == "signaldelete" || command == "signalrename" || command == "signalprobe") { - std::map values; - std::string pair; - while (words >> pair) - { - auto const equals{pair.find('=')}; - if (equals != std::string::npos) - values[pair.substr(0, equals)] = pair.substr(equals + 1); - } + auto values{selftest_values(words)}; show_track_tab(track_tab::signals); signal_scan(); signal_refresh(true); diff --git a/application/editormodeselftestsweep.cpp b/application/editormodeselftestsweep.cpp index 38289631..2524246e 100644 --- a/application/editormodeselftestsweep.cpp +++ b/application/editormodeselftestsweep.cpp @@ -52,14 +52,7 @@ editor_mode::selftest_result editor_mode::selftest_sweep(std::string const &comm { if (command == "sweep") { - std::map values; - std::string pair; - while (words >> pair) - { - auto const equals{pair.find('=')}; - if (equals != std::string::npos) - values[pair.substr(0, equals)] = pair.substr(equals + 1); - } + auto values{selftest_values(words)}; auto &tool{m_sweep}; auto const get = [&](char const *Key, std::string const &Default) { auto const found{values.find(Key)}; return found != values.end() ? found->second : Default; }; tool.edited = nullptr; @@ -158,14 +151,7 @@ editor_mode::selftest_result editor_mode::selftest_sweep(std::string const &comm } if (command == "bend" || command == "bendset") { - std::map values; - std::string pair; - while (words >> pair) - { - auto const equals{pair.find('=')}; - if (equals != std::string::npos) - values[pair.substr(0, equals)] = pair.substr(equals + 1); - } + auto values{selftest_values(words)}; auto const vector = [](std::string Text) { std::replace(Text.begin(), Text.end(), ',', ' '); std::istringstream numbers(Text); diff --git a/application/editormodeshared.h b/application/editormodeshared.h index 149a4a09..0041e34a 100644 --- a/application/editormodeshared.h +++ b/application/editormodeshared.h @@ -24,6 +24,25 @@ http://mozilla.org/MPL/2.0/. float constexpr kDragPixels{5.0f}; +template inline std::string name_of(Node_ const &Node) +{ + return Node.name().empty() ? std::string{STR_C("(no name)")} : Node.name(); +} + +inline float tall_button_height() +{ + return std::round(ImGui::GetFrameHeight() * 1.3f); +} + +inline bool main_button(char const *Label, ImVec2 const &Size) +{ + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.55f, 0.25f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.25f, 0.65f, 0.3f, 1.0f)); + auto const pressed{ImGui::Button(Label, Size)}; + ImGui::PopStyleColor(2); + return pressed; +} + inline void item_tooltip(char const *Text) { if (ImGui::IsItemHovered()) diff --git a/application/editormodesignal.cpp b/application/editormodesignal.cpp index 0156c914..e9d04354 100644 --- a/application/editormodesignal.cpp +++ b/application/editormodesignal.cpp @@ -36,6 +36,7 @@ namespace { using geometry::bezier; +using geometry::heading_of; using geometry::plan_of; double const kRailTop{0.18}; @@ -58,12 +59,6 @@ int kind_index(std::string const &Kind) return -1; } -std::string lower(std::string Text) -{ - std::transform(Text.begin(), Text.end(), Text.begin(), [](unsigned char const Character) { return static_cast(std::tolower(Character)); }); - return Text; -} - double nearest_on(TTrack const &Track, int const Path, glm::dvec3 const &Point) { bezier const curve{Track.m_paths[Path]}; @@ -93,12 +88,6 @@ glm::dvec2 right_of(glm::dvec2 const &Direction) return {-Direction.y, Direction.x}; } -double heading_of(glm::dvec2 const &Direction) -{ - auto const degrees{glm::degrees(std::atan2(Direction.x, Direction.y))}; - return degrees < 0.0 ? degrees + 360.0 : degrees; -} - int lean_wanted(int const Side) { return Side > 0 ? 1 : 2; @@ -159,7 +148,7 @@ std::vector const &plate_textures() std::string stem; try { - stem = lower(file->path().stem().string()); + stem = ToLower(file->path().stem().string()); } catch (std::exception const &) { @@ -177,7 +166,7 @@ std::string plate_for(std::string const &Plate, std::string const &Name) if (false == Plate.empty()) return Plate; auto const &plates{plate_textures()}; - auto const name{lower(Name)}; + auto const name{ToLower(Name)}; return std::binary_search(plates.begin(), plates.end(), name) ? name : std::string{"0_nothing"}; } @@ -304,7 +293,7 @@ editor_mode::signal_template const *editor_mode::signal_armed() const editor_mode::signal_template const *editor_mode::signal_template_of(std::string const &File) const { - auto const file{lower(File)}; + auto const file{ToLower(File)}; for (auto const &item : m_signal.templates) if (item.file == file) return &item; @@ -350,7 +339,7 @@ std::string editor_mode::signal_intent_label(signal_template const &Template, si if (Template.read.empty()) return format(STR_C("Click: %s %s (%s)%s, the train doesn't read it"), STR(kKindLabels[kind_index(Template.kind)]).c_str(), name.c_str(), variant.c_str(), where.c_str()) + "\n" + STR("Drag along: the trains it faces run the way of the drag"); auto const event{editor_includes::substitute(Template.read, {name})}; - auto const path{Spot.track->name().empty() ? std::string{STR("(no name)")} : Spot.track->name()}; + auto const path{name_of(*Spot.track)}; return format(STR_C("Click: %s %s (%s)%s, read by %s %s of %s"), STR(kKindLabels[kind_index(Template.kind)]).c_str(), name.c_str(), variant.c_str(), where.c_str(), editor_track::event_keyword(Spot.list), event.c_str(), path.c_str()) + "\n" + STR("Drag along: the trains it faces run the way of the drag"); } @@ -376,7 +365,7 @@ void editor_mode::signal_refresh(bool const Force) for (int list = 0; list < 6; ++list) for (auto const &event : editor_track::events(*track, list)) if (false == event.first.empty()) - readers.emplace(lower(event.first), std::make_pair(track, list)); + readers.emplace(ToLower(event.first), std::make_pair(track, list)); } for (auto const &include : found) { @@ -388,7 +377,7 @@ void editor_mode::signal_refresh(bool const Force) if (false == item->read.empty()) { standing.read = editor_includes::substitute(item->read, include.values); - if (auto const reader{readers.find(lower(standing.read))}; reader != readers.end()) + if (auto const reader{readers.find(ToLower(standing.read))}; reader != readers.end()) { standing.track = reader->second.first; standing.list = reader->second.second; @@ -564,7 +553,7 @@ void editor_mode::signal_release() signal_detach_events(found->read, states); if (std::none_of(states.begin(), states.end(), [&](auto const &State) { return State.first == spot.track; })) states.emplace_back(spot.track, editor_track::capture(*spot.track)); - editor_track::events(*spot.track, spot.list).emplace_back(lower(found->read), nullptr); + editor_track::events(*spot.track, spot.list).emplace_back(ToLower(found->read), nullptr); std::string missing; editor_track::bind_events(*spot.track, missing); spot.track->mark_dirty(); @@ -585,7 +574,7 @@ void editor_mode::signal_release() m_history.back().directives.emplace_back(found->include.instance, std::move(before)); } tool.status = found->read.empty() ? format(STR_C("Signal %s moved, Ctrl+Z takes it back"), found->name.c_str()) - : format(STR_C("Signal %s moved, read by %s %s of %s, Ctrl+Z takes it back"), found->name.c_str(), editor_track::event_keyword(spot.list), found->read.c_str(), spot.track->name().empty() ? STR_C("(no name)") : spot.track->name().c_str()); + : format(STR_C("Signal %s moved, read by %s %s of %s, Ctrl+Z takes it back"), found->name.c_str(), editor_track::event_keyword(spot.list), found->read.c_str(), name_of(*spot.track).c_str()); auto const instance{found->include.instance}; signal_refresh(true); signal_select(instance); @@ -639,7 +628,7 @@ bool editor_mode::signal_place(signal_spot const &Spot, signal_template const &T } signal_refresh(true); auto const taken = [&](std::string const &Name) { - return std::any_of(tool.standing.begin(), tool.standing.end(), [&](signal_standing const &Standing) { return lower(Standing.name) == lower(Name); }); + return std::any_of(tool.standing.begin(), tool.standing.end(), [&](signal_standing const &Standing) { return ToLower(Standing.name) == ToLower(Name); }); }; for (int guard = 0; taken(name) && guard < 1000; ++guard) name = signal_next_name(name); @@ -663,7 +652,7 @@ bool editor_mode::signal_place(signal_spot const &Spot, signal_template const &T auto const instance{m_history.back().instances.front()}; auto const *placed{signal_template_of(file)}; auto const read{placed != nullptr && false == placed->read.empty() ? editor_includes::substitute(placed->read, {name}) : std::string{}}; - auto const path{Spot.track->name().empty() ? std::string{STR("(no name)")} : Spot.track->name()}; + auto const path{name_of(*Spot.track)}; std::string reason; if (read.empty()) tool.status = format(STR_C("Signal %s placed (%s), the train doesn't read it, Ctrl+Z takes it back"), name.c_str(), file.c_str()); @@ -676,7 +665,7 @@ bool editor_mode::signal_place(signal_spot const &Spot, signal_template const &T auto snap{std::move(m_history.back())}; m_history.pop_back(); auto const before{editor_track::capture(*Spot.track)}; - editor_track::events(*Spot.track, Spot.list).emplace_back(lower(read), nullptr); + editor_track::events(*Spot.track, Spot.list).emplace_back(ToLower(read), nullptr); std::string missing; editor_track::bind_events(*Spot.track, missing); Spot.track->mark_dirty(); @@ -693,7 +682,7 @@ bool editor_mode::signal_place(signal_spot const &Spot, signal_template const &T void editor_mode::signal_detach_events(std::string const &Read, std::vector> &States) { - auto const name{lower(Read)}; + auto const name{ToLower(Read)}; for (auto *track : simulation::Paths.sequence()) { if (track == nullptr || track->m_editorremoved) @@ -701,7 +690,7 @@ void editor_mode::signal_detach_events(std::string const &Read, std::vectorm_editorremoved) @@ -755,15 +744,15 @@ void editor_mode::signal_renamed(std::string const &Before, std::string const &A bool carries{false}; for (int list = 0; list < 6 && false == carries; ++list) for (auto const &event : editor_track::events(*track, list)) - carries = carries || lower(event.first) == name; + carries = carries || ToLower(event.first) == name; if (false == carries) continue; if (std::none_of(Snapshot.tracks.begin(), Snapshot.tracks.end(), [&](auto const &State) { return State.first == track; })) Snapshot.tracks.emplace_back(track, editor_track::capture(*track)); for (int list = 0; list < 6; ++list) for (auto &event : editor_track::events(*track, list)) - if (lower(event.first) == name) - event = {lower(After), nullptr}; + if (ToLower(event.first) == name) + event = {ToLower(After), nullptr}; std::string missing; editor_track::bind_events(*track, missing); track->mark_dirty(); @@ -790,7 +779,7 @@ void editor_mode::render_signal_palette() auto &tool{m_signal}; ImGui::SetNextItemWidth(-1.0f); ImGui::InputTextWithHint("##signalfilter", STR_C("filter: name, w24, sbl..."), tool.filter, sizeof(tool.filter)); - auto const filter{lower(tool.filter)}; + auto const filter{ToLower(tool.filter)}; auto const *armed{signal_armed()}; std::vector> groups; std::vector keys; @@ -799,7 +788,7 @@ void editor_mode::render_signal_palette() auto const &item{tool.templates[i]}; if (item.kind != kKinds[tool.kind]) continue; - if (false == filter.empty() && item.name.find(filter) == std::string::npos && lower(item.description).find(filter) == std::string::npos) + if (false == filter.empty() && item.name.find(filter) == std::string::npos && ToLower(item.description).find(filter) == std::string::npos) continue; auto const key{group_of(item.mount, item.lamps)}; auto const found{std::find(keys.begin(), keys.end(), key)}; @@ -897,7 +886,7 @@ void editor_mode::render_signal_selected() if (found->read.empty()) ImGui::TextDisabled("%s", STR_C("The train doesn't read it")); else if (found->track != nullptr) - ImGui::TextDisabled(STR_C("Read by %s %s of %s"), editor_track::event_keyword(found->list), found->read.c_str(), found->track->name().empty() ? STR_C("(no name)") : found->track->name().c_str()); + ImGui::TextDisabled(STR_C("Read by %s %s of %s"), editor_track::event_keyword(found->list), found->read.c_str(), name_of(*found->track).c_str()); else ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.3f, 1.0f), STR_C("No path has %s among its events: the train doesn't see it"), found->read.c_str()); } @@ -954,7 +943,7 @@ void editor_mode::render_signal_ui() if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", STR_C("Texture of the plate from textures/tabl, e.g. a-12 or tob. Left empty: the one named like the signal, if there is one")); auto const &plates{plate_textures()}; - auto const typed{lower(tool.plate)}; + auto const typed{ToLower(tool.plate)}; auto const exists{typed.empty() || std::binary_search(plates.begin(), plates.end(), typed)}; if (false == exists) ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.3f, 1.0f), "%s", STR_C("no such texture")); @@ -987,7 +976,7 @@ void editor_mode::render_signal_ui() if (missing) { auto const &plates{plate_textures()}; - auto const typed{lower(tool.plate)}; + auto const typed{ToLower(tool.plate)}; int shown{0}; for (auto const &plate : plates) { diff --git a/application/editormodespeed.cpp b/application/editormodespeed.cpp index c033d9cd..8927886b 100644 --- a/application/editormodespeed.cpp +++ b/application/editormodespeed.cpp @@ -223,7 +223,7 @@ void speed_tool::render_options() { ImGui::PushStyleColor(ImGuiCol_Button, ImGui::ColorConvertU32ToFloat4(family_colour(2, 0.55f))); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertU32ToFloat4(family_colour(2, 0.75f))); - if (ImGui::Button(STR_C("Check"), ImVec2(-1.0f, std::round(ImGui::GetFrameHeight() * 1.3f)))) + if (ImGui::Button(STR_C("Check"), ImVec2(-1.0f, tall_button_height()))) speed_start(); ImGui::PopStyleColor(2); item_tooltip("Looks at the radius and the cant, the cant ramps, the curves without the transitions and the switch branches"); @@ -312,7 +312,7 @@ void speed_tool::render_options() bool const violated{verdict.violated(state.options)}; bool const bad{violated || verdict.warned(state.options)}; auto const colour{violated ? ImVec4(1.0f, 0.45f, 0.4f, 1.0f) : ImVec4(1.0f, 0.75f, 0.3f, 1.0f)}; - auto name{verdict.track->name().empty() ? STR("(no name)") : verdict.track->name()}; + auto name{name_of(*verdict.track)}; if (verdict.track->eType == tt_Switch) name += verdict.path == 0 ? STR_C(" (main)") : STR_C(" (branch)"); if (bad) diff --git a/application/editormodesweep.cpp b/application/editormodesweep.cpp index 5fe783a6..e430e619 100644 --- a/application/editormodesweep.cpp +++ b/application/editormodesweep.cpp @@ -22,6 +22,7 @@ http://mozilla.org/MPL/2.0/. #include "simulation/simulation.h" #include "utilities/Globals.h" #include "utilities/Logs.h" +#include "utilities/utilities.h" #include "world/Sweep.h" #include "model/AnimModel.h" #include "world/Track.h" @@ -126,12 +127,6 @@ bool same_piece(segment_data const &A, segment_data const &B) return std::abs(A.rolls[0] - B.rolls[0]) < 1e-3f && std::abs(A.rolls[1] - B.rolls[1]) < 1e-3f; } -std::string lower(std::string Text) -{ - std::transform(Text.begin(), Text.end(), Text.begin(), [](unsigned char const Character) { return static_cast(std::tolower(Character)); }); - return Text; -} - } // namespace void editor_mode::sweep_scan_models() @@ -153,7 +148,7 @@ void editor_mode::sweep_scan_models() std::string name; try { - extension = lower(entry->path().extension().string()); + extension = ToLower(entry->path().extension().string()); if (extension != ".e3d" && extension != ".t3d") continue; name = std::filesystem::relative(entry->path(), "models", status).generic_string(); @@ -754,14 +749,14 @@ void editor_mode::render_sweep_ui() changed = true; ImGui::SetNextItemWidth(-1.0f); ImGui::InputTextWithHint("##sweepsearch", STR_C("search the models"), tool.search, sizeof(tool.search)); - std::string const text{lower(tool.search)}; + std::string const text{ToLower(tool.search)}; if (false == text.empty()) { ImGui::BeginChild("##sweepmodels", ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 7.0f + 6.0f), true); std::size_t listed{0}; for (auto const &model : tool.models) { - if (lower(model).find(text) == std::string::npos) + if (ToLower(model).find(text) == std::string::npos) continue; if (++listed > kSweepModelList) break; @@ -775,7 +770,7 @@ void editor_mode::render_sweep_ui() } // a template takes its parameters, the ones which place it are zeros: it's laid out in its own space std::string const model{tool.model}; - bool const istemplate{model.size() > 4 && lower(model.substr(model.size() - 4)) == ".inc"}; + bool const istemplate{model.size() > 4 && ToLower(model.substr(model.size() - 4)) == ".inc"}; if (istemplate && model != tool.parameters_for) { tool.parameters_for = model; @@ -803,7 +798,7 @@ void editor_mode::render_sweep_ui() auto const id{std::atoi(word.substr(open + 2, close - open - 2).c_str())}; if (id < 1 || id > count || false == tool.parameter_choices[id - 1].empty()) continue; - auto const prefix{lower(word.substr(0, open))}; + auto const prefix{ToLower(word.substr(0, open))}; auto suffix{word.substr(close + 1)}; suffix = suffix.substr(0, suffix.find(':')); auto const slash{prefix.rfind('/')}; @@ -816,13 +811,13 @@ void editor_mode::render_sweep_ui() std::string stem; try { - stem = lower(entry->path().stem().string()); + stem = ToLower(entry->path().stem().string()); } catch (std::exception const &) { continue; } - if (stem.size() > start.size() + suffix.size() && stem.compare(0, start.size(), start) == 0 && (suffix.empty() || stem.compare(stem.size() - suffix.size(), suffix.size(), lower(suffix)) == 0)) + if (stem.size() > start.size() + suffix.size() && stem.compare(0, start.size(), start) == 0 && (suffix.empty() || stem.compare(stem.size() - suffix.size(), suffix.size(), ToLower(suffix)) == 0)) variants.insert(stem.substr(start.size(), stem.size() - start.size() - suffix.size())); } tool.parameter_choices[id - 1].assign(variants.begin(), variants.end()); @@ -1836,7 +1831,7 @@ void editor_mode::render_bend() else { tool.track = std::clamp(tool.track, 0, static_cast(tool.tracks.size()) - 1); - auto const label = [](std::pair const &Entry) { return format("%s (%.1f m)", Entry.first->name().empty() ? STR_C("(no name)") : Entry.first->name().c_str(), Entry.second); }; + auto const label = [](std::pair const &Entry) { return format("%s (%.1f m)", name_of(*Entry.first).c_str(), Entry.second); }; if (begin_fields("##bendsource", 0.35f)) { field_row("Track"); diff --git a/application/editormodeswitchslide.cpp b/application/editormodeswitchslide.cpp index 63040584..d58b9b5b 100644 --- a/application/editormodeswitchslide.cpp +++ b/application/editormodeswitchslide.cpp @@ -43,11 +43,6 @@ struct switch_end_move double shift{0.0}; }; -std::string track_label(TTrack const &Track) -{ - return Track.name().empty() ? std::string{STR_C("(no name)")} : Track.name(); -} - std::optional> switch_end_moves(TTrack const &Track, segment_data const &Main, bool const Online, std::pair const &Room, double const Offset, std::string &Status) { auto const axis{editor_track::main_axis(Track)}; @@ -83,7 +78,7 @@ std::string switch_rejoin(TTrack &Track, std::vector const auto const shift{glm::distance(editor_track::point_position(Track, joint.own), editor_track::point_position(*joint.other, joint.theirs))}; if (shift > 0.3 * bezier{theirs}.plan_length()) { - loose += (loose.empty() ? "" : ", ") + track_label(*joint.other); + loose += (loose.empty() ? "" : ", ") + name_of(*joint.other); continue; } following.push_back(joint); @@ -579,7 +574,7 @@ std::string editor_mode::plan_switch_branches(TTrack const &Track, std::vectorpart.tracks, reason)) { - notes += (notes.empty() ? "" : "\n") + format(STR_C("The curve past the end of the switch can't keep to it, %s follows the switch on its own"), track_label(*joint.other).c_str()); + notes += (notes.empty() ? "" : "\n") + format(STR_C("The curve past the end of the switch can't keep to it, %s follows the switch on its own"), name_of(*joint.other).c_str()); branch = Branches.erase(branch); continue; } diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index e9ada20f..45ba31c8 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -302,7 +302,7 @@ bool editor_mode::tracks_editable(std::vector const &Tracks, std::stri std::string reason; if (track != nullptr && false == editor_track::can_edit_geometry(*track, reason)) { - Reason = (track->name().empty() ? std::string{STR_C("(no name)")} : track->name()) + ": " + reason; + Reason = name_of(*track) + ": " + reason; return false; } } @@ -698,7 +698,7 @@ void editor_mode::render_track_selection() auto *track{tracks.front()}; if (false == sheet) { - ImGui::TextUnformatted(track->name().empty() ? STR_C("(no name)") : track->name().c_str()); + ImGui::TextUnformatted(name_of(*track).c_str()); ImGui::TextDisabled("%s, %.2f m", STR_C(type_name(*track)), track->Length()); return; } @@ -1743,7 +1743,7 @@ std::vector editor_mode::track_key_hints(bool const All) // values of the change in progress, shown next to the cursor std::string editor_mode::track_readout() const { - auto const name = [](TTrack const *Track) { return Track->name().empty() ? std::string{STR_C("(no name)")} : Track->name(); }; + auto const name = [](TTrack const *Track) { return name_of(*Track); }; auto const azimuth = [](glm::dvec3 const &From, glm::dvec3 const &To) { auto const degrees{glm::degrees(std::atan2(To.x - From.x, To.z - From.z))}; return degrees < 0.0 ? degrees + 360.0 : degrees; diff --git a/application/editormodetrackprops.cpp b/application/editormodetrackprops.cpp index 4d507023..8804e819 100644 --- a/application/editormodetrackprops.cpp +++ b/application/editormodetrackprops.cpp @@ -57,11 +57,6 @@ bool rail(TTrack const &Track) return (Track.iCategoryFlag & 15) == kRailCategory && false == Track.m_editorremoved && Track.visible(); } -std::string name_of(TTrack const &Track) -{ - return Track.name().empty() ? std::string{STR_C("(no name)")} : Track.name(); -} - char const *type_of(TTrack const &Track) { switch (Track.eType) diff --git a/application/editormodetrackux.cpp b/application/editormodetrackux.cpp index 238b0cfe..3cb577f6 100644 --- a/application/editormodetrackux.cpp +++ b/application/editormodetrackux.cpp @@ -18,6 +18,7 @@ http://mozilla.org/MPL/2.0/. #include "utilities/Globals.h" #include "rendering/renderer.h" #include "world/Track.h" +#include "utilities/utilities.h" #include "scene/scene.h" #include "scene/scenelayers.h" #include "simulation/simulation.h" @@ -61,17 +62,6 @@ bool rail(TTrack const &Track) return (Track.iCategoryFlag & 15) == kRailCategory && false == Track.m_editorremoved && Track.visible(); } -std::string name_of(TTrack const &Track) -{ - return Track.name().empty() ? std::string{STR_C("(no name)")} : Track.name(); -} - -std::string lower(std::string Text) -{ - std::transform(Text.begin(), Text.end(), Text.begin(), [](unsigned char const Character) { return static_cast(std::tolower(Character)); }); - return Text; -} - char const *type_of(TTrack const &Track) { switch (Track.eType) @@ -843,7 +833,7 @@ void editor_mode::render_track_search() } ImGui::SetNextItemWidth(-1.0f); ImGui::InputTextWithHint("##findtrack", STR_C("Find a track by its name (Ctrl+F)"), search.text, sizeof(search.text)); - std::string const text{lower(search.text)}; + std::string const text{ToLower(search.text)}; if (text.empty()) { search.searched.clear(); @@ -857,7 +847,7 @@ void editor_mode::render_track_search() search.total = 0; for (auto *track : simulation::Paths.sequence()) { - if (track == nullptr || false == rail(*track) || lower(track->name()).find(text) == std::string::npos) + if (track == nullptr || false == rail(*track) || ToLower(track->name()).find(text) == std::string::npos) continue; ++search.total; if (search.found.size() < kSearchLimit) diff --git a/application/editormodeturntable.cpp b/application/editormodeturntable.cpp index 1cc35b63..47b719c5 100644 --- a/application/editormodeturntable.cpp +++ b/application/editormodeturntable.cpp @@ -77,19 +77,6 @@ double line_angle(double const Angle) return result; } -bool numeric(std::string const &Text, double &Value) -{ - char *end{nullptr}; - Value = std::strtod(Text.c_str(), &end); - return false == Text.empty() && *end == '\0'; -} - -std::string normalized(std::string File) -{ - std::replace(File.begin(), File.end(), '\\', '/'); - return ToLower(File); -} - std::vector fixed_angles(std::string const &File) { std::vector result; @@ -280,8 +267,8 @@ bool editor_mode::turntable_read(TTrack *Table) auto const &included{scene::Layers.instance(handle)}; if (included.removed || included.dead || included.file == nullptr) continue; - auto const file{normalized(*included.file)}; - auto const kind{std::find_if(tool.templates.begin(), tool.templates.end(), [&](turntable_template const &Template) { return normalized(Template.file) == file; })}; + auto const file{editor_includes::normalized(*included.file)}; + auto const kind{std::find_if(tool.templates.begin(), tool.templates.end(), [&](turntable_template const &Template) { return editor_includes::normalized(Template.file) == file; })}; if (kind == tool.templates.end()) continue; std::string target; @@ -559,7 +546,7 @@ std::vector editor_mode::turntable_includes(TTrack const auto const &included{scene::Layers.instance(handle)}; if (included.removed || included.dead || included.file == nullptr) continue; - auto const kind{std::find_if(m_turntable.templates.begin(), m_turntable.templates.end(), [&](turntable_template const &Template) { return normalized(Template.file) == normalized(*included.file); })}; + auto const kind{std::find_if(m_turntable.templates.begin(), m_turntable.templates.end(), [&](turntable_template const &Template) { return editor_includes::normalized(Template.file) == editor_includes::normalized(*included.file); })}; std::string target; std::vector values; if (kind != m_turntable.templates.end() && editor_includes::parse_directive(scene::Layers.directive(handle), target, values) && kind->track <= static_cast(values.size()) && ToLower(values[kind->track - 1]) == Table.name()) @@ -959,7 +946,7 @@ void editor_mode::render_turntable_ui() else ImGui::TextDisabled(" - "); ImGui::SameLine(); - auto const name{fit.end.track->name().empty() ? std::string{STR_C("(no name)")} : fit.end.track->name()}; + auto const name{name_of(*fit.end.track)}; if (ImGui::Selectable(name.c_str(), false, ImGuiSelectableFlags_None, ImVec2(ImGui::CalcTextSize(name.c_str()).x, 0.f))) focus_track(*fit.end.track, fit.end.point.path, fit.end.position); if (ImGui::IsItemHovered()) @@ -1158,7 +1145,7 @@ bool editor_mode::turntable_controls(std::vector &Rootstatements) auto const &included{scene::Layers.instance(handle)}; if (included.removed || included.dead || included.file == nullptr) continue; - auto const kind{std::find_if(m_turntable.templates.begin(), m_turntable.templates.end(), [&](turntable_template const &Template) { return normalized(Template.file) == normalized(*included.file); })}; + auto const kind{std::find_if(m_turntable.templates.begin(), m_turntable.templates.end(), [&](turntable_template const &Template) { return editor_includes::normalized(Template.file) == editor_includes::normalized(*included.file); })}; if (kind == m_turntable.templates.end() || kind->angles == 0) continue; std::string target; diff --git a/application/editormodevehicle.cpp b/application/editormodevehicle.cpp index e83353f2..1a640076 100644 --- a/application/editormodevehicle.cpp +++ b/application/editormodevehicle.cpp @@ -19,6 +19,7 @@ http://mozilla.org/MPL/2.0/. #include "simulation/simulation.h" #include "utilities/Globals.h" #include "utilities/Logs.h" +#include "utilities/utilities.h" #include "vehicle/DynObj.h" #include "world/Track.h" @@ -37,12 +38,6 @@ using geometry::bezier; std::size_t const kVehicleList{300}; double const kHalfVehicle{8.0}; // m, the vehicle is placed about centred on the point -std::string lower(std::string Text) -{ - std::transform(Text.begin(), Text.end(), Text.begin(), [](unsigned char const Character) { return static_cast(std::tolower(Character)); }); - return Text; -} - std::string skin_name(ui::skin_set const &Skin) { return Skin.meta != nullptr && false == Skin.meta->name.empty() ? Skin.meta->name : Skin.skin; @@ -191,7 +186,7 @@ bool vehicle_tool::place() std::string name; for (int index = 1; index < 10000; ++index) { - name = lower("editor_" + stem + "_" + std::to_string(index)); + name = ToLower("editor_" + stem + "_" + std::to_string(index)); if (simulation::Vehicles.find(name) == nullptr && std::find(names.begin(), names.end(), name) == names.end()) break; } @@ -210,7 +205,7 @@ bool vehicle_tool::place() } tool.placed = head; m_guide.first_step_done(editor_first_step::drive); - auto const trackname{tool.track->name().empty() ? std::string{STR("(no name)")} : tool.track->name()}; + auto const trackname{name_of(*tool.track)}; auto const behind{static_cast(vehicles.size()) - 1}; tool.status = behind > 0 ? format(STR_P("%s and %d vehicle behind it stand on %s", "%s and %d vehicles behind it stand on %s", behind), head.c_str(), behind, trackname.c_str()) : format(STR_C("%s stands on %s"), head.c_str(), trackname.c_str()); if (tool.write && tool.track->name().empty()) @@ -259,7 +254,7 @@ void vehicle_tool::render_options() ImGui::SetNextItemWidth(-1.0f); ImGui::InputTextWithHint("##vehiclesearch", STR_C("search: name, series, skin"), tool.search, sizeof(tool.search)); ImGui::Checkbox(STR_C("Only the ones with a cab"), &tool.controllable_only); - std::string const text{lower(tool.search)}; + std::string const text{ToLower(tool.search)}; std::vector, std::shared_ptr>> listed; std::size_t total{0}; for (auto const &[path, vehicle] : tool.bank->vehicles) @@ -270,7 +265,7 @@ void vehicle_tool::render_options() { if (skin == nullptr) continue; - if (false == text.empty() && lower(skin_label(*vehicle, *skin)).find(text) == std::string::npos && (skin->meta == nullptr || skin->meta->search_lowered.find(text) == std::string::npos)) + if (false == text.empty() && ToLower(skin_label(*vehicle, *skin)).find(text) == std::string::npos && (skin->meta == nullptr || skin->meta->search_lowered.find(text) == std::string::npos)) continue; ++total; if (listed.size() < kVehicleList) @@ -426,13 +421,10 @@ void vehicle_tool::render_options() if (false == tool.consist.empty() || (tool.vehicle != nullptr && tool.skin != nullptr)) { auto const half{std::floor((ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f)}; - auto const tall{std::round(ImGui::GetFrameHeight() * 1.3f)}; - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.55f, 0.25f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.25f, 0.65f, 0.3f, 1.0f)); - if (ImGui::Button(STR_C("Place"), ImVec2(half, tall))) + auto const tall{tall_button_height()}; + if (main_button(STR_C("Place"), ImVec2(half, tall))) place(); item_tooltip("A trainset on the track, placed where the track was clicked, or in the middle of the path"); - ImGui::PopStyleColor(2); ImGui::SameLine(); if (ImGui::Button(STR_C("Place and drive"), ImVec2(-1.0f, tall)) && place()) { diff --git a/application/editortool.h b/application/editortool.h index 19e301bf..f93503f8 100644 --- a/application/editortool.h +++ b/application/editortool.h @@ -84,6 +84,19 @@ struct nmt_place bool shapes{true}; // not a bridge nor a tunnel }; +inline std::map selftest_values(std::istringstream &Words) +{ + std::map values; + std::string pair; + while (Words >> pair) + { + auto const equals{pair.find('=')}; + if (equals != std::string::npos) + values[pair.substr(0, equals)] = pair.substr(equals + 1); + } + return values; +} + class editor_selection { public: diff --git a/editor/editorFormat.hpp b/editor/editorFormat.hpp index e22a9745..0b0940ee 100644 --- a/editor/editorFormat.hpp +++ b/editor/editorFormat.hpp @@ -10,6 +10,7 @@ http://mozilla.org/MPL/2.0/. #pragma once #include +#include #include // printf-style formatting into a string, for the messages and the data lines of the editor @@ -24,3 +25,10 @@ std::string format(char const *Format, Args... Arguments) text.resize(static_cast(length)); return text; } + +inline bool numeric(std::string const &Text, double &Value) +{ + char *end{nullptr}; + Value = std::strtod(Text.c_str(), &end); + return false == Text.empty() && *end == '\0'; +} diff --git a/editor/editorGeometry.hpp b/editor/editorGeometry.hpp index a9ec6667..6c2e6365 100644 --- a/editor/editorGeometry.hpp +++ b/editor/editorGeometry.hpp @@ -38,6 +38,12 @@ inline double plan_distance(glm::dvec3 const &A, glm::dvec3 const &B) } // z component of the 3d cross product; for a unit Direction it's the offset of the Point to its left +inline double heading_of(glm::dvec2 const &Direction) +{ + auto const degrees{glm::degrees(std::atan2(Direction.x, Direction.y))}; + return degrees < 0.0 ? degrees + 360.0 : degrees; +} + inline double cross(glm::dvec2 const &Direction, glm::dvec2 const &Point) { return Direction.x * Point.y - Direction.y * Point.x; diff --git a/editor/editorIncludeInfo.cpp b/editor/editorIncludeInfo.cpp index 45129f57..55b7cd4f 100644 --- a/editor/editorIncludeInfo.cpp +++ b/editor/editorIncludeInfo.cpp @@ -590,6 +590,12 @@ bool complete(include_info const &Info, int const Parameters, std::string *Issue return issue.empty(); } +std::string normalized(std::string File) +{ + std::replace(File.begin(), File.end(), '\\', '/'); + return ToLower(File); +} + std::string number(double const Value) { std::ostringstream converter; diff --git a/editor/editorIncludeInfo.hpp b/editor/editorIncludeInfo.hpp index 0f78cd76..4b70de17 100644 --- a/editor/editorIncludeInfo.hpp +++ b/editor/editorIncludeInfo.hpp @@ -89,6 +89,7 @@ bool parse_directive(std::string const &Directive, std::string &File, std::vecto std::string compose_directive(std::string const &File, std::vector const &Values); // text of a number passed as a parameter value std::string number(double Value); +std::string normalized(std::string File); // number of the parameter with specified role, 0 if the description has none int parameter_with_role(include_info const &Info, std::string const &Role);