diff --git a/common/settings/settings_manager.cpp b/common/settings/settings_manager.cpp index d88689c016..15189acb15 100644 --- a/common/settings/settings_manager.cpp +++ b/common/settings/settings_manager.cpp @@ -704,7 +704,7 @@ bool SETTINGS_MANAGER::LoadProject( const wxString& aFullPath, bool aSetActive ) // No MDI yet if( aSetActive && !m_projects.empty() ) { - PROJECT* oldProject = m_projects.begin()->second.get(); + PROJECT* oldProject = m_projects.begin()->second; unloadProjectFile( oldProject, true ); m_projects.erase( m_projects.begin() ); } @@ -716,12 +716,13 @@ bool SETTINGS_MANAGER::LoadProject( const wxString& aFullPath, bool aSetActive ) bool success = loadProjectFile( *project ); - m_projects[fullPath].reset( project.release() ); + m_projects_list.push_back( std::move( project ) ); + m_projects[fullPath] = m_projects_list.back().get(); wxString fn( path.GetName() ); PROJECT_LOCAL_SETTINGS* settings = static_cast( - RegisterSettings( new PROJECT_LOCAL_SETTINGS( m_projects[fullPath].get(), fn ) ) ); + RegisterSettings( new PROJECT_LOCAL_SETTINGS( m_projects[fullPath], fn ) ) ); m_projects[fullPath]->setLocalSettings( settings ); @@ -740,9 +741,20 @@ bool SETTINGS_MANAGER::UnloadProject( PROJECT* aProject, bool aSave ) if( !unloadProjectFile( aProject, aSave ) ) return false; - wxLogTrace( traceSettings, "Unload project %s", aProject->GetProjectFullName() ); + wxString projectPath = aProject->GetProjectFullName(); + wxLogTrace( traceSettings, "Unload project %s", projectPath ); - m_projects.erase( aProject->GetProjectFullName() ); + PROJECT* toRemove = m_projects.at( projectPath ); + auto it = std::find_if( m_projects_list.begin(), m_projects_list.end(), + [&]( const std::unique_ptr& ptr ) + { + return ptr.get() == toRemove; + } ); + + wxASSERT( it != m_projects_list.end() ); + m_projects_list.erase( it ); + + m_projects.erase( projectPath ); // Immediately reload a null project; this is required until the rest of the application // is refactored to not assume that Prj() always works @@ -775,7 +787,7 @@ bool SETTINGS_MANAGER::IsProjectOpen() const PROJECT* SETTINGS_MANAGER::GetProject( const wxString& aFullPath ) const { if( m_projects.count( aFullPath ) ) - return m_projects.at( aFullPath ).get(); + return m_projects.at( aFullPath ); return nullptr; } @@ -785,7 +797,7 @@ std::vector SETTINGS_MANAGER::GetOpenProjects() const { std::vector ret; - for( const std::pair>& pair : m_projects ) + for( const std::pair& pair : m_projects ) ret.emplace_back( pair.first ); return ret; @@ -816,6 +828,30 @@ bool SETTINGS_MANAGER::SaveProject( const wxString& aFullPath ) } +void SETTINGS_MANAGER::SaveProjectAs( const wxString& aFullPath ) +{ + wxString oldName = Prj().GetProjectFullName(); + + // Changing this will cause UnloadProject to not save over the "old" project when loading below + Prj().setProjectFullName( aFullPath ); + + wxFileName fn( aFullPath ); + + PROJECT_FILE* project = m_project_files.at( oldName ); + project->SetFilename( fn.GetName() ); + project->SaveToFile( fn.GetPath() ); + + Prj().GetLocalSettings().SetFilename( fn.GetName() ); + Prj().GetLocalSettings().SaveToFile( fn.GetPath() ); + + m_project_files[fn.GetFullPath()] = project; + m_project_files.erase( oldName ); + + m_projects[fn.GetFullPath()] = m_projects[oldName]; + m_projects.erase( oldName ); +} + + bool SETTINGS_MANAGER::loadProjectFile( PROJECT& aProject ) { wxFileName fullFn( aProject.GetProjectFullName() ); diff --git a/eeschema/files-io.cpp b/eeschema/files-io.cpp index 54278e1cf9..3919178c8f 100644 --- a/eeschema/files-io.cpp +++ b/eeschema/files-io.cpp @@ -198,6 +198,15 @@ void SCH_EDIT_FRAME::Save_File( bool doSaveAs ) if( fn.GetExt() == LegacySchematicFileExtension ) CreateArchiveLibraryCacheFile( true ); + + // If we are saving under a new name, and don't have a real project yet, create one + fn.SetExt( ProjectFileExtension ); + + if( fn.IsDirWritable() && !fn.FileExists() ) + { + Prj().SetReadOnly( false ); + GetSettingsManager()->SaveProjectAs( fn.GetFullPath() ); + } } } else @@ -291,8 +300,8 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in GetSettingsManager()->LoadProject( pro.GetFullPath() ); // Do not allow saving a project if one doesn't exist. This normally happens if we are - // standalone and opening a board that has been moved from its project folder. - if( !pro.Exists() ) + // standalone and opening a schematic that has been moved from its project folder. + if( !pro.Exists() && !( aCtl & KICTL_CREATE ) ) Prj().SetReadOnly(); CreateScreens(); @@ -342,6 +351,7 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in { // mark new, unsaved file as modified. GetScreen()->SetModify(); + GetScreen()->SetFileName( fullFileName ); } else { diff --git a/include/settings/json_settings.h b/include/settings/json_settings.h index b02d9ef99a..15b7d12634 100644 --- a/include/settings/json_settings.h +++ b/include/settings/json_settings.h @@ -65,6 +65,8 @@ public: wxString GetFullFilename() const; + void SetFilename( const wxString& aFilename ) { m_filename = aFilename; } + SETTINGS_LOC GetLocation() const { return m_location; } void SetLegacyFilename( const wxString& aFilename ) { m_legacy_filename = aFilename; } diff --git a/include/settings/settings_manager.h b/include/settings/settings_manager.h index a4bc0c8355..123defa12f 100644 --- a/include/settings/settings_manager.h +++ b/include/settings/settings_manager.h @@ -254,6 +254,12 @@ public: */ bool SaveProject( const wxString& aFullPath = wxEmptyString ); + /** + * Sets the currently loaded project path and saves it (pointers remain valid) + * @param aFullPath is the full filename to set for the project + */ + void SaveProjectAs( const wxString& aFullPath ); + /** * @return the full path to where project backups should be stored */ @@ -390,8 +396,11 @@ private: /// True if settings loaded successfully at construction bool m_ok; + /// Loaded projects (ownership here) + std::vector> m_projects_list; + /// Loaded projects, mapped according to project full name - std::map> m_projects; + std::map m_projects; /// Loaded project files, mapped according to project full name std::map m_project_files; diff --git a/qa/data/complex_hierarchy.kicad_prl b/qa/data/complex_hierarchy.kicad_prl index 0467523ab1..2c0f55376e 100644 --- a/qa/data/complex_hierarchy.kicad_prl +++ b/qa/data/complex_hierarchy.kicad_prl @@ -33,6 +33,7 @@ 4, 5, 6, + 7, 8, 9, 10, @@ -56,13 +57,10 @@ 28, 29, 30, - 31, - 32, - 33, - 34, - 35 + 36, + 37 ], - "visible_layers": "7ffff_ffffffff" + "visible_layers": "007ffff_ffffffff" }, "meta": { "filename": "complex_hierarchy.kicad_prl", diff --git a/qa/data/complex_hierarchy.kicad_pro b/qa/data/complex_hierarchy.kicad_pro index f02449fec0..f3a493b081 100644 --- a/qa/data/complex_hierarchy.kicad_pro +++ b/qa/data/complex_hierarchy.kicad_pro @@ -64,6 +64,8 @@ "clearance": "error", "copper_edge_clearance": "error", "courtyards_overlap": "error", + "diff_pair_gap_out_of_range": "error", + "diff_pair_uncoupled_length_too_long": "error", "drill_too_small": "error", "duplicate_footprints": "warning", "extra_footprint": "warning", @@ -73,6 +75,7 @@ "item_on_disabled_layer": "error", "items_not_allowed": "error", "keepout": "error", + "length_out_of_range": "error", "malformed_courtyard": "error", "microvia_drill_too_small": "error", "missing_courtyard": "ignore", @@ -82,6 +85,10 @@ "padstack": "error", "pth_inside_courtyard": "ignore", "shorting_items": "error", + "silk_over_copper": "error", + "silk_overlap": "error", + "skew_out_of_range": "error", + "too_many_vias": "error", "track_dangling": "warning", "track_width": "error", "tracks_crossing": "error", @@ -192,7 +199,8 @@ "netlist": "", "specctra_dsn": "", "step": "", - "vmrl": "" + "vmrl": "", + "vrml": "" }, "page_layout_descr_file": "" },