diff --git a/application/editormode.cpp b/application/editormode.cpp index 16a22c92..8d16bbee 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -2986,8 +2986,9 @@ bool editor_mode::save() } first_step_done(first_step::save); - // the heightmap terrain is kept in files of its own, the scenery only needs the directive which loads it + // the heightmap terrain is kept in files of its own, the scenery only needs the directive which loads it, in a layer of its own std::vector rootstatements; + std::map> statements; if (m_streamer.active()) { std::string error; @@ -2997,7 +2998,13 @@ bool editor_mode::save() return false; } if (false == scene::Layers.terrain_directive()) - rootstatements.emplace_back("heightmap_terrain " + m_streamer.name() + " endheightmap_terrain"); + { + auto const directive{"heightmap_terrain " + m_streamer.name() + " endheightmap_terrain"}; + if (auto const layer{output_layer("teren")}; layer != null_handle) + statements[layer].push_back(directive); + else + rootstatements.push_back(directive); + } } if (m_profile.changed) @@ -3016,14 +3023,14 @@ bool editor_mode::save() m_orthophoto_mark = map; scene::Layers.mark(scene::layer_handle{1}, "//$o", {map}); } - auto const terrainadded{false == rootstatements.empty()}; + auto const terrainadded{false == rootstatements.empty() || false == statements.empty()}; auto const controls{turntable_controls(rootstatements)}; auto const noses{nose_controls(rootstatements)}; std::vector trailing; auto const placed{m_vehicle.trainset_lines(trailing)}; if (placed == 0) starter_trainset(trailing); - auto const result = scene::Layers.save(rootstatements, trailing); + auto const result = scene::Layers.save(rootstatements, trailing, statements); if (result.success && controls) m_turntable_included = true; if (result.success && noses) diff --git a/application/editormode.h b/application/editormode.h index 80cc6388..130df383 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -1896,8 +1896,9 @@ class editor_mode : public application_mode, private editor_track::observer, pri void apply_switch_drive_choice(TTrack &Switch); void switch_drive_captured(TTrack const &Track); void switch_drives_committed(std::vector const &Tracks); - // places them in the active layer, as one step of the undo. returns: how many were placed - std::size_t place_templates(std::vector const &Items, std::string &Error) override; + scene::layer_handle output_layer(std::string const &Part) override; + // places them in the active layer or the given one, as one step of the undo. returns: how many were placed + std::size_t place_templates(std::vector const &Items, std::string &Error, scene::layer_handle Layer = scene::layer_handle{}) override; // moves them, as one step of the undo std::size_t move_templates(std::vector> const &Moves) override; // takes them out, as one step of the undo diff --git a/application/editormodelineside.cpp b/application/editormodelineside.cpp index fdfc030e..b22a19b3 100644 --- a/application/editormodelineside.cpp +++ b/application/editormodelineside.cpp @@ -243,7 +243,41 @@ std::vector editor_mode::standing_templates(std: return result; } -std::size_t editor_mode::place_templates(std::vector const &Items, std::string &Error) +scene::layer_handle editor_mode::output_layer(std::string const &Part) +{ + if (scene::Layers.empty()) + return null_handle; + auto stem{scene::Layers.layer(scene::layer_handle{1}).name}; + if (auto const slash{stem.find_last_of("/\\")}; slash != std::string::npos) + stem.erase(0, slash + 1); + if (auto const dot{stem.rfind('.')}; dot != std::string::npos) + stem.erase(dot); + stem.erase(0, stem.find_first_not_of('$')); + if (stem.empty()) + stem = "scenery"; + std::string reason; + // a file of that name the scenery doesn't include, or one which can't take more, gives way to the next number + for (int attempt = 1; attempt <= 9; ++attempt) + { + auto const name{ToLower(stem + "_" + Part + (attempt > 1 ? std::to_string(attempt) : std::string{}) + ".scm")}; + auto layer{null_handle}; + for (std::size_t index = 1; index <= scene::Layers.size() && layer == null_handle; ++index) + if (auto const &existing{scene::Layers.layer(static_cast(index))}; false == existing.dead && false == existing.removed && existing.name == name) + layer = scene::Layers.resolve(static_cast(index)); + if (layer == null_handle) + layer = scene::Layers.create(name, scene::layer_handle{1}, reason, false); + if (layer != null_handle && scene::Layers.accepts(layer, &reason)) + { + scene::Layers.visible(layer, true); + scene::Layers.locked(layer, false); + return layer; + } + } + WriteLog("Editor: no layer for " + Part + ", the active one takes it: " + reason, logtype::generic); + return null_handle; +} + +std::size_t editor_mode::place_templates(std::vector const &Items, std::string &Error, scene::layer_handle const Layer) { if (Items.empty()) return 0; @@ -252,7 +286,7 @@ std::size_t editor_mode::place_templates(std::vector const &Items Error = STR_C("Templates can be placed only in a scenery opened for editing (-edit)"); return 0; } - auto const layer{scene::Layers.resolve(scene::Layers.active())}; + auto const layer{scene::Layers.resolve(Layer != null_handle ? Layer : scene::Layers.active())}; if (std::string reason; false == scene::Layers.accepts(layer, &reason)) { Error = STR_C("The active layer can't take them: ") + (reason.empty() ? std::string{STR_C("there's no active layer")} : reason); @@ -569,8 +603,11 @@ void hekto_tool::hekto_place() items.push_back(std::move(item)); } std::string error; - auto const placed{m_templates.place_templates(items, error)}; + auto const layer{items.empty() ? null_handle : m_templates.output_layer("hektometry")}; + auto const placed{m_templates.place_templates(items, error, layer)}; tool.status = placed > 0 ? format(STR_P("%zu hectometre post placed, Ctrl+Z takes it back", "%zu hectometre posts placed, Ctrl+Z takes them back", static_cast(placed)), placed) : error; + if (placed > 0 && layer != null_handle) + tool.status += format(STR_C(" (layer \"%s\")"), scene::Layers.layer(layer).name.c_str()); if (placed > 0) m_guide.first_step_done(editor_first_step::posts); WriteLog("Editor: " + tool.status, logtype::generic); diff --git a/application/editormodesignal.cpp b/application/editormodesignal.cpp index 740b1549..86180806 100644 --- a/application/editormodesignal.cpp +++ b/application/editormodesignal.cpp @@ -844,7 +844,7 @@ bool editor_mode::signal_place(signal_spot const &Spot, signal_template const &T if (Template.linked) item.values["#7"] = tool.linked[0] != '\0' ? std::string{tool.linked} : std::string{"none"}; std::string error; - if (place_templates({item}, error) == 0) + if (place_templates({item}, error, output_layer("srk")) == 0) { tool.status = refused(STR_C("The signal couldn't be placed: ") + error); return false; @@ -1452,7 +1452,7 @@ std::size_t editor_mode::signal_batch(signal_changes const &Changes, std::vector std::vector instances; if (Error.empty() && false == items.empty()) { - if (place_templates(items, Error) == 0) + if (place_templates(items, Error, output_layer("srk")) == 0) reads.clear(); else { diff --git a/application/editormodesrk.cpp b/application/editormodesrk.cpp index 251182ac..da290173 100644 --- a/application/editormodesrk.cpp +++ b/application/editormodesrk.cpp @@ -879,7 +879,9 @@ srk_tool::record &srk_tool::record_of_open() auto *first{m_layout->switches.front()}; entry.anchor = first->name(); entry.track = first; - entry.layer = alignment_store::layer_of(*first); + entry.layer = m_templates.output_layer("srk"); + if (entry.layer == null_handle) + entry.layer = alignment_store::layer_of(*first); m_records.push_back(std::move(entry)); m_record = static_cast(m_records.size()) - 1; m_anchor = first; diff --git a/application/editortool.h b/application/editortool.h index 7fd1255f..17af2764 100644 --- a/application/editortool.h +++ b/application/editortool.h @@ -199,7 +199,10 @@ class editor_templates { public: virtual std::vector standing_templates(std::function const &Accept) const = 0; - virtual std::size_t place_templates(std::vector const &Items, std::string &Error) = 0; + // the layer a tool keeps what it makes in, "_.scm", made when the scenery has none yet. returns: null_handle if there can't be one + virtual scene::layer_handle output_layer(std::string const &Part) = 0; + // in the active layer unless given another + virtual std::size_t place_templates(std::vector const &Items, std::string &Error, scene::layer_handle Layer = scene::layer_handle{}) = 0; virtual std::size_t move_templates(std::vector> const &Moves) = 0; virtual std::size_t remove_templates(std::vector const &Instances) = 0; diff --git a/lang/editor_pl.po b/lang/editor_pl.po index 7d4ff6b7..87a4e573 100644 --- a/lang/editor_pl.po +++ b/lang/editor_pl.po @@ -11469,3 +11469,6 @@ msgstr[2] "Odczyt przywrócony: %zu z %zu semaforów" msgid ": the rest stand farther than 10 m from a track, by vehicles or their events are gone" msgstr ": pozostałe stoją dalej niż 10 m od toru, przy torze z pojazdami albo ich zdarzenia już nie istnieją" + +msgid " (layer \"%s\")" +msgstr " (warstwa \"%s\")" diff --git a/scene/scenelayers.cpp b/scene/scenelayers.cpp index 18274767..a2d158f1 100644 --- a/scene/scenelayers.cpp +++ b/scene/scenelayers.cpp @@ -558,7 +558,7 @@ void node_layers::stat(layer_handle const Layer) } // creates empty layer, included from specified one. returns: handle to the new layer, or null_handle with the explanation in Reason -layer_handle node_layers::create(std::string Name, layer_handle Parent, std::string &Reason) +layer_handle node_layers::create(std::string Name, layer_handle Parent, std::string &Reason, bool const Activate) { Parent = resolve(Parent); if (false == valid(Parent) || layer(Parent).removed || layer(Parent).binary) @@ -639,7 +639,10 @@ layer_handle node_layers::create(std::string Name, layer_handle Parent, std::str created.context_begin = valid(anchor) ? layer(anchor).context_end : layer(Parent).context_insert(); created.context_end = created.context_begin; - active(layerhandle); + if (Activate) + { + active(layerhandle); + } return layerhandle; } diff --git a/scene/scenelayers.h b/scene/scenelayers.h index 1eb2e4d5..bf342e10 100644 --- a/scene/scenelayers.h +++ b/scene/scenelayers.h @@ -294,8 +294,8 @@ class node_layers void mark(layer_handle Layer, std::string const &Mark, std::vector Lines); // layer management. changes are applied to the scenery files by save() - // creates empty layer, included from specified one. returns: handle to the new layer, or null_handle with the explanation in Reason - layer_handle create(std::string Name, layer_handle Parent, std::string &Reason); + // creates empty layer, included from specified one, made active unless told otherwise. returns: handle to the new layer, or null_handle with the explanation in Reason + layer_handle create(std::string Name, layer_handle Parent, std::string &Reason, bool const Activate = true); bool can_remove(layer_handle Layer, std::string &Reason) const; // marks specified layer and the layers it includes as dropped from the scenery. their files are left on the disk bool remove(layer_handle Layer); @@ -348,8 +348,8 @@ class node_layers // writes changes made in the editor to the scenery files: changed, created and deleted model instances and // memory cells, and layer changes. the files are left intact unless the whole operation can be performed. - // Rootstatements: additional statements to place at the end of the scenario file - save_result save(std::vector const &Rootstatements = {}, std::vector const &Trailingstatements = {}); + // Rootstatements: additional statements to place at the end of the scenario file; Statements: the same for other layers + save_result save(std::vector const &Rootstatements = {}, std::vector const &Trailingstatements = {}, std::map> const &Statements = {}); std::string check_sources() const; private: diff --git a/scene/scenelayerssave.cpp b/scene/scenelayerssave.cpp index c869ff7d..7d6a1ecb 100644 --- a/scene/scenelayerssave.cpp +++ b/scene/scenelayerssave.cpp @@ -633,7 +633,7 @@ bool node_layers::compose(save_state &State, layer_handle const Layer, compositi } // writes changes made in the editor to the scenery files -save_result node_layers::save(std::vector const &Rootstatements, std::vector const &Trailingstatements) +save_result node_layers::save(std::vector const &Rootstatements, std::vector const &Trailingstatements, std::map> const &Statements) { save_result result; if (m_layers.empty()) @@ -1455,7 +1455,7 @@ save_result node_layers::save(std::vector const &Rootstatements, st continue; } if (layer(candidate).created || state.patches.count(candidate) != 0 || appended.count(candidate) != 0 || created.count(candidate) != 0 || placed.count(candidate) != 0 || - markedtext.count(candidate) != 0 || (candidate == root && (false == Rootstatements.empty() || false == Trailingstatements.empty()))) + markedtext.count(candidate) != 0 || Statements.count(candidate) != 0 || (candidate == root && (false == Rootstatements.empty() || false == Trailingstatements.empty()))) { outputs.emplace_back(candidate); } @@ -1568,6 +1568,14 @@ save_result node_layers::save(std::vector const &Rootstatements, st added.text += statement + eol; } } + if (auto const lookup{Statements.find(output)}; lookup != Statements.end()) + { + for (auto const &statement : lookup->second) + { + ensure_newline(added.text, eol); + added.text += statement + eol; + } + } if (output == root && false == Trailingstatements.empty()) { composition trailing;