From 6937969175874261615df4dc0539976128ed1805 Mon Sep 17 00:00:00 2001 From: Sven Eberhardt Date: Sat, 11 Mar 2017 00:13:13 -0500 Subject: [PATCH] Force pre-select definitions specified in [Definitions] section of editor template c4s (#1898) --- src/editor/C4ConsoleQtNewScenario.cpp | 140 +++++++++++++++++++++++--- src/editor/C4ConsoleQtNewScenario.h | 19 +++- src/editor/C4ConsoleQtNewScenario.ui | 25 ++++- src/landscape/C4Scenario.cpp | 17 ++++ src/landscape/C4Scenario.h | 1 + 5 files changed, 181 insertions(+), 21 deletions(-) diff --git a/src/editor/C4ConsoleQtNewScenario.cpp b/src/editor/C4ConsoleQtNewScenario.cpp index 3a3d60463..6e0d64b1e 100644 --- a/src/editor/C4ConsoleQtNewScenario.cpp +++ b/src/editor/C4ConsoleQtNewScenario.cpp @@ -25,7 +25,7 @@ /* Definition file list model for new scenario definition selection */ C4ConsoleQtDefinitionFileListModel::DefFileInfo::DefFileInfo(C4ConsoleQtDefinitionFileListModel::DefFileInfo *parent, const char *filename, const char *root_path) - : parent(parent), filename(filename), root_path(root_path), was_opened(false), is_root(false), selected(parent->IsSelected()), disabled(parent->IsSelected() /*not a bug*/) + : parent(parent), filename(filename), root_path(root_path), was_opened(false), is_root(false), user_selected(parent->IsUserSelected()), force_selected(parent->IsForceSelected()) { // Delay opening of groups until information is actually requested // Full names into child groups in C4S always delimeted with backslashes @@ -36,14 +36,12 @@ C4ConsoleQtDefinitionFileListModel::DefFileInfo::DefFileInfo(C4ConsoleQtDefiniti } C4ConsoleQtDefinitionFileListModel::DefFileInfo::DefFileInfo() - : parent(nullptr), was_opened(true), is_root(true), selected(false), disabled(false) + : parent(nullptr), was_opened(true), is_root(true), user_selected(false), force_selected(false) { // Init as root: List definitions in root paths // Objects.ocd is always there (even if not actually found) and always first DefFileInfo *main_objects_def = new DefFileInfo(this, C4CFN_Objects, ""); children.emplace_back(main_objects_def); - main_objects_def->SetSelected(true); - main_objects_def->SetDisabled(true); bool has_default_objects_found = false; for (auto & root_iter : ::Reloc) { @@ -73,14 +71,16 @@ C4ConsoleQtDefinitionFileListModel::DefFileInfo::DefFileInfo() } } -void C4ConsoleQtDefinitionFileListModel::DefFileInfo::SetSelected(bool to_val) +void C4ConsoleQtDefinitionFileListModel::DefFileInfo::SetSelected(bool to_val, bool forced) { - selected = to_val; - // Selection propagates to children; children of selected are disabled because they cannot be un-selected + if (forced) + force_selected = to_val; + else + user_selected = to_val; + // Selection propagates to children for (auto & child : children) { - child->SetSelected(selected); - child->SetDisabled(selected); + child->SetSelected(to_val, forced); } } @@ -126,6 +126,20 @@ int32_t C4ConsoleQtDefinitionFileListModel::DefFileInfo::GetChildIndex(const Def return int32_t(iter - children.begin()); } +void C4ConsoleQtDefinitionFileListModel::DefFileInfo::AddUserSelectedDefinitions(std::list *result) const +{ + // Add parent-most selected + // Ignore any forced selection even if also selected by user. + // It may have been selected first and then forced by the scenario preset after the template has been switched) + if (!IsForceSelected()) + { + if (IsUserSelected()) + result->push_back(full_filename.getData()); + else + for (auto &iter : children) iter->AddUserSelectedDefinitions(result); + } +} + void C4ConsoleQtDefinitionFileListModel::DefFileInfo::AddSelectedDefinitions(std::list *result) const { // Add parent-most selected @@ -135,6 +149,41 @@ void C4ConsoleQtDefinitionFileListModel::DefFileInfo::AddSelectedDefinitions(std for (auto &iter : children) iter->AddSelectedDefinitions(result); } +void C4ConsoleQtDefinitionFileListModel::DefFileInfo::SetForcedSelection(const char *selected_def_filepath) +{ + // Filenames are assumed to be case insensitive for the Windows client + if (SEqualNoCase(selected_def_filepath, full_filename.getData())) + { + // This is the def to be force-selected + SetSelected(true, true); + } + else if (is_root || (SEqual2NoCase(selected_def_filepath, full_filename.getData()) && selected_def_filepath[full_filename.getLength()] == '\\')) + { + // One of the child definitions should be force-selected + if (!was_opened) OpenGroup(); + for (auto &iter : children) iter->SetForcedSelection(selected_def_filepath); + } +} + +void C4ConsoleQtDefinitionFileListModel::DefFileInfo::AddExtraDef(const char *def) +{ + assert(is_root); + // Ignore if it was already added + // Could also avoid adding child definitions if they are already in the list. + // E.g. do not add both foo.ocs\bar.ocd and foo.ocs\bar.ocd\baz.ocd, but keep only the parent path. + // But it's overkill for a case that will probably never happen and would pose just a minor nuisance if it does. + for (auto &iter : children) + { + if (SEqualNoCase(iter->full_filename.getData(), def)) + { + return; + } + } + // Add using user path as root (extra defs will always be in the user path because they are not used by our main system templates) + children.emplace_back(new DefFileInfo(this, def, ::Config.General.UserDataPath)); +} + + C4ConsoleQtDefinitionFileListModel::C4ConsoleQtDefinitionFileListModel() { } @@ -143,6 +192,18 @@ C4ConsoleQtDefinitionFileListModel::~C4ConsoleQtDefinitionFileListModel() { } +void C4ConsoleQtDefinitionFileListModel::AddExtraDef(const char *def) +{ + root.AddExtraDef(def); +} + +std::list C4ConsoleQtDefinitionFileListModel::GetUserSelectedDefinitions() const +{ + std::list result; + root.AddUserSelectedDefinitions(&result); + return result; +} + std::list C4ConsoleQtDefinitionFileListModel::GetSelectedDefinitions() const { std::list result; @@ -150,6 +211,17 @@ std::list C4ConsoleQtDefinitionFileListModel::GetSelectedDefinitio return result; } +void C4ConsoleQtDefinitionFileListModel::SetForcedSelection(std::list &defs) +{ + // Unselect previous + root.SetSelected(false, true); + // Force new selection + for (const char *def : defs) + { + root.SetForcedSelection(def); + } +} + int C4ConsoleQtDefinitionFileListModel::rowCount(const QModelIndex & parent) const { if (!parent.isValid()) return root.GetChildCount(); @@ -216,7 +288,7 @@ bool C4ConsoleQtDefinitionFileListModel::setData(const QModelIndex& index, const DefFileInfo *def = static_cast(index.internalPointer()); if (def && !def->IsDisabled()) { - def->SetSelected(value.toBool()); + def->SetSelected(value.toBool(), false); // Update changed index and all children int32_t child_count = def->GetChildCount(); QModelIndex end_changed = index; @@ -287,6 +359,28 @@ void C4ConsoleQtNewScenarioDlg::AddScenarioTemplate(C4Group &parent, const char // Add it; remember full path as user data StdStrBuf template_path(grp.GetFullName()); ui.templateComboBox->addItem(QString(title.getData()), QString(template_path.getData())); + all_template_c4s.push_back(template_c4s); + // Add any extra definition (e.g. pointing into a scenario) to selection model + // "extra" definitions are those that use non-ocd-files anywhere in their path + auto c4s_defs = template_c4s.Definitions.GetModulesAsList(); + for (const char *c4s_def : c4s_defs) + { + char c4s_def_component[_MAX_PATH + 1]; + int32_t i = 0; + bool is_extra_def = false; + while (SCopySegment(c4s_def, i++, c4s_def_component, '\\', _MAX_PATH)) + { + if (!WildcardMatch(C4CFN_DefFiles, c4s_def_component)) + { + is_extra_def = true; + break; + } + } + if (is_extra_def) + { + def_file_model.AddExtraDef(c4s_def); + } + } // Default selection if (is_default) ui.templateComboBox->setCurrentIndex(ui.templateComboBox->count()-1); } @@ -296,6 +390,20 @@ bool C4ConsoleQtNewScenarioDlg::IsHostAsNetwork() const return ui.startInNetworkCheckbox->isChecked(); } +void C4ConsoleQtNewScenarioDlg::SelectedTemplateChanged(int new_selection) +{ + // Update forced definition selection for template + if (new_selection >= 0 && new_selection < all_template_c4s.size()) + { + const C4Scenario &template_c4s = all_template_c4s[new_selection]; + def_file_model.SetForcedSelection(template_c4s.Definitions.GetModulesAsList()); + } + else + { + def_file_model.SetForcedSelection(std::list()); + } +} + bool C4ConsoleQtNewScenarioDlg::CreateScenario() { // Try to create scenario from template. Unpack if necessery. @@ -339,11 +447,19 @@ bool C4ConsoleQtNewScenarioDlg::CreateScenario() c4s.Game.Mode.Copy(ui.gameModeComboBox->currentText().toUtf8()); if (c4s.Game.Mode == "Undefined") c4s.Game.Mode.Clear(); filename.Copy(ui.filenameEdit->text().toUtf8()); - std::list definitions = def_file_model.GetSelectedDefinitions(); - std::ostringstream definitions_join(""); + std::list definitions = def_file_model.GetUserSelectedDefinitions(); + StdStrBuf forced_definitions; + c4s.Definitions.GetModules(&forced_definitions); + const char *forced_definitions_c = forced_definitions.getData(); + std::ostringstream definitions_join(forced_definitions_c ? forced_definitions_c : nullptr, std::ostringstream::ate); if (definitions.size()) { // definitions_join = definitions.join(";") + if (forced_definitions.getLength()) + { + // Combine both forced and user-selected definitions + definitions_join << ";"; + } auto iter_end = definitions.end(); std::copy(definitions.begin(), --iter_end, std::ostream_iterator(definitions_join, ";")); definitions_join << *iter_end; diff --git a/src/editor/C4ConsoleQtNewScenario.h b/src/editor/C4ConsoleQtNewScenario.h index 978ec31c5..f75d1372a 100644 --- a/src/editor/C4ConsoleQtNewScenario.h +++ b/src/editor/C4ConsoleQtNewScenario.h @@ -34,7 +34,10 @@ class C4ConsoleQtDefinitionFileListModel : public QAbstractItemModel public: C4ConsoleQtDefinitionFileListModel(); ~C4ConsoleQtDefinitionFileListModel(); + void AddExtraDef(const char *def); + std::list GetUserSelectedDefinitions() const; std::list GetSelectedDefinitions() const; + void SetForcedSelection(std::list &defs); private: @@ -46,7 +49,7 @@ private: std::vector< std::unique_ptr > children; StdCopyStrBuf filename, root_path, full_filename; bool was_opened, is_root; - bool selected, disabled; + bool user_selected, force_selected; bool OpenGroup(); public: @@ -58,11 +61,15 @@ private: int32_t GetChildIndex(const DefFileInfo *child); const char *GetName() const { return filename.getData(); } bool IsRoot() const { return is_root; } - void SetSelected(bool to_val); - bool IsSelected() const { return selected; } - void SetDisabled(bool to_val) { disabled = to_val; } - bool IsDisabled() const { return disabled; } + void SetSelected(bool to_val, bool forced); + bool IsUserSelected() const { return user_selected; } + bool IsForceSelected() const { return force_selected; } + bool IsSelected() const { return user_selected || force_selected; } + bool IsDisabled() const { return force_selected || (parent && parent->IsSelected()); } + void AddUserSelectedDefinitions(std::list *result) const; void AddSelectedDefinitions(std::list *result) const; + void SetForcedSelection(const char *selected_def_filepath); + void AddExtraDef(const char *def); }; mutable DefFileInfo root; @@ -85,6 +92,7 @@ class C4ConsoleQtNewScenarioDlg : public QDialog StdCopyStrBuf filename; bool has_custom_filename; C4ConsoleQtDefinitionFileListModel def_file_model; + std::vector all_template_c4s; public: C4ConsoleQtNewScenarioDlg(class QMainWindow *parent_window); @@ -101,6 +109,7 @@ protected slots: void CreatePressed(); void BrowsePressed(); void TitleChanged(const QString &new_title); + void SelectedTemplateChanged(int new_selection); }; #endif // WITH_QT_EDITOR diff --git a/src/editor/C4ConsoleQtNewScenario.ui b/src/editor/C4ConsoleQtNewScenario.ui index 46fe7e0a2..4af693fd7 100644 --- a/src/editor/C4ConsoleQtNewScenario.ui +++ b/src/editor/C4ConsoleQtNewScenario.ui @@ -419,7 +419,7 @@ CreatePressed() - 596 + 535 262 @@ -435,7 +435,7 @@ reject() - 701 + 640 262 @@ -467,8 +467,8 @@ BrowsePressed() - 638 - 73 + 629 + 85 356 @@ -476,10 +476,27 @@ + + templateComboBox + currentIndexChanged(int) + NewScenarioDialog + SelectedTemplateChanged(int) + + + 486 + 131 + + + 325 + 138 + + + CreatePressed() TitleChanged(QString) BrowsePressed() + SelectedTemplateChanged(int) diff --git a/src/landscape/C4Scenario.cpp b/src/landscape/C4Scenario.cpp index 115a3a1a6..19b2f8683 100644 --- a/src/landscape/C4Scenario.cpp +++ b/src/landscape/C4Scenario.cpp @@ -434,6 +434,23 @@ bool C4SDefinitions::GetModules(StdStrBuf *psOutModules) const return true; } +std::list C4SDefinitions::GetModulesAsList() const +{ + // get definitions as string pointers into this structure + std::list result; + if (!LocalOnly) + { + for (const char *def : Definition) + { + if (*def) + { + result.push_back(def); + } + } + } + return result; +} + void C4SDefinitions::SetModules(const char *szList, const char *szRelativeToPath, const char *szRelativeToPath2) { diff --git a/src/landscape/C4Scenario.h b/src/landscape/C4Scenario.h index ecc3fca57..55a63a4da 100644 --- a/src/landscape/C4Scenario.h +++ b/src/landscape/C4Scenario.h @@ -94,6 +94,7 @@ public: C4IDList SkipDefs; void SetModules(const char *szList, const char *szRelativeToPath=nullptr, const char *szRelativeToPath2=nullptr); bool GetModules(StdStrBuf *psOutModules) const; + std::list GetModulesAsList() const; // get definitions as string pointers into this structure void Default(); void CompileFunc(StdCompiler *pComp);