diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 763b42f138..9f752c0f7d 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -353,6 +353,8 @@ set( COMMON_DLG_SRCS dialogs/dialog_color_picker_base.cpp dialogs/dialog_configure_paths.cpp dialogs/dialog_configure_paths_base.cpp + dialogs/dialog_design_block_properties.cpp + dialogs/dialog_design_block_properties_base.cpp dialogs/dialog_display_html_text_base.cpp dialogs/dialog_edit_library_tables.cpp dialogs/dialog_embed_files.cpp @@ -431,6 +433,7 @@ set( COMMON_WIDGET_SRCS widgets/bitmap_toggle.cpp widgets/button_row_panel.cpp widgets/color_swatch.cpp + widgets/design_block_pane.cpp widgets/filter_combobox.cpp widgets/font_choice.cpp widgets/footprint_choice.cpp @@ -455,6 +458,7 @@ set( COMMON_WIDGET_SRCS widgets/mathplot.cpp widgets/msgpanel.cpp widgets/paged_dialog.cpp + widgets/panel_design_block_chooser.cpp widgets/properties_panel.cpp widgets/search_pane.cpp widgets/search_pane_base.cpp @@ -616,6 +620,7 @@ set( COMMON_SRCS lib_table_grid_tricks.cpp lib_tree_model.cpp lib_tree_model_adapter.cpp + design_block_tree_model_adapter.cpp marker_base.cpp origin_transforms.cpp printout.cpp @@ -649,6 +654,7 @@ set( COMMON_SRCS tool/construction_manager.cpp tool/common_tools.cpp tool/conditional_menu.cpp + tool/design_block_control.cpp tool/edit_constraints.cpp tool/edit_points.cpp tool/editor_conditions.cpp diff --git a/common/advanced_config.cpp b/common/advanced_config.cpp index 0b267de866..e6b985b4fc 100644 --- a/common/advanced_config.cpp +++ b/common/advanced_config.cpp @@ -98,7 +98,7 @@ static const wxChar AllowManualCanvasScale[] = wxT( "AllowManualCanvasScale" ); static const wxChar UpdateUIEventInterval[] = wxT( "UpdateUIEventInterval" ); static const wxChar V3DRT_BevelHeight_um[] = wxT( "V3DRT_BevelHeight_um" ); static const wxChar V3DRT_BevelExtentFactor[] = wxT( "V3DRT_BevelExtentFactor" ); -static const wxChar EnableDesignBlocks[] = wxT( "EnableDesignBlocks" ); +static const wxChar EnablePcbDesignBlocks[] = wxT( "EnablePcbDesignBlocks" ); static const wxChar EnableGenerators[] = wxT( "EnableGenerators" ); static const wxChar EnableGit[] = wxT( "EnableGit" ); static const wxChar EnableLibWithText[] = wxT( "EnableLibWithText" ); @@ -257,7 +257,7 @@ ADVANCED_CFG::ADVANCED_CFG() m_CompactSave = false; m_UpdateUIEventInterval = 0; m_ShowRepairSchematic = false; - m_EnableDesignBlocks = true; + m_EnablePcbDesignBlocks = false; m_EnableGenerators = false; m_EnableGit = true; m_EnableLibWithText = false; @@ -493,8 +493,8 @@ void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg ) m_DisambiguationMenuDelay, 50, 10000 ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableDesignBlocks, - &m_EnableDesignBlocks, m_EnableDesignBlocks ) ); + configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnablePcbDesignBlocks, &m_EnablePcbDesignBlocks, + m_EnablePcbDesignBlocks ) ); configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableGenerators, &m_EnableGenerators, m_EnableGenerators ) ); diff --git a/common/design_block.h b/common/design_block.h index 9a862411ae..533c3008b2 100644 --- a/common/design_block.h +++ b/common/design_block.h @@ -21,6 +21,10 @@ * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ + +#ifndef DESIGN_BLOCK_H +#define DESIGN_BLOCK_H + #include <kicommon.h> #include <lib_id.h> #include <nlohmann/json.hpp> @@ -41,6 +45,9 @@ public: const wxString& GetSchematicFile() const { return m_schematicFile; } void SetSchematicFile( const wxString& aFile ) { m_schematicFile = aFile; } + const wxString& GetBoardFile() const { return m_boardFile; } + void SetBoardFile( const wxString& aFile ) { m_boardFile = aFile; } + void SetFields( nlohmann::ordered_map<wxString, wxString>& aFields ) { m_fields = std::move( aFields ); @@ -55,9 +62,12 @@ public: private: LIB_ID m_lib_id; - wxString m_schematicFile; ///< File name and path for schematic symbol. + wxString m_schematicFile; ///< File name and path for schematic file. + wxString m_boardFile; ///< File name and path for board file wxString m_libDescription; ///< File name and path for documentation file. - wxString m_keywords; ///< Search keywords to find footprint in library. + wxString m_keywords; ///< Search keywords to find design block in library. nlohmann::ordered_map<wxString, wxString> m_fields; }; + +#endif diff --git a/common/design_block_io.cpp b/common/design_block_io.cpp index e25acbad2e..2fd2ed875f 100644 --- a/common/design_block_io.cpp +++ b/common/design_block_io.cpp @@ -267,7 +267,9 @@ void DESIGN_BLOCK_IO::DesignBlockEnumerate( wxArrayString& aDesignBlockNames, wxDir dir( aLibraryPath ); if( !dir.IsOpened() ) - return; + { + THROW_IO_ERROR( wxString::Format( _( "Design block '%s' does not exist." ), aLibraryPath ) ); + } wxString dirname; wxString fileSpec = wxT( "*." ) + wxString( FILEEXT::KiCadDesignBlockPathExtension ); @@ -289,17 +291,23 @@ DESIGN_BLOCK* DESIGN_BLOCK_IO::DesignBlockLoad( const wxString& aLibraryPath, + FILEEXT::KiCadDesignBlockPathExtension + wxFileName::GetPathSeparator(); wxString dbSchPath = dbPath + aDesignBlockName + wxT( "." ) + FILEEXT::KiCadSchematicFileExtension; + wxString dbPcbPath = dbPath + aDesignBlockName + wxT( "." ) + FILEEXT::KiCadPcbFileExtension; wxString dbMetadataPath = dbPath + aDesignBlockName + wxT( "." ) + FILEEXT::JsonFileExtension; - if( !wxFileExists( dbSchPath ) ) - return nullptr; + if( !wxDir::Exists( dbPath ) ) + THROW_IO_ERROR( wxString::Format( _( "Design block '%s' does not exist." ), dbPath ) ); DESIGN_BLOCK* newDB = new DESIGN_BLOCK(); // Library name needs to be empty for when we fill it in with the correct library nickname // one layer above newDB->SetLibId( LIB_ID( wxEmptyString, aDesignBlockName ) ); - newDB->SetSchematicFile( dbSchPath ); + + if( wxFileExists( dbSchPath ) ) + newDB->SetSchematicFile( dbSchPath ); + + if( wxFileExists( dbPcbPath ) ) + newDB->SetBoardFile( dbPcbPath ); // Parse the JSON file if it exists if( wxFileExists( dbMetadataPath ) ) @@ -335,16 +343,27 @@ DESIGN_BLOCK* DESIGN_BLOCK_IO::DesignBlockLoad( const wxString& aLibraryPath, } catch( ... ) { + delete newDB; THROW_IO_ERROR( wxString::Format( _( "Design block metadata file '%s' could not be read." ), dbMetadataPath ) ); } } - return newDB; } +bool DESIGN_BLOCK_IO::DesignBlockExists( const wxString& aLibraryPath, + const wxString& aDesignBlockName, + const std::map<std::string, UTF8>* aProperties ) +{ + wxString dbPath = aLibraryPath + wxFileName::GetPathSeparator() + aDesignBlockName + wxT( "." ) + + FILEEXT::KiCadDesignBlockPathExtension + wxFileName::GetPathSeparator(); + + return wxDir::Exists( dbPath ); +} + + void DESIGN_BLOCK_IO::DesignBlockSave( const wxString& aLibraryPath, const DESIGN_BLOCK* aDesignBlock, const std::map<std::string, UTF8>* aProperties ) @@ -355,12 +374,23 @@ void DESIGN_BLOCK_IO::DesignBlockSave( const wxString& aLibra THROW_IO_ERROR( _( "Design block does not have a valid library ID." ) ); } - wxFileName schematicFile( aDesignBlock->GetSchematicFile() ); - - if( !schematicFile.FileExists() ) + if( aDesignBlock->GetSchematicFile().IsEmpty() && aDesignBlock->GetBoardFile().IsEmpty() ) { - THROW_IO_ERROR( wxString::Format( _( "Schematic source file '%s' does not exist." ), - schematicFile.GetFullPath() ) ); + THROW_IO_ERROR( _( "Design block does not have a schematic or board file." ) ); + } + + wxFileName schematicFile( aDesignBlock->GetSchematicFile() ); + wxFileName boardFile( aDesignBlock->GetBoardFile() ); + + if( !aDesignBlock->GetSchematicFile().IsEmpty() && !schematicFile.FileExists() ) + { + THROW_IO_ERROR( + wxString::Format( _( "Schematic source file '%s' does not exist." ), schematicFile.GetFullPath() ) ); + } + + if( !aDesignBlock->GetBoardFile().IsEmpty() && !boardFile.FileExists() ) + { + THROW_IO_ERROR( wxString::Format( _( "Board source file '%s' does not exist." ), boardFile.GetFullPath() ) ); } // Create the design block folder @@ -378,23 +408,44 @@ void DESIGN_BLOCK_IO::DesignBlockSave( const wxString& aLibra } } - // The new schematic file name is based on the design block name, not the source sheet name - wxString dbSchematicFile = dbFolder.GetFullPath() + aDesignBlock->GetLibId().GetLibItemName() - + wxT( "." ) + FILEEXT::KiCadSchematicFileExtension; - - // If the source and destination files are the same, then we don't need to copy the file - // as we are just updating the metadata - if( schematicFile.GetFullPath() != dbSchematicFile ) + if( !aDesignBlock->GetSchematicFile().IsEmpty() ) { - // Copy the source sheet file to the design block folder, under the design block name - if( !wxCopyFile( schematicFile.GetFullPath(), dbSchematicFile ) ) + // The new schematic file name is based on the design block name, not the source sheet name + wxString dbSchematicFile = dbFolder.GetFullPath() + aDesignBlock->GetLibId().GetLibItemName() + wxT( "." ) + + FILEEXT::KiCadSchematicFileExtension; + + // If the source and destination files are the same, then we don't need to copy the file + // as we are just updating the metadata + if( schematicFile.GetFullPath() != dbSchematicFile ) { - THROW_IO_ERROR( wxString::Format( - _( "Schematic file '%s' could not be saved as design block at '%s'." ), - schematicFile.GetFullPath(), dbSchematicFile ) ); + // Copy the source sheet file to the design block folder, under the design block name + if( !wxCopyFile( schematicFile.GetFullPath(), dbSchematicFile ) ) + { + THROW_IO_ERROR( + wxString::Format( _( "Schematic file '%s' could not be saved as design block at '%s'." ), + schematicFile.GetFullPath(), dbSchematicFile ) ); + } } } + if( !aDesignBlock->GetBoardFile().IsEmpty() ) + { + // The new Board file name is based on the design block name, not the source sheet name + wxString dbBoardFile = dbFolder.GetFullPath() + aDesignBlock->GetLibId().GetLibItemName() + wxT( "." ) + + FILEEXT::KiCadPcbFileExtension; + + // If the source and destination files are the same, then we don't need to copy the file + // as we are just updating the metadata + if( boardFile.GetFullPath() != dbBoardFile ) + { + // Copy the source sheet file to the design block folder, under the design block name + if( !wxCopyFile( boardFile.GetFullPath(), dbBoardFile ) ) + { + THROW_IO_ERROR( wxString::Format( _( "Board file '%s' could not be saved as design block at '%s'." ), + boardFile.GetFullPath(), dbBoardFile ) ); + } + } + } wxString dbMetadataFile = dbFolder.GetFullPath() + aDesignBlock->GetLibId().GetLibItemName() + wxT( "." ) + FILEEXT::JsonFileExtension; diff --git a/common/design_block_io.h b/common/design_block_io.h index 9eb2b335ce..aa1598651b 100644 --- a/common/design_block_io.h +++ b/common/design_block_io.h @@ -76,10 +76,7 @@ public: } bool DesignBlockExists( const wxString& aLibraryPath, const wxString& aDesignBlockName, - const std::map<std::string, UTF8>* aProperties = nullptr ) - { - return DesignBlockLoad( aLibraryPath, aDesignBlockName, true, aProperties ) != nullptr; - } + const std::map<std::string, UTF8>* aProperties = nullptr ); DESIGN_BLOCK* ImportDesignBlock( const wxString& aDesignBlockPath, wxString& aDesignBlockNameOut, diff --git a/common/design_block_lib_table.cpp b/common/design_block_lib_table.cpp index 774158b395..bbeeac30ec 100644 --- a/common/design_block_lib_table.cpp +++ b/common/design_block_lib_table.cpp @@ -73,6 +73,23 @@ void DESIGN_BLOCK_LIB_TABLE_ROW::SetType( const wxString& aType ) } +bool DESIGN_BLOCK_LIB_TABLE_ROW::Refresh() +{ + if( !plugin ) + { + wxArrayString dummyList; + + plugin.reset( DESIGN_BLOCK_IO_MGR::FindPlugin( type ) ); + SetLoaded( false ); + plugin->DesignBlockEnumerate( dummyList, GetFullURI( true ), true, GetProperties() ); + SetLoaded( true ); + return true; + } + + return false; +} + + DESIGN_BLOCK_LIB_TABLE::DESIGN_BLOCK_LIB_TABLE( DESIGN_BLOCK_LIB_TABLE* aFallBackTable ) : LIB_TABLE( aFallBackTable ) { diff --git a/eeschema/design_block_tree_model_adapter.cpp b/common/design_block_tree_model_adapter.cpp similarity index 85% rename from eeschema/design_block_tree_model_adapter.cpp rename to common/design_block_tree_model_adapter.cpp index d561352cf3..9e191f3a72 100644 --- a/eeschema/design_block_tree_model_adapter.cpp +++ b/common/design_block_tree_model_adapter.cpp @@ -25,30 +25,32 @@ #include <project/project_file.h> #include <wx/log.h> #include <wx/tokenzr.h> +#include <settings/app_settings.h> #include <string_utils.h> #include <eda_pattern_match.h> #include <design_block.h> #include <design_block_lib_table.h> #include <design_block_info.h> #include <design_block_tree_model_adapter.h> -#include <tools/sch_design_block_control.h> wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER> DESIGN_BLOCK_TREE_MODEL_ADAPTER::Create( EDA_BASE_FRAME* aParent, LIB_TABLE* aLibs, - APP_SETTINGS_BASE::LIB_TREE& aSettings ) + APP_SETTINGS_BASE::LIB_TREE& aSettings, + TOOL_INTERACTIVE* aContextMenuTool ) { - auto* adapter = new DESIGN_BLOCK_TREE_MODEL_ADAPTER( aParent, aLibs, aSettings ); - adapter->m_frame = aParent; + auto* adapter = new DESIGN_BLOCK_TREE_MODEL_ADAPTER( aParent, aLibs, aSettings, aContextMenuTool ); return wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>( adapter ); } -DESIGN_BLOCK_TREE_MODEL_ADAPTER::DESIGN_BLOCK_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent, - LIB_TABLE* aLibs, - APP_SETTINGS_BASE::LIB_TREE& aSettings ) : - LIB_TREE_MODEL_ADAPTER( aParent, wxT( "pinned_design_block_libs" ), aSettings ), +DESIGN_BLOCK_TREE_MODEL_ADAPTER::DESIGN_BLOCK_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent, LIB_TABLE* aLibs, + APP_SETTINGS_BASE::LIB_TREE& aSettings, + TOOL_INTERACTIVE* aContextMenuTool ) : + LIB_TREE_MODEL_ADAPTER( aParent, wxT( "pinned_design_block_libs" ), + Kiface().KifaceSettings()->m_DesignBlockChooserPanel.tree ), m_libs( (DESIGN_BLOCK_LIB_TABLE*) aLibs ), - m_frame( aParent ) + m_frame( aParent ), + m_contextMenuTool( aContextMenuTool ) { } @@ -74,8 +76,7 @@ void DESIGN_BLOCK_TREE_MODEL_ADAPTER::AddLibraries( EDA_BASE_FRAME* aParent ) bool pinned = alg::contains( cfg->m_Session.pinned_design_block_libs, libName ) || alg::contains( project.m_PinnedDesignBlockLibs, libName ); - DoAddLibrary( libName, library->GetDescr(), getDesignBlocks( aParent, libName ), pinned, - true ); + DoAddLibrary( libName, library->GetDescr(), getDesignBlocks( aParent, libName ), pinned, true ); } m_tree.AssignIntrinsicRanks(); @@ -88,23 +89,20 @@ void DESIGN_BLOCK_TREE_MODEL_ADAPTER::ClearLibraries() } -std::vector<LIB_TREE_ITEM*> -DESIGN_BLOCK_TREE_MODEL_ADAPTER::getDesignBlocks( EDA_BASE_FRAME* aParent, - const wxString& aLibName ) +std::vector<LIB_TREE_ITEM*> DESIGN_BLOCK_TREE_MODEL_ADAPTER::getDesignBlocks( EDA_BASE_FRAME* aParent, + const wxString& aLibName ) { std::vector<LIB_TREE_ITEM*> libList; auto fullListStart = DESIGN_BLOCK_LIB_TABLE::GetGlobalList().GetList().begin(); auto fullListEnd = DESIGN_BLOCK_LIB_TABLE::GetGlobalList().GetList().end(); - std::unique_ptr<DESIGN_BLOCK_INFO> dummy = - std::make_unique<DESIGN_BLOCK_INFO_IMPL>( aLibName, wxEmptyString ); + std::unique_ptr<DESIGN_BLOCK_INFO> dummy = std::make_unique<DESIGN_BLOCK_INFO_IMPL>( aLibName, wxEmptyString ); // List is sorted, so use a binary search to find the range of footnotes for our library auto libBounds = std::equal_range( fullListStart, fullListEnd, dummy, - []( const std::unique_ptr<DESIGN_BLOCK_INFO>& a, - const std::unique_ptr<DESIGN_BLOCK_INFO>& b ) + []( const std::unique_ptr<DESIGN_BLOCK_INFO>& a, const std::unique_ptr<DESIGN_BLOCK_INFO>& b ) { return StrNumCmp( a->GetLibNickname(), b->GetLibNickname(), false ) < 0; } ); @@ -148,8 +146,7 @@ wxString DESIGN_BLOCK_TREE_MODEL_ADAPTER::GenerateInfo( LIB_ID const& aLibId, in catch( const IO_ERROR& ioe ) { wxLogError( _( "Error loading design block %s from library '%s'." ) + wxS( "\n%s" ), - aLibId.GetLibItemName().wx_str(), aLibId.GetLibNickname().wx_str(), - ioe.What() ); + aLibId.GetLibItemName().wx_str(), aLibId.GetLibNickname().wx_str(), ioe.What() ); return wxEmptyString; } @@ -206,5 +203,5 @@ wxString DESIGN_BLOCK_TREE_MODEL_ADAPTER::GenerateInfo( LIB_ID const& aLibId, in TOOL_INTERACTIVE* DESIGN_BLOCK_TREE_MODEL_ADAPTER::GetContextMenuTool() { - return m_frame->GetToolManager()->GetTool<SCH_DESIGN_BLOCK_CONTROL>(); + return m_contextMenuTool; } diff --git a/eeschema/design_block_tree_model_adapter.h b/common/design_block_tree_model_adapter.h similarity index 86% rename from eeschema/design_block_tree_model_adapter.h rename to common/design_block_tree_model_adapter.h index b8e46e1cbd..35f33f1579 100644 --- a/eeschema/design_block_tree_model_adapter.h +++ b/common/design_block_tree_model_adapter.h @@ -33,9 +33,9 @@ public: * * @param aLibs library set from which parts will be loaded */ - static wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER> Create( EDA_BASE_FRAME* aParent, - LIB_TABLE* aLibs, - APP_SETTINGS_BASE::LIB_TREE& aSettings ); + static wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER> Create( EDA_BASE_FRAME* aParent, LIB_TABLE* aLibs, + APP_SETTINGS_BASE::LIB_TREE& aSettings, + TOOL_INTERACTIVE* aContextMenuTool ); void AddLibraries( EDA_BASE_FRAME* aParent ); void ClearLibraries(); @@ -51,16 +51,17 @@ protected: * Constructor; takes a set of libraries to be included in the search. */ DESIGN_BLOCK_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent, LIB_TABLE* aLibs, - APP_SETTINGS_BASE::LIB_TREE& aSettings ); + APP_SETTINGS_BASE::LIB_TREE& aSettings, + TOOL_INTERACTIVE* aContextMenuTool ); - std::vector<LIB_TREE_ITEM*> getDesignBlocks( EDA_BASE_FRAME* aParent, - const wxString& aLibName ); + std::vector<LIB_TREE_ITEM*> getDesignBlocks( EDA_BASE_FRAME* aParent, const wxString& aLibName ); PROJECT::LIB_TYPE_T getLibType() override { return PROJECT::LIB_TYPE_T::DESIGN_BLOCK_LIB; } protected: DESIGN_BLOCK_LIB_TABLE* m_libs; EDA_BASE_FRAME* m_frame; + TOOL_INTERACTIVE* m_contextMenuTool; }; #endif // DESIGN_BLOCK_TREE_MODEL_ADAPTER_H diff --git a/eeschema/dialogs/dialog_design_block_properties.cpp b/common/dialogs/dialog_design_block_properties.cpp similarity index 92% rename from eeschema/dialogs/dialog_design_block_properties.cpp rename to common/dialogs/dialog_design_block_properties.cpp index f20fac8d8b..744aeb4d7b 100644 --- a/eeschema/dialogs/dialog_design_block_properties.cpp +++ b/common/dialogs/dialog_design_block_properties.cpp @@ -24,20 +24,20 @@ #include <dialogs/dialog_design_block_properties.h> -#include <sch_edit_frame.h> #include <wx/msgdlg.h> #include <wx/tooltip.h> #include <grid_tricks.h> #include <widgets/std_bitmap_button.h> +#include <bitmaps.h> #include <design_block.h> -DIALOG_DESIGN_BLOCK_PROPERTIES::DIALOG_DESIGN_BLOCK_PROPERTIES( SCH_EDIT_FRAME* aParent, - DESIGN_BLOCK* aDesignBlock ) : +DIALOG_DESIGN_BLOCK_PROPERTIES::DIALOG_DESIGN_BLOCK_PROPERTIES( wxWindow* aParent, + DESIGN_BLOCK* aDesignBlock, + bool aDisableName ) : DIALOG_DESIGN_BLOCK_PROPERTIES_BASE( aParent ), m_designBlock( aDesignBlock ) { - if( !m_textName->IsEmpty() ) - m_textName->SetEditable( false ); + m_textName->SetEditable( !aDisableName ); wxToolTip::Enable( true ); SetupStandardButtons(); @@ -50,10 +50,11 @@ DIALOG_DESIGN_BLOCK_PROPERTIES::DIALOG_DESIGN_BLOCK_PROPERTIES( SCH_EDIT_FRAME* m_fieldsGrid->SetUseNativeColLabels(); - m_fieldsGrid->PushEventHandler( new GRID_TRICKS( m_fieldsGrid, [this]( wxCommandEvent& aEvent ) - { - OnAddField( aEvent ); - } ) ); + m_fieldsGrid->PushEventHandler( new GRID_TRICKS( m_fieldsGrid, + [this]( wxCommandEvent& aEvent ) + { + OnAddField( aEvent ); + } ) ); m_fieldsGrid->SetSelectionMode( wxGrid::wxGridSelectRows ); } @@ -86,8 +87,7 @@ bool DIALOG_DESIGN_BLOCK_PROPERTIES::TransferDataToWindow() bool DIALOG_DESIGN_BLOCK_PROPERTIES::TransferDataFromWindow() { - m_designBlock->SetLibId( - LIB_ID( m_designBlock->GetLibId().GetLibNickname(), m_textName->GetValue() ) ); + m_designBlock->SetLibId( LIB_ID( m_designBlock->GetLibId().GetLibNickname(), m_textName->GetValue() ) ); m_designBlock->SetLibDescription( m_textDescription->GetValue() ); m_designBlock->SetKeywords( m_textKeywords->GetValue() ); diff --git a/eeschema/dialogs/dialog_design_block_properties.h b/common/dialogs/dialog_design_block_properties.h similarity index 88% rename from eeschema/dialogs/dialog_design_block_properties.h rename to common/dialogs/dialog_design_block_properties.h index c4dc5f1b7b..00fd46355e 100644 --- a/eeschema/dialogs/dialog_design_block_properties.h +++ b/common/dialogs/dialog_design_block_properties.h @@ -22,6 +22,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#ifndef DIALOG_DESIGN_BLOCK_PROPERTIES_H +#define DIALOG_DESIGN_BLOCK_PROPERTIES_H + #include <dialogs/dialog_design_block_properties_base.h> #include <nlohmann/json.hpp> @@ -31,7 +34,7 @@ class DESIGN_BLOCK; class DIALOG_DESIGN_BLOCK_PROPERTIES : public DIALOG_DESIGN_BLOCK_PROPERTIES_BASE { public: - DIALOG_DESIGN_BLOCK_PROPERTIES( SCH_EDIT_FRAME* aParent, DESIGN_BLOCK* aDesignBlock ); + DIALOG_DESIGN_BLOCK_PROPERTIES( wxWindow* aParent, DESIGN_BLOCK* aDesignBlock, bool aDisableName = false ); ~DIALOG_DESIGN_BLOCK_PROPERTIES() override; bool TransferDataToWindow() override; @@ -50,6 +53,8 @@ public: private: void AdjustGridColumns( int aWidth ); - DESIGN_BLOCK* m_designBlock; + DESIGN_BLOCK* m_designBlock; nlohmann::ordered_map<wxString, wxString> m_fields; }; + +#endif diff --git a/eeschema/dialogs/dialog_design_block_properties_base.cpp b/common/dialogs/dialog_design_block_properties_base.cpp similarity index 100% rename from eeschema/dialogs/dialog_design_block_properties_base.cpp rename to common/dialogs/dialog_design_block_properties_base.cpp diff --git a/eeschema/dialogs/dialog_design_block_properties_base.fbp b/common/dialogs/dialog_design_block_properties_base.fbp similarity index 100% rename from eeschema/dialogs/dialog_design_block_properties_base.fbp rename to common/dialogs/dialog_design_block_properties_base.fbp diff --git a/eeschema/dialogs/dialog_design_block_properties_base.h b/common/dialogs/dialog_design_block_properties_base.h similarity index 100% rename from eeschema/dialogs/dialog_design_block_properties_base.h rename to common/dialogs/dialog_design_block_properties_base.h diff --git a/common/dialogs/panel_design_block_lib_table.cpp b/common/dialogs/panel_design_block_lib_table.cpp index 52c93444da..28904ed141 100644 --- a/common/dialogs/panel_design_block_lib_table.cpp +++ b/common/dialogs/panel_design_block_lib_table.cpp @@ -633,6 +633,44 @@ bool PANEL_DESIGN_BLOCK_LIB_TABLE::verifyTables() } } + for( DESIGN_BLOCK_LIB_TABLE* table : { global_model(), project_model() } ) + { + if( !table ) + continue; + + for( unsigned int r = 0; r < table->GetCount(); ++r ) + { + DESIGN_BLOCK_LIB_TABLE_ROW& row = dynamic_cast<DESIGN_BLOCK_LIB_TABLE_ROW&>( table->At( r ) ); + + // Newly-added rows won't have set this yet + row.SetParent( table ); + + if( !row.GetIsEnabled() ) + continue; + + try + { + if( row.Refresh() ) + { + if( table == global_model() ) + m_parent->m_GlobalTableChanged = true; + else + m_parent->m_ProjectTableChanged = true; + } + } + catch( const IO_ERROR& ioe ) + { + msg.Printf( _( "Design block library '%s' failed to load." ), row.GetNickName() ); + + wxWindow* topLevelParent = wxGetTopLevelParent( this ); + wxMessageDialog errdlg( topLevelParent, msg + wxS( "\n" ) + ioe.What(), _( "Error Loading Library" ) ); + errdlg.ShowModal(); + + return true; + } + } + } + return true; } @@ -1214,7 +1252,7 @@ void InvokeEditDesignBlockLibTable( KIWAY* aKiway, wxWindow *aParent ) std::string payload = ""; aKiway->ExpressMail( FRAME_SCH, MAIL_RELOAD_LIB, payload ); - aKiway->ExpressMail( FRAME_SCH_VIEWER, MAIL_RELOAD_LIB, payload ); + aKiway->ExpressMail( FRAME_PCB_EDITOR, MAIL_RELOAD_LIB, payload ); return; } diff --git a/common/settings/app_settings.cpp b/common/settings/app_settings.cpp index ee0d3511e4..a77e0d4871 100644 --- a/common/settings/app_settings.cpp +++ b/common/settings/app_settings.cpp @@ -71,6 +71,59 @@ APP_SETTINGS_BASE::APP_SETTINGS_BASE( const std::string& aFilename, int aSchemaV m_params.emplace_back( new PARAM_LIST<wxString>( "find_replace.replace_history", &m_FindReplace.replace_history, {} ) ); + m_params.emplace_back( new PARAM<int>( "design_block_chooser.sash_pos_h", + &m_DesignBlockChooserPanel.sash_pos_h, -1 ) ); + + m_params.emplace_back( new PARAM<int>( "design_block_chooser.sash_pos_v", + &m_DesignBlockChooserPanel.sash_pos_v, -1 ) ); + + m_params.emplace_back( new PARAM<int>( "design_block_chooser.width", + &m_DesignBlockChooserPanel.width, -1 ) ); + + m_params.emplace_back( new PARAM<int>( "design_block_chooser.height", + &m_DesignBlockChooserPanel.height, -1 ) ); + + m_params.emplace_back( new PARAM<int>( "design_block_chooser.sort_mode", + &m_DesignBlockChooserPanel.sort_mode, 0 ) ); + + m_params.emplace_back( new PARAM<bool>( "design_block_chooser.repeated_placement", + &m_DesignBlockChooserPanel.repeated_placement, false ) ); + + m_params.emplace_back( new PARAM<bool>( "design_block_chooser.place_as_sheet", + &m_DesignBlockChooserPanel.place_as_sheet, false ) ); + + m_params.emplace_back( new PARAM<bool>( "design_block_chooser.keep_annotations", + &m_DesignBlockChooserPanel.keep_annotations, false ) ); + + m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( + "design_block_chooser.lib_tree.column_widths", + [&]() -> nlohmann::json + { + nlohmann::json ret = {}; + + for( const auto& [name, width] : m_DesignBlockChooserPanel.tree.column_widths ) + ret[std::string( name.ToUTF8() )] = width; + + return ret; + }, + [&]( const nlohmann::json& aJson ) + { + if( !aJson.is_object() ) + return; + + m_DesignBlockChooserPanel.tree.column_widths.clear(); + + for( const auto& entry : aJson.items() ) + { + if( !entry.value().is_number_integer() ) + continue; + + m_DesignBlockChooserPanel.tree.column_widths[entry.key()] = entry.value().get<int>(); + } + }, + {} ) ); + + m_params.emplace_back( new PARAM<int>( "graphics.canvas_type", &m_Graphics.canvas_type, EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL ) ); diff --git a/common/tool/design_block_control.cpp b/common/tool/design_block_control.cpp new file mode 100644 index 0000000000..5dcb80bea2 --- /dev/null +++ b/common/tool/design_block_control.cpp @@ -0,0 +1,218 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright The KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +#include <tool/actions.h> +#include <tool/library_editor_control.h> +#include <tool/design_block_control.h> +#include <widgets/design_block_pane.h> +#include <widgets/panel_design_block_chooser.h> +#include <dialog_design_block_properties.h> +#include <mail_type.h> +#include <kiway.h> + + +DESIGN_BLOCK_CONTROL::~DESIGN_BLOCK_CONTROL() +{ +} + +DESIGN_BLOCK_CONTROL::DESIGN_BLOCK_CONTROL( const std::string& aName ) : TOOL_INTERACTIVE( aName ) +{ +} + + +void DESIGN_BLOCK_CONTROL::Reset( RESET_REASON aReason ) +{ + m_frame = getEditFrame<EDA_DRAW_FRAME>(); +} + + +void DESIGN_BLOCK_CONTROL::AddContextMenuItems( CONDITIONAL_MENU* aMenu ) +{ + auto pinnedLib = + [this]( const SELECTION& aSel ) + { + // + LIB_TREE_NODE* current = getCurrentTreeNode(); + return current && current->m_Type == LIB_TREE_NODE::TYPE::LIBRARY + && current->m_Pinned; + }; + + auto unpinnedLib = + [this](const SELECTION& aSel ) + { + LIB_TREE_NODE* current = getCurrentTreeNode(); + return current && current->m_Type == LIB_TREE_NODE::TYPE::LIBRARY + && !current->m_Pinned; + }; + + auto isDesignBlock = + [this](const SELECTION& aSel ) + { + return this->selIsDesignBlock(aSel); + }; + + aMenu->AddItem( ACTIONS::pinLibrary, unpinnedLib, 1 ); + aMenu->AddItem( ACTIONS::unpinLibrary, pinnedLib, 1 ); + aMenu->AddItem( ACTIONS::newLibrary, !isDesignBlock, 1 ); + aMenu->AddSeparator( 2 ); + + aMenu->AddSeparator( 400 ); + aMenu->AddItem( ACTIONS::hideLibraryTree, SELECTION_CONDITIONS::ShowAlways, 400 ); +} + + +int DESIGN_BLOCK_CONTROL::PinLibrary( const TOOL_EVENT& aEvent ) +{ + LIB_TREE_NODE* current = getCurrentTreeNode(); + + if( current && !current->m_Pinned ) + { + m_frame->Prj().PinLibrary( current->m_LibId.GetLibNickname(), PROJECT::LIB_TYPE_T::DESIGN_BLOCK_LIB ); + current->m_Pinned = true; + getDesignBlockPane()->RefreshLibs(); + notifyOtherFrames(); + + return 0; + } + + return -1; +} + + +int DESIGN_BLOCK_CONTROL::UnpinLibrary( const TOOL_EVENT& aEvent ) +{ + LIB_TREE_NODE* current = getCurrentTreeNode(); + + if( current && current->m_Pinned ) + { + m_frame->Prj().UnpinLibrary( current->m_LibId.GetLibNickname(), PROJECT::LIB_TYPE_T::DESIGN_BLOCK_LIB ); + current->m_Pinned = false; + getDesignBlockPane()->RefreshLibs(); + notifyOtherFrames(); + + return 0; + } + + return -1; +} + + +int DESIGN_BLOCK_CONTROL::NewLibrary( const TOOL_EVENT& aEvent ) +{ + if( getDesignBlockPane()->CreateNewDesignBlockLibrary() != wxEmptyString ) + { + notifyOtherFrames(); + return 0; + } + + return -1; +} + + +int DESIGN_BLOCK_CONTROL::DeleteDesignBlock( const TOOL_EVENT& aEvent ) +{ + LIB_TREE_NODE* current = getCurrentTreeNode(); + + if( !current ) + return -1; + + if( getDesignBlockPane()->DeleteDesignBlockFromLibrary( current->m_LibId, true ) ) + { + notifyOtherFrames(); + return 0; + } + + return -1; +} + + +int DESIGN_BLOCK_CONTROL::EditDesignBlockProperties( const TOOL_EVENT& aEvent ) +{ + LIB_TREE_NODE* current = getCurrentTreeNode(); + + if( !current ) + return -1; + + if( getDesignBlockPane()->EditDesignBlockProperties( current->m_LibId ) ) + { + notifyOtherFrames(); + return 0; + } + + return -1; +} + + +int DESIGN_BLOCK_CONTROL::HideLibraryTree( const TOOL_EVENT& aEvent ) +{ + m_frame->ToggleLibraryTree(); + return 0; +} + + +bool DESIGN_BLOCK_CONTROL::selIsInLibrary( const SELECTION& aSel ) +{ + LIB_TREE_NODE* current = getCurrentTreeNode(); + return current + && ( current->m_Type == LIB_TREE_NODE::TYPE::LIBRARY + || current->m_Type == LIB_TREE_NODE::TYPE::ITEM ); +} + + +bool DESIGN_BLOCK_CONTROL::selIsDesignBlock( const SELECTION& aSel ) +{ + LIB_TREE_NODE* current = getCurrentTreeNode(); + return current && current->m_Type == LIB_TREE_NODE::TYPE::ITEM; +} + + +void DESIGN_BLOCK_CONTROL::setTransitions() +{ + Go( &DESIGN_BLOCK_CONTROL::PinLibrary, ACTIONS::pinLibrary.MakeEvent() ); + Go( &DESIGN_BLOCK_CONTROL::UnpinLibrary, ACTIONS::unpinLibrary.MakeEvent() ); + Go( &DESIGN_BLOCK_CONTROL::NewLibrary, ACTIONS::newLibrary.MakeEvent() ); + Go( &DESIGN_BLOCK_CONTROL::HideLibraryTree, ACTIONS::hideLibraryTree.MakeEvent() ); +} + + +LIB_ID DESIGN_BLOCK_CONTROL::getSelectedLibId() +{ + getDesignBlockPane()->GetSelectedLibId(); + + return LIB_ID(); +} + + +LIB_TREE_NODE* DESIGN_BLOCK_CONTROL::getCurrentTreeNode() +{ + LIB_TREE* libTree = getDesignBlockPane()->GetDesignBlockPanel()->GetLibTree(); + return libTree ? libTree->GetCurrentTreeNode() : nullptr; +} + + +void DESIGN_BLOCK_CONTROL::notifyOtherFrames() +{ + std::string payload = ""; + + for( FRAME_T frame : m_framesToNotify ) + m_frame->Kiway().ExpressMail( frame, MAIL_RELOAD_LIB, payload ); +} diff --git a/common/tool/design_block_control.h b/common/tool/design_block_control.h new file mode 100644 index 0000000000..61339df080 --- /dev/null +++ b/common/tool/design_block_control.h @@ -0,0 +1,77 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright The KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + +#ifndef DESIGN_BLOCK_CONTROL_H +#define DESIGN_BLOCK_CONTROL_H + +#include <eda_draw_frame.h> +#include <tool/tool_interactive.h> + +class DESIGN_BLOCK_PANE; + +/** + * Handle schematic design block actions in the schematic editor. + */ +class DESIGN_BLOCK_CONTROL : public TOOL_INTERACTIVE, public wxEvtHandler +{ +public: + DESIGN_BLOCK_CONTROL( const std::string& aName ); + virtual ~DESIGN_BLOCK_CONTROL(); + + /// @copydoc TOOL_INTERACTIVE::Reset() + void Reset( RESET_REASON aReason ) override; + + void AddContextMenuItems( CONDITIONAL_MENU* aMenu ); + + int PinLibrary( const TOOL_EVENT& aEvent ); + int UnpinLibrary( const TOOL_EVENT& aEvent ); + + int NewLibrary( const TOOL_EVENT& aEvent ); + int DeleteLibrary( const TOOL_EVENT& aEvent ); + + int DeleteDesignBlock( const TOOL_EVENT& aEvent ); + int EditDesignBlockProperties( const TOOL_EVENT& aEvent ); + + int HideLibraryTree( const TOOL_EVENT& aEvent ); + +protected: + bool selIsInLibrary( const SELECTION& aSel ); + bool selIsDesignBlock( const SELECTION& aSel ); + + LIB_ID getSelectedLibId(); + ///< Set up handlers for various events. + void setTransitions() override; + + virtual DESIGN_BLOCK_PANE* getDesignBlockPane() = 0; + LIB_TREE_NODE* getCurrentTreeNode(); + + /// Notify other frames that the design block lib table has changed + std::vector<FRAME_T> m_framesToNotify; + void notifyOtherFrames(); + + EDA_DRAW_FRAME* m_frame = nullptr; +}; + + +#endif diff --git a/common/widgets/design_block_pane.cpp b/common/widgets/design_block_pane.cpp new file mode 100644 index 0000000000..2441924a4c --- /dev/null +++ b/common/widgets/design_block_pane.cpp @@ -0,0 +1,512 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright The KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <design_block.h> +#include <design_block_lib_table.h> +#include <paths.h> +#include <env_paths.h> +#include <pgm_base.h> +#include <common.h> +#include <kidialog.h> +#include <widgets/design_block_pane.h> +#include <dialog_design_block_properties.h> +#include <widgets/panel_design_block_chooser.h> +#include <kiface_base.h> +#include <core/kicad_algo.h> +#include <template_fieldnames.h> +#include <wx/button.h> +#include <wx/checkbox.h> +#include <wx/sizer.h> +#include <wx/choicdlg.h> +#include <wx/msgdlg.h> +#include <wx/textdlg.h> +#include <confirm.h> +#include <wildcards_and_files_ext.h> +#include <tool/tool_manager.h> + +DESIGN_BLOCK_PANE::DESIGN_BLOCK_PANE( EDA_DRAW_FRAME* aParent, const LIB_ID* aPreselect, + std::vector<LIB_ID>& aHistoryList ) : WX_PANEL( aParent ), m_frame( aParent ) +{ + m_frame->Bind( wxEVT_AUI_PANE_CLOSE, &DESIGN_BLOCK_PANE::OnClosed, this ); + m_frame->Bind( EDA_LANG_CHANGED, &DESIGN_BLOCK_PANE::OnLanguageChanged, this ); +} + + +DESIGN_BLOCK_PANE::~DESIGN_BLOCK_PANE() +{ + m_frame->Unbind( wxEVT_AUI_PANE_CLOSE, &DESIGN_BLOCK_PANE::OnClosed, this ); + m_frame->Unbind( EDA_LANG_CHANGED, &DESIGN_BLOCK_PANE::OnLanguageChanged, this ); +} + + +void DESIGN_BLOCK_PANE::OnLanguageChanged( wxCommandEvent& aEvent ) +{ + if( m_chooserPanel ) + m_chooserPanel->ShowChangedLanguage(); + + setLabelsAndTooltips(); + + aEvent.Skip(); +} + + +void DESIGN_BLOCK_PANE::OnClosed( wxAuiManagerEvent& aEvent ) +{ + if( APP_SETTINGS_BASE* cfg = m_frame->config() ) + { + if( IsShownOnScreen() ) // Ensure the panel is shown when trying to save its size + m_frame->SaveSettings( cfg ); + } + + aEvent.Skip(); +} + + +void DESIGN_BLOCK_PANE::SaveSettings() +{ + m_chooserPanel->SaveSettings(); +} + + +LIB_ID DESIGN_BLOCK_PANE::GetSelectedLibId( int* aUnit ) const +{ + return m_chooserPanel->GetSelectedLibId( aUnit ); +} + + +void DESIGN_BLOCK_PANE::SelectLibId( const LIB_ID& aLibId ) +{ + m_chooserPanel->SelectLibId( aLibId ); +} + + +void DESIGN_BLOCK_PANE::RefreshLibs() +{ + m_chooserPanel->RefreshLibs(); +} + + +DESIGN_BLOCK* DESIGN_BLOCK_PANE::GetDesignBlock( const LIB_ID& aLibId, bool aUseCacheLib, bool aShowErrorMsg ) +{ + DESIGN_BLOCK_LIB_TABLE* prjLibs = m_frame->Prj().DesignBlockLibs(); + + wxCHECK_MSG( prjLibs, nullptr, wxS( "Invalid design block library table." ) ); + + DESIGN_BLOCK* designBlock = nullptr; + + try + { + designBlock = prjLibs->DesignBlockLoadWithOptionalNickname( aLibId, true ); + } + catch( const IO_ERROR& ioe ) + { + if( aShowErrorMsg ) + { + wxString msg = wxString::Format( _( "Error loading design block %s from library '%s'." ), + aLibId.GetLibItemName().wx_str(), aLibId.GetLibNickname().wx_str() ); + DisplayErrorMessage( m_frame, msg, ioe.What() ); + } + } + + return designBlock; +} + + +DESIGN_BLOCK* DESIGN_BLOCK_PANE::GetSelectedDesignBlock( bool aUseCacheLib, bool aShowErrorMsg ) +{ + if( !GetSelectedLibId().IsValid() ) + return nullptr; + + return GetDesignBlock( GetSelectedLibId(), aUseCacheLib, aShowErrorMsg ); +} + + +wxString DESIGN_BLOCK_PANE::CreateNewDesignBlockLibrary( const wxString& aLibName, const wxString& aProposedName ) +{ + return createNewDesignBlockLibrary( aLibName, aProposedName, selectDesignBlockLibTable() ); +} + + +wxString DESIGN_BLOCK_PANE::createNewDesignBlockLibrary( const wxString& aLibName, const wxString& aProposedName, + DESIGN_BLOCK_LIB_TABLE* aTable ) +{ + if( aTable == nullptr ) + return wxEmptyString; + + wxFileName fn; + bool doAdd = false; + bool isGlobal = ( aTable == &DESIGN_BLOCK_LIB_TABLE::GetGlobalLibTable() ); + wxString initialPath = aProposedName; + + if( initialPath.IsEmpty() ) + initialPath = isGlobal ? PATHS::GetDefaultUserDesignBlocksPath() : m_frame->Prj().GetProjectPath(); + + if( aLibName.IsEmpty() ) + { + fn = initialPath; + + if( !m_frame->LibraryFileBrowser( false, fn, FILEEXT::KiCadDesignBlockLibPathWildcard(), + FILEEXT::KiCadDesignBlockLibPathExtension, false, isGlobal, initialPath ) ) + { + return wxEmptyString; + } + + doAdd = true; + } + else + { + fn = EnsureFileExtension( aLibName, FILEEXT::KiCadDesignBlockLibPathExtension ); + + if( !fn.IsAbsolute() ) + { + fn.SetName( aLibName ); + fn.MakeAbsolute( initialPath ); + } + } + + // We can save libs only using DESIGN_BLOCK_IO_MGR::KICAD_SEXP format (.pretty libraries) + DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T piType = DESIGN_BLOCK_IO_MGR::KICAD_SEXP; + wxString libPath = fn.GetFullPath(); + + try + { + IO_RELEASER<DESIGN_BLOCK_IO> pi( DESIGN_BLOCK_IO_MGR::FindPlugin( piType ) ); + + bool writable = false; + bool exists = false; + + try + { + writable = pi->IsLibraryWritable( libPath ); + exists = fn.Exists(); + } + catch( const IO_ERROR& ) + { + // best efforts.... + } + + if( exists ) + { + if( !writable ) + { + wxString msg = wxString::Format( _( "Library %s is read only." ), libPath ); + m_frame->ShowInfoBarError( msg ); + return wxEmptyString; + } + else + { + wxString msg = wxString::Format( _( "Library %s already exists." ), libPath ); + KIDIALOG dlg( m_frame, msg, _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING ); + dlg.SetOKLabel( _( "Overwrite" ) ); + dlg.DoNotShowCheckbox( __FILE__, __LINE__ ); + + if( dlg.ShowModal() == wxID_CANCEL ) + return wxEmptyString; + + pi->DeleteLibrary( libPath ); + } + } + + pi->CreateLibrary( libPath ); + } + catch( const IO_ERROR& ioe ) + { + DisplayError( m_frame, ioe.What() ); + return wxEmptyString; + } + + if( doAdd ) + AddDesignBlockLibrary( libPath, aTable ); + + return libPath; +} + + +bool DESIGN_BLOCK_PANE::AddDesignBlockLibrary( const wxString& aFilename, DESIGN_BLOCK_LIB_TABLE* aTable ) +{ + if( aTable == nullptr ) + aTable = selectDesignBlockLibTable(); + + if( aTable == nullptr ) + return wxEmptyString; + + bool isGlobal = ( aTable == &DESIGN_BLOCK_LIB_TABLE::GetGlobalLibTable() ); + + wxFileName fn( aFilename ); + + if( aFilename.IsEmpty() ) + { + if( !m_frame->LibraryFileBrowser( true, fn, FILEEXT::KiCadDesignBlockLibPathWildcard(), + FILEEXT::KiCadDesignBlockLibPathExtension, true, isGlobal, + PATHS::GetDefaultUserDesignBlocksPath() ) ) + { + return false; + } + } + + wxString libPath = fn.GetFullPath(); + wxString libName = fn.GetName(); + + if( libName.IsEmpty() ) + return false; + + // Open a dialog to ask for a description + wxString description = wxGetTextFromUser( _( "Enter a description for the library:" ), _( "Library Description" ), + wxEmptyString, m_frame ); + + DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T lib_type = DESIGN_BLOCK_IO_MGR::GuessPluginTypeFromLibPath( libPath ); + + if( lib_type == DESIGN_BLOCK_IO_MGR::FILE_TYPE_NONE ) + lib_type = DESIGN_BLOCK_IO_MGR::KICAD_SEXP; + + wxString type = DESIGN_BLOCK_IO_MGR::ShowType( lib_type ); + + // KiCad lib is our default guess. So it might not have the .kicad_blocks extension + // In this case, the extension is part of the library name + if( lib_type == DESIGN_BLOCK_IO_MGR::KICAD_SEXP && fn.GetExt() != FILEEXT::KiCadDesignBlockLibPathExtension ) + libName = fn.GetFullName(); + + // try to use path normalized to an environmental variable or project path + wxString normalizedPath = NormalizePath( libPath, &Pgm().GetLocalEnvVariables(), &m_frame->Prj() ); + + try + { + DESIGN_BLOCK_LIB_TABLE_ROW* row = + new DESIGN_BLOCK_LIB_TABLE_ROW( libName, normalizedPath, type, wxEmptyString, description ); + aTable->InsertRow( row ); + + if( isGlobal ) + DESIGN_BLOCK_LIB_TABLE::GetGlobalLibTable().Save( DESIGN_BLOCK_LIB_TABLE::GetGlobalTableFileName() ); + else + m_frame->Prj().DesignBlockLibs()->Save( m_frame->Prj().DesignBlockLibTblName() ); + } + catch( const IO_ERROR& ioe ) + { + DisplayError( m_frame, ioe.What() ); + return false; + } + + LIB_ID libID( libName, wxEmptyString ); + RefreshLibs(); + SelectLibId( libID ); + + return true; +} + + +bool DESIGN_BLOCK_PANE::DeleteDesignBlockLibrary( const wxString& aLibName, bool aConfirm ) +{ + if( aLibName.IsEmpty() ) + { + DisplayErrorMessage( m_frame, _( "Please select a library to delete." ) ); + return false; + } + + if( !m_frame->Prj().DesignBlockLibs()->IsDesignBlockLibWritable( aLibName ) ) + { + wxString msg = wxString::Format( _( "Library '%s' is read only." ), aLibName ); + m_frame->ShowInfoBarError( msg ); + return false; + } + + // Confirmation + wxString msg = wxString::Format( _( "Delete design block library '%s' from disk? This will " + "delete all design blocks within the library." ), + aLibName.GetData() ); + + if( aConfirm && !IsOK( m_frame, msg ) ) + return false; + + try + { + m_frame->Prj().DesignBlockLibs()->DesignBlockLibDelete( aLibName ); + } + catch( const IO_ERROR& ioe ) + { + DisplayError( m_frame, ioe.What() ); + return false; + } + + msg.Printf( _( "Design block library '%s' deleted" ), aLibName.GetData() ); + m_frame->SetStatusText( msg ); + + RefreshLibs(); + + return true; +} + + +bool DESIGN_BLOCK_PANE::DeleteDesignBlockFromLibrary( const LIB_ID& aLibId, bool aConfirm ) +{ + if( !aLibId.IsValid() ) + return false; + + wxString libname = aLibId.GetLibNickname(); + wxString dbname = aLibId.GetLibItemName(); + + if( !m_frame->Prj().DesignBlockLibs()->IsDesignBlockLibWritable( libname ) ) + { + wxString msg = wxString::Format( _( "Library '%s' is read only." ), libname ); + m_frame->ShowInfoBarError( msg ); + return false; + } + + // Confirmation + wxString msg = wxString::Format( _( "Delete design block '%s' in library '%s' from disk?" ), + dbname.GetData(), libname.GetData() ); + + if( aConfirm && !IsOK( m_frame, msg ) ) + return false; + + try + { + m_frame->Prj().DesignBlockLibs()->DesignBlockDelete( libname, dbname ); + } + catch( const IO_ERROR& ioe ) + { + DisplayError( m_frame, ioe.What() ); + return false; + } + + msg.Printf( _( "Design block '%s' deleted from library '%s'" ), dbname.GetData(), libname.GetData() ); + + m_frame->SetStatusText( msg ); + + RefreshLibs(); + + return true; +} + + +bool DESIGN_BLOCK_PANE::EditDesignBlockProperties( const LIB_ID& aLibId ) +{ + if( !aLibId.IsValid() ) + return false; + + wxString libname = aLibId.GetLibNickname(); + wxString dbname = aLibId.GetLibItemName(); + + if( !m_frame->Prj().DesignBlockLibs()->IsDesignBlockLibWritable( libname ) ) + { + wxString msg = wxString::Format( _( "Library '%s' is read only." ), libname ); + m_frame->ShowInfoBarError( msg ); + return false; + } + + DESIGN_BLOCK* designBlock = GetDesignBlock( aLibId, true, true ); + + if( !designBlock ) + return false; + + wxString originalName = designBlock->GetLibId().GetLibItemName(); + DIALOG_DESIGN_BLOCK_PROPERTIES dlg( m_frame, designBlock ); + + if( dlg.ShowModal() != wxID_OK ) + return false; + + wxString newName = designBlock->GetLibId().GetLibItemName(); + + try + { + if( originalName != newName ) + { + if( m_frame->Prj().DesignBlockLibs()->DesignBlockExists( libname, newName ) ) + if( !checkOverwrite( m_frame, libname, newName ) ) + return false; + + m_frame->Prj().DesignBlockLibs()->DesignBlockSave( libname, designBlock ); + m_frame->Prj().DesignBlockLibs()->DesignBlockDelete( libname, originalName ); + } + else + m_frame->Prj().DesignBlockLibs()->DesignBlockSave( libname, designBlock ); + } + catch( const IO_ERROR& ioe ) + { + DisplayError( m_frame, ioe.What() ); + return false; + } + + RefreshLibs(); + SelectLibId( designBlock->GetLibId() ); + + return true; +} + + +bool DESIGN_BLOCK_PANE::checkOverwrite( wxWindow* aFrame, wxString& libname, wxString& newName ) +{ + wxString msg = wxString::Format( _( "Design block '%s' already exists in library '%s'." ), + newName.GetData(), libname.GetData() ); + + if( OKOrCancelDialog( aFrame, _( "Confirmation" ), msg, _( "Overwrite existing design block?" ), _( "Overwrite" ) ) + != wxID_OK ) + { + return false; + } + + return true; +} + + +DESIGN_BLOCK_LIB_TABLE* DESIGN_BLOCK_PANE::selectDesignBlockLibTable( bool aOptional ) +{ + // If no project is loaded, always work with the global table + if( m_frame->Prj().IsNullProject() ) + { + DESIGN_BLOCK_LIB_TABLE* ret = &DESIGN_BLOCK_LIB_TABLE::GetGlobalLibTable(); + + if( aOptional ) + { + wxMessageDialog dlg( m_frame, _( "Add the library to the global library table?" ), + _( "Add To Global Library Table" ), wxYES_NO ); + + if( dlg.ShowModal() != wxID_OK ) + ret = nullptr; + } + + return ret; + } + + wxArrayString libTableNames; + libTableNames.Add( _( "Global" ) ); + libTableNames.Add( _( "Project" ) ); + + wxSingleChoiceDialog dlg( m_frame, _( "Choose the Library Table to add the library to:" ), + _( "Add To Library Table" ), libTableNames ); + + if( aOptional ) + { + dlg.FindWindow( wxID_CANCEL )->SetLabel( _( "Skip" ) ); + dlg.FindWindow( wxID_OK )->SetLabel( _( "Add" ) ); + } + + if( dlg.ShowModal() != wxID_OK ) + return nullptr; + + switch( dlg.GetSelection() ) + { + case 0: return &DESIGN_BLOCK_LIB_TABLE::GetGlobalLibTable(); + case 1: return m_frame->Prj().DesignBlockLibs(); + default: return nullptr; + } +} diff --git a/common/widgets/design_block_pane.h b/common/widgets/design_block_pane.h new file mode 100644 index 0000000000..452bc65160 --- /dev/null +++ b/common/widgets/design_block_pane.h @@ -0,0 +1,109 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright The KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +#ifndef DESIGN_BLOCK_PANE_H +#define DESIGN_BLOCK_PANE_H + +#include <design_block_tree_model_adapter.h> +#include <widgets/html_window.h> +#include <widgets/wx_panel.h> +#include <wx/checkbox.h> +#include <wx/filedlgcustomize.h> +#include <eda_draw_frame.h> + + +class DESIGN_BLOCK; +class PANEL_DESIGN_BLOCK_CHOOSER; + + +class DESIGN_BLOCK_PANE : public WX_PANEL +{ +public: + DESIGN_BLOCK_PANE( EDA_DRAW_FRAME* aParent, const LIB_ID* aPreselect, std::vector<LIB_ID>& aHistoryList ); + + ~DESIGN_BLOCK_PANE() override; + + void SaveSettings(); + + LIB_ID GetSelectedLibId( int* aUnit = nullptr ) const; + void SelectLibId( const LIB_ID& aLibId ); + + /** + * Load design block from design block library table. + * + * @param aLibId is the design block library identifier to load. + * @param aUseCacheLib set to true to fall back to cache library if design block is not found in + * design block library table. + * @param aShowErrorMessage set to true to show any error messages. + * @return The design block found in the library or NULL if the design block was not found. + */ + DESIGN_BLOCK* GetDesignBlock( const LIB_ID& aLibId, bool aUseCacheLib, bool aShowErrorMsg ); + DESIGN_BLOCK* GetSelectedDesignBlock( bool aUseCacheLib, bool aShowErrorMsg ); + + void RefreshLibs(); + + /** + * If a library name is given, creates a new design block library in the project folder + * with the given name. If no library name is given it prompts user for a library path, + * then creates a new design block library at that location. + * If library exists, user is warned about that, and is given a chance + * to abort the new creation, and in that case existing library is first deleted. + * + * @param aProposedName is the initial path and filename shown in the file chooser dialog. + * @return The newly created library path if library was successfully created, else + * wxEmptyString because user aborted or error. + */ + wxString CreateNewDesignBlockLibrary( const wxString& aLibName = wxEmptyString, + const wxString& aProposedName = wxEmptyString ); + + /** + * Add an existing library to either the global or project library table. + * + * @param aFileName the library to add; a file open dialog will be displayed if empty. + * @return true if successfully added. + */ + bool AddDesignBlockLibrary( const wxString& aFilename, DESIGN_BLOCK_LIB_TABLE* aTable ); + + bool DeleteDesignBlockLibrary( const wxString& aLibName, bool aConfirm ); + + bool DeleteDesignBlockFromLibrary( const LIB_ID& aLibId, bool aConfirm ); + + bool EditDesignBlockProperties( const LIB_ID& aLibId ); + + PANEL_DESIGN_BLOCK_CHOOSER* GetDesignBlockPanel() const { return m_chooserPanel; } + +protected: + virtual void setLabelsAndTooltips() = 0; + + virtual void OnLanguageChanged( wxCommandEvent& aEvent ); + void OnClosed( wxAuiManagerEvent& aEvent ); + + EDA_DRAW_FRAME* m_frame = nullptr; + PANEL_DESIGN_BLOCK_CHOOSER* m_chooserPanel = nullptr; + +private: + bool checkOverwrite( wxWindow* aFrame, wxString& libname, wxString& newName ); + DESIGN_BLOCK_LIB_TABLE* selectDesignBlockLibTable( bool aOptional = false ); + wxString createNewDesignBlockLibrary( const wxString& aLibName, const wxString& aProposedName, + DESIGN_BLOCK_LIB_TABLE* aTable ); +}; +#endif diff --git a/common/widgets/design_block_preview_widget.h b/common/widgets/design_block_preview_widget.h new file mode 100644 index 0000000000..46b2e52141 --- /dev/null +++ b/common/widgets/design_block_preview_widget.h @@ -0,0 +1,66 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright The KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef DESIGN_BLOCK_PREVIEW_WIDGET_H +#define DESIGN_BLOCK_PREVIEW_WIDGET_H + +#include <wx/panel.h> +#include <kiway.h> +#include <gal_display_options_common.h> +#include <class_draw_panel_gal.h> + + +class LIB_ID; +class DESIGN_BLOCK; +class SCHEMATIC; +class SCH_SHEET; +class wxStaticText; +class wxSizer; + + +class DESIGN_BLOCK_PREVIEW_WIDGET : public wxPanel +{ +public: + /** + * Construct a design block preview widget. + * + * @param aParent - parent window + */ + DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent ) : wxPanel( aParent ) {} + ~DESIGN_BLOCK_PREVIEW_WIDGET() = default; + + + /** + * Set the contents of the status label and display it. + */ + virtual void SetStatusText( const wxString& aText ) = 0; + + /** + * Set the currently displayed design block. + */ + virtual void DisplayDesignBlock( DESIGN_BLOCK* aDesignBlock ) = 0; + +protected: + void onSize( wxSizeEvent& aEvent ); + + void fitOnDrawArea(); // set the view scale to fit the item on screen and center +}; + + +#endif // DESIGN_BLOCK_PREVIEW_WIDGET_H diff --git a/eeschema/widgets/panel_design_block_chooser.cpp b/common/widgets/panel_design_block_chooser.cpp similarity index 85% rename from eeschema/widgets/panel_design_block_chooser.cpp rename to common/widgets/panel_design_block_chooser.cpp index 517a8c4f11..b2649ed3f0 100644 --- a/eeschema/widgets/panel_design_block_chooser.cpp +++ b/common/widgets/panel_design_block_chooser.cpp @@ -23,15 +23,18 @@ #include <pgm_base.h> #include <design_block.h> +#include <design_block_pane.h> #include <design_block_lib_table.h> #include <panel_design_block_chooser.h> #include <design_block_preview_widget.h> #include <kiface_base.h> -#include <sch_edit_frame.h> +#include <kiway_holder.h> +#include <eda_draw_frame.h> #include <widgets/lib_tree.h> #include <settings/settings_manager.h> #include <project/project_file.h> #include <dialogs/html_message_box.h> +#include <settings/app_settings.h> #include <string_utils.h> #include <wx/log.h> #include <wx/panel.h> @@ -46,24 +49,24 @@ wxString PANEL_DESIGN_BLOCK_CHOOSER::g_designBlockSearchString; -PANEL_DESIGN_BLOCK_CHOOSER::PANEL_DESIGN_BLOCK_CHOOSER( SCH_EDIT_FRAME* aFrame, wxWindow* aParent, +PANEL_DESIGN_BLOCK_CHOOSER::PANEL_DESIGN_BLOCK_CHOOSER( EDA_DRAW_FRAME* aFrame, DESIGN_BLOCK_PANE* aParent, std::vector<LIB_ID>& aHistoryList, - std::function<void()> aSelectHandler ) : + std::function<void()> aSelectHandler, + TOOL_INTERACTIVE* aContextMenuTool ) : + wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize ), m_dbl_click_timer( nullptr ), m_open_libs_timer( nullptr ), m_vsplitter( nullptr ), m_tree( nullptr ), m_preview( nullptr ), + m_parent( aParent ), m_frame( aFrame ), m_selectHandler( std::move( aSelectHandler ) ), m_historyList( aHistoryList ) { DESIGN_BLOCK_LIB_TABLE* libs = m_frame->Prj().DesignBlockLibs(); - // Make sure settings are loaded before we start running multi-threaded design block loaders - Pgm().GetSettingsManager().GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" ); - // Load design block files: WX_PROGRESS_REPORTER* progressReporter = new WX_PROGRESS_REPORTER( aParent, _( "Loading Design Block Libraries" ), 1 ); @@ -79,8 +82,8 @@ PANEL_DESIGN_BLOCK_CHOOSER::PANEL_DESIGN_BLOCK_CHOOSER( SCH_EDIT_FRAME* aFrame, if( DESIGN_BLOCK_LIB_TABLE::GetGlobalList().GetErrorCount() ) displayErrors( aFrame ); - m_adapter = DESIGN_BLOCK_TREE_MODEL_ADAPTER::Create( m_frame, libs, - m_frame->eeconfig()->m_DesignBlockChooserPanel.tree ); + m_adapter = DESIGN_BLOCK_TREE_MODEL_ADAPTER::Create( + m_frame, libs, m_frame->config()->m_DesignBlockChooserPanel.tree, aContextMenuTool ); // ------------------------------------------------------------------------------------- // Construct the actual panel @@ -99,27 +102,21 @@ PANEL_DESIGN_BLOCK_CHOOSER::PANEL_DESIGN_BLOCK_CHOOSER( SCH_EDIT_FRAME* aFrame, wxBoxSizer* treeSizer = new wxBoxSizer( wxVERTICAL ); treePanel->SetSizer( treeSizer ); - wxPanel* detailsPanel = new wxPanel( m_vsplitter ); - wxBoxSizer* detailsSizer = new wxBoxSizer( wxVERTICAL ); - detailsPanel->SetSizer( detailsSizer ); + m_detailsPanel = new wxPanel( m_vsplitter ); + m_detailsSizer = new wxBoxSizer( wxVERTICAL ); + m_detailsPanel->SetSizer( m_detailsSizer ); - // Use the same draw engine type as the one used in parent frame m_frame - EDA_DRAW_PANEL_GAL::GAL_TYPE canvasType = m_frame->GetCanvas()->GetBackend(); - m_preview = new DESIGN_BLOCK_PREVIEW_WIDGET( detailsPanel, false, canvasType ); - - detailsSizer->Add( m_preview, 1, wxEXPAND, 5 ); - detailsPanel->Layout(); - detailsSizer->Fit( detailsPanel ); + m_detailsPanel->Layout(); + m_detailsSizer->Fit( m_detailsPanel ); m_vsplitter->SetSashGravity( 0.5 ); m_vsplitter->SetMinimumPaneSize( 20 ); - m_vsplitter->SplitHorizontally( treePanel, detailsPanel ); + m_vsplitter->SplitHorizontally( treePanel, m_detailsPanel ); sizer->Add( m_vsplitter, 1, wxEXPAND, 5 ); - m_tree = new LIB_TREE( treePanel, wxT( "design_blocks" ), libs, m_adapter, - LIB_TREE::FLAGS::ALL_WIDGETS, nullptr ); + m_tree = new LIB_TREE( treePanel, wxT( "design_blocks" ), libs, m_adapter, LIB_TREE::FLAGS::ALL_WIDGETS, nullptr ); treeSizer->Add( m_tree, 1, wxEXPAND, 5 ); treePanel->Layout(); @@ -137,10 +134,8 @@ PANEL_DESIGN_BLOCK_CHOOSER::PANEL_DESIGN_BLOCK_CHOOSER( SCH_EDIT_FRAME* aFrame, Layout(); - Bind( wxEVT_TIMER, &PANEL_DESIGN_BLOCK_CHOOSER::onCloseTimer, this, - m_dbl_click_timer->GetId() ); - Bind( wxEVT_TIMER, &PANEL_DESIGN_BLOCK_CHOOSER::onOpenLibsTimer, this, - m_open_libs_timer->GetId() ); + Bind( wxEVT_TIMER, &PANEL_DESIGN_BLOCK_CHOOSER::onCloseTimer, this, m_dbl_click_timer->GetId() ); + Bind( wxEVT_TIMER, &PANEL_DESIGN_BLOCK_CHOOSER::onOpenLibsTimer, this, m_open_libs_timer->GetId() ); Bind( EVT_LIBITEM_CHOSEN, &PANEL_DESIGN_BLOCK_CHOOSER::onDesignBlockChosen, this ); Bind( wxEVT_CHAR_HOOK, &PANEL_DESIGN_BLOCK_CHOOSER::OnChar, this ); @@ -170,17 +165,14 @@ void PANEL_DESIGN_BLOCK_CHOOSER::SaveSettings() { g_designBlockSearchString = m_tree->GetSearchString(); - if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) ) + if( APP_SETTINGS_BASE* cfg = m_frame->config() ) { // Save any changes to column widths, etc. m_adapter->SaveSettings(); cfg->m_DesignBlockChooserPanel.width = GetParent()->GetSize().x; cfg->m_DesignBlockChooserPanel.height = GetParent()->GetSize().y; - - if( m_vsplitter ) - cfg->m_DesignBlockChooserPanel.sash_pos_v = m_vsplitter->GetSashPosition(); - + cfg->m_DesignBlockChooserPanel.sash_pos_v = m_vsplitter->GetSashPosition(); cfg->m_DesignBlockChooserPanel.sort_mode = m_tree->GetSortMode(); } } @@ -193,6 +185,14 @@ void PANEL_DESIGN_BLOCK_CHOOSER::ShowChangedLanguage() } +void PANEL_DESIGN_BLOCK_CHOOSER::SetPreviewWidget( DESIGN_BLOCK_PREVIEW_WIDGET* aPreview ) +{ + m_preview = aPreview; + m_detailsSizer->Add( m_preview, 1, wxEXPAND, 5 ); + Layout(); +} + + void PANEL_DESIGN_BLOCK_CHOOSER::OnChar( wxKeyEvent& aEvent ) { if( aEvent.GetKeyCode() == WXK_ESCAPE ) @@ -202,8 +202,7 @@ void PANEL_DESIGN_BLOCK_CHOOSER::OnChar( wxKeyEvent& aEvent ) if( wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( eventSource ) ) { // First escape cancels search string value - if( textCtrl->GetValue() == m_tree->GetSearchString() - && !m_tree->GetSearchString().IsEmpty() ) + if( textCtrl->GetValue() == m_tree->GetSearchString() && !m_tree->GetSearchString().IsEmpty() ) { m_tree->SetSearchString( wxEmptyString ); return; @@ -219,7 +218,7 @@ void PANEL_DESIGN_BLOCK_CHOOSER::OnChar( wxKeyEvent& aEvent ) void PANEL_DESIGN_BLOCK_CHOOSER::FinishSetup() { - if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) ) + if( APP_SETTINGS_BASE* cfg = m_frame->config() ) { auto horizPixelsFromDU = [&]( int x ) -> int @@ -228,7 +227,7 @@ void PANEL_DESIGN_BLOCK_CHOOSER::FinishSetup() return GetParent()->ConvertDialogToPixels( sz ).x; }; - EESCHEMA_SETTINGS::PANEL_DESIGN_BLOCK_CHOOSER& panelCfg = cfg->m_DesignBlockChooserPanel; + APP_SETTINGS_BASE::PANEL_DESIGN_BLOCK_CHOOSER& panelCfg = cfg->m_DesignBlockChooserPanel; int w = panelCfg.width > 40 ? panelCfg.width : horizPixelsFromDU( 440 ); int h = panelCfg.height > 40 ? panelCfg.height : horizPixelsFromDU( 340 ); @@ -259,8 +258,7 @@ void PANEL_DESIGN_BLOCK_CHOOSER::RefreshLibs( bool aProgress ) // if a selected item is removed during the sync m_tree->Unselect(); - DESIGN_BLOCK_TREE_MODEL_ADAPTER* adapter = - static_cast<DESIGN_BLOCK_TREE_MODEL_ADAPTER*>( m_adapter.get() ); + DESIGN_BLOCK_TREE_MODEL_ADAPTER* adapter = static_cast<DESIGN_BLOCK_TREE_MODEL_ADAPTER*>( m_adapter.get() ); // Clear all existing libraries then re-add adapter->ClearLibraries(); @@ -273,8 +271,7 @@ void PANEL_DESIGN_BLOCK_CHOOSER::RefreshLibs( bool aProgress ) if( aProgress ) { WX_PROGRESS_REPORTER progressReporter( this, _( "Updating Design Block Libraries" ), 2 ); - DESIGN_BLOCK_LIB_TABLE::GetGlobalList().ReadDesignBlockFiles( fpTable, nullptr, - &progressReporter ); + DESIGN_BLOCK_LIB_TABLE::GetGlobalList().ReadDesignBlockFiles( fpTable, nullptr, &progressReporter ); progressReporter.Show( false ); } else @@ -336,7 +333,7 @@ void PANEL_DESIGN_BLOCK_CHOOSER::onCloseTimer( wxTimerEvent& aEvent ) void PANEL_DESIGN_BLOCK_CHOOSER::onOpenLibsTimer( wxTimerEvent& aEvent ) { - if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) ) + if( APP_SETTINGS_BASE* cfg = m_frame->config() ) m_adapter->OpenLibs( cfg->m_LibTree.open_libs ); // Bind this now se we don't spam the event queue with EVT_LIBITEM_SELECTED events during @@ -348,7 +345,7 @@ void PANEL_DESIGN_BLOCK_CHOOSER::onOpenLibsTimer( wxTimerEvent& aEvent ) void PANEL_DESIGN_BLOCK_CHOOSER::onDesignBlockSelected( wxCommandEvent& aEvent ) { if( GetSelectedLibId().IsValid() ) - m_preview->DisplayDesignBlock( m_frame->GetDesignBlock( GetSelectedLibId() ) ); + m_preview->DisplayDesignBlock( m_parent->GetDesignBlock( GetSelectedLibId(), true, true ) ); } @@ -405,8 +402,8 @@ void PANEL_DESIGN_BLOCK_CHOOSER::rebuildHistoryNode() for( const LIB_ID& lib : m_historyList ) { - LIB_TREE_ITEM* fp_info = DESIGN_BLOCK_LIB_TABLE::GetGlobalList().GetDesignBlockInfo( - lib.GetLibNickname(), lib.GetLibItemName() ); + LIB_TREE_ITEM* fp_info = DESIGN_BLOCK_LIB_TABLE::GetGlobalList().GetDesignBlockInfo( lib.GetLibNickname(), + lib.GetLibItemName() ); // this can be null, for example, if the design block has been deleted from a library. if( fp_info != nullptr ) @@ -427,7 +424,7 @@ void PANEL_DESIGN_BLOCK_CHOOSER::displayErrors( wxTopLevelWindow* aWindow ) HTML_MESSAGE_BOX dlg( aWindow, _( "Load Error" ) ); - dlg.MessageSet( _( "Errors were encountered loading footprints:" ) ); + dlg.MessageSet( _( "Errors were encountered loading design blocks:" ) ); wxString msg; diff --git a/eeschema/widgets/panel_design_block_chooser.h b/common/widgets/panel_design_block_chooser.h similarity index 74% rename from eeschema/widgets/panel_design_block_chooser.h rename to common/widgets/panel_design_block_chooser.h index bad61c0efd..0984657653 100644 --- a/eeschema/widgets/panel_design_block_chooser.h +++ b/common/widgets/panel_design_block_chooser.h @@ -31,24 +31,27 @@ class wxPanel; class wxTimer; class wxSplitterWindow; -class SCH_EDIT_FRAME; +class EDA_DRAW_FRAME; +class DESIGN_BLOCK_PANE; class DESIGN_BLOCK_PREVIEW_WIDGET; class PANEL_DESIGN_BLOCK_CHOOSER : public wxPanel { public: -/** - * Create dialog to choose design_block. - * - * @param aFrame the parent frame (usually a SCH_EDIT_FRAME or DESIGN_BLOCK_CHOOSER_FRAME) - * @param aParent the parent window (usually a DIALOG_SHIM or DESIGN_BLOCK_CHOOSER_FRAME) - * @param aAcceptHandler a handler to be called on double-click of a footprint - * @param aEscapeHandler a handler to be called on <ESC> - */ - PANEL_DESIGN_BLOCK_CHOOSER( SCH_EDIT_FRAME* aFrame, wxWindow* aParent, + /** + * Panel for using design blocks. + * + * @param aFrame the parent frame (usually a SCH_EDIT_FRAME or PCB_EDIT_FRAME) + * @param aParent the parent design block pane + * @param aAcceptHandler a handler to be called on double-click of a footprint + * @param aContextMenuTool the tool that will be used to provide an appropriate context menu + * for the design block actions available in that frame + */ + PANEL_DESIGN_BLOCK_CHOOSER( EDA_DRAW_FRAME* aFrame, DESIGN_BLOCK_PANE* aParent, std::vector<LIB_ID>& aHistoryList, - std::function<void()> aSelectHandler ); + std::function<void()> aSelectHandler, + TOOL_INTERACTIVE* aContextMenuTool ); ~PANEL_DESIGN_BLOCK_CHOOSER(); @@ -56,7 +59,9 @@ public: void OnChar( wxKeyEvent& aEvent ); - void FinishSetup(); + wxPanel* GetDetailsPanel() { return m_detailsPanel; } + void SetPreviewWidget( DESIGN_BLOCK_PREVIEW_WIDGET* aPreview ); + void FinishSetup(); void SetPreselect( const LIB_ID& aPreselect ); @@ -105,16 +110,19 @@ protected: protected: static wxString g_designBlockSearchString; - wxTimer* m_dbl_click_timer; - wxTimer* m_open_libs_timer; - wxSplitterWindow* m_vsplitter; + wxTimer* m_dbl_click_timer; + wxTimer* m_open_libs_timer; + wxSplitterWindow* m_vsplitter; + wxPanel* m_detailsPanel; + wxBoxSizer* m_detailsSizer; wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER> m_adapter; - LIB_TREE* m_tree; - DESIGN_BLOCK_PREVIEW_WIDGET* m_preview; + LIB_TREE* m_tree; + DESIGN_BLOCK_PREVIEW_WIDGET* m_preview; - SCH_EDIT_FRAME* m_frame; + DESIGN_BLOCK_PANE* m_parent; + EDA_DRAW_FRAME* m_frame; std::function<void()> m_selectHandler; std::vector<LIB_ID> m_historyList; diff --git a/eeschema/CMakeLists.txt b/eeschema/CMakeLists.txt index 8b38c7e229..702adaac81 100644 --- a/eeschema/CMakeLists.txt +++ b/eeschema/CMakeLists.txt @@ -99,8 +99,6 @@ set( EESCHEMA_DLGS dialogs/dialog_change_symbols_base.cpp dialogs/dialog_database_lib_settings_base.cpp dialogs/dialog_database_lib_settings.cpp - dialogs/dialog_design_block_properties_base.cpp - dialogs/dialog_design_block_properties.cpp dialogs/dialog_edit_symbols_libid.cpp dialogs/dialog_edit_symbols_libid_base.cpp dialogs/dialog_eeschema_page_settings.cpp @@ -278,16 +276,15 @@ set( EESCHEMA_SIM_SRCS ) set( EESCHEMA_WIDGETS - widgets/design_block_pane.cpp - widgets/design_block_preview_widget.cpp widgets/hierarchy_pane.cpp - widgets/panel_design_block_chooser.cpp widgets/panel_sch_selection_filter_base.cpp widgets/panel_sch_selection_filter.cpp widgets/panel_symbol_chooser.cpp widgets/pin_shape_combobox.cpp widgets/pin_type_combobox.cpp widgets/symbol_diff_widget.cpp + widgets/sch_design_block_pane.cpp + widgets/sch_design_block_preview_widget.cpp widgets/sch_properties_panel.cpp widgets/sch_search_pane.cpp widgets/search_handlers.cpp @@ -357,8 +354,7 @@ set( EESCHEMA_SRCS bus-wire-junction.cpp connection_graph.cpp cross-probing.cpp - design_block_tree_model_adapter.cpp - design_block_utils.cpp + sch_design_block_utils.cpp eeschema_config.cpp eeschema_helpers.cpp eeschema_jobs_handler.cpp diff --git a/eeschema/cross-probing.cpp b/eeschema/cross-probing.cpp index 7426e85761..16a0a6035c 100644 --- a/eeschema/cross-probing.cpp +++ b/eeschema/cross-probing.cpp @@ -41,7 +41,7 @@ #include <tools/sch_actions.h> #include <tools/sch_editor_control.h> #include <advanced_config.h> -#include <widgets/design_block_pane.h> +#include <widgets/sch_design_block_pane.h> #include <wx/log.h> SCH_ITEM* SCH_EDITOR_CONTROL::FindSymbolAndItem( const wxString* aPath, const wxString* aReference, diff --git a/eeschema/eeschema_config.cpp b/eeschema/eeschema_config.cpp index fb78c74d0e..eb2a030ab0 100644 --- a/eeschema/eeschema_config.cpp +++ b/eeschema/eeschema_config.cpp @@ -34,8 +34,8 @@ #include <sch_edit_frame.h> #include <sch_painter.h> #include <schematic.h> -#include <widgets/design_block_pane.h> #include <widgets/hierarchy_pane.h> +#include <widgets/sch_design_block_pane.h> #include <widgets/sch_search_pane.h> #include <widgets/panel_sch_selection_filter.h> #include <widgets/properties_panel.h> diff --git a/eeschema/eeschema_settings.cpp b/eeschema/eeschema_settings.cpp index 0472c79eee..1a0efdbe76 100644 --- a/eeschema/eeschema_settings.cpp +++ b/eeschema/eeschema_settings.cpp @@ -658,59 +658,6 @@ EESCHEMA_SETTINGS::EESCHEMA_SETTINGS() : m_params.emplace_back( new PARAM<bool>( "symbol_chooser.place_all_units", &m_SymChooserPanel.place_all_units, true ) ); - m_params.emplace_back( new PARAM<int>( "design_block_chooser.sash_pos_h", - &m_DesignBlockChooserPanel.sash_pos_h, -1 ) ); - - m_params.emplace_back( new PARAM<int>( "design_block_chooser.sash_pos_v", - &m_DesignBlockChooserPanel.sash_pos_v, -1 ) ); - - m_params.emplace_back( new PARAM<int>( "design_block_chooser.width", - &m_DesignBlockChooserPanel.width, -1 ) ); - - m_params.emplace_back( new PARAM<int>( "design_block_chooser.height", - &m_DesignBlockChooserPanel.height, -1 ) ); - - m_params.emplace_back( new PARAM<int>( "design_block_chooser.sort_mode", - &m_DesignBlockChooserPanel.sort_mode, 0 ) ); - - m_params.emplace_back( new PARAM<bool>( "design_block_chooser.repeated_placement", - &m_DesignBlockChooserPanel.repeated_placement, false ) ); - - m_params.emplace_back( new PARAM<bool>( "design_block_chooser.place_as_sheet", - &m_DesignBlockChooserPanel.place_as_sheet, false ) ); - - m_params.emplace_back( new PARAM<bool>( "design_block_chooser.keep_annotations", - &m_DesignBlockChooserPanel.keep_annotations, false ) ); - - m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( - "design_block_chooser.lib_tree.column_widths", - [&]() -> nlohmann::json - { - nlohmann::json ret = {}; - - for( const auto& [name, width] : m_DesignBlockChooserPanel.tree.column_widths ) - ret[std::string( name.ToUTF8() )] = width; - - return ret; - }, - [&]( const nlohmann::json& aJson ) - { - if( !aJson.is_object() ) - return; - - m_DesignBlockChooserPanel.tree.column_widths.clear(); - - for( const auto& entry : aJson.items() ) - { - if( !entry.value().is_number_integer() ) - continue; - - m_DesignBlockChooserPanel.tree.column_widths[ entry.key() ] = - entry.value().get<int>(); - } - }, - {} ) ); - m_params.emplace_back( new PARAM<bool>( "import_graphics.interactive_placement", &m_ImportGraphics.interactive_placement, true ) ); diff --git a/eeschema/eeschema_settings.h b/eeschema/eeschema_settings.h index a44278f315..fe5d964950 100644 --- a/eeschema/eeschema_settings.h +++ b/eeschema/eeschema_settings.h @@ -285,21 +285,6 @@ public: bool place_all_units; }; - struct PANEL_DESIGN_BLOCK_CHOOSER - { - int sash_pos_h; - int sash_pos_v; - int width; - int height; - int sort_mode; - bool repeated_placement; - bool place_as_sheet; - bool keep_annotations; - - // For saving tree columns and widths - LIB_TREE tree; - }; - struct DIALOG_IMPORT_GRAPHICS { bool interactive_placement; @@ -390,8 +375,6 @@ public: PANEL_SYM_CHOOSER m_SymChooserPanel; - PANEL_DESIGN_BLOCK_CHOOSER m_DesignBlockChooserPanel; - DIALOG_IMPORT_GRAPHICS m_ImportGraphics; SELECTION m_Selection; diff --git a/eeschema/menubar.cpp b/eeschema/menubar.cpp index a0c39508e8..874da3889a 100644 --- a/eeschema/menubar.cpp +++ b/eeschema/menubar.cpp @@ -187,9 +187,7 @@ void SCH_EDIT_FRAME::doReCreateMenuBar() if( ADVANCED_CFG::GetCfg().m_IncrementalConnectivity ) showHidePanels->Add( SCH_ACTIONS::showNetNavigator, ACTION_MENU::CHECK ); - if( ADVANCED_CFG::GetCfg().m_EnableDesignBlocks ) - showHidePanels->Add( SCH_ACTIONS::showDesignBlockPanel, ACTION_MENU::CHECK, - _( "Design Blocks" ) ); + showHidePanels->Add( SCH_ACTIONS::showDesignBlockPanel, ACTION_MENU::CHECK, _( "Design Blocks" ) ); viewMenu->Add( showHidePanels ); @@ -334,8 +332,7 @@ void SCH_EDIT_FRAME::doReCreateMenuBar() prefsMenu->Add( ACTIONS::configurePaths ); prefsMenu->Add( ACTIONS::showSymbolLibTable ); - if( ADVANCED_CFG::GetCfg().m_EnableDesignBlocks ) - prefsMenu->Add( ACTIONS::showDesignBlockLibTable ); + prefsMenu->Add( ACTIONS::showDesignBlockLibTable ); prefsMenu->Add( ACTIONS::openPreferences ); prefsMenu->AppendSeparator(); diff --git a/eeschema/sch_design_block_utils.cpp b/eeschema/sch_design_block_utils.cpp new file mode 100644 index 0000000000..8db9803fce --- /dev/null +++ b/eeschema/sch_design_block_utils.cpp @@ -0,0 +1,446 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright The KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <pgm_base.h> +#include <kiway.h> +#include <design_block.h> +#include <design_block_lib_table.h> +#include <sch_design_block_pane.h> +#include <sch_edit_frame.h> +#include <wx/choicdlg.h> +#include <wx/msgdlg.h> +#include <wx/textdlg.h> +#include <wildcards_and_files_ext.h> +#include <paths.h> +#include <env_paths.h> +#include <common.h> +#include <kidialog.h> +#include <confirm.h> +#include <tool/tool_manager.h> +#include <sch_selection_tool.h> +#include <dialogs/dialog_design_block_properties.h> +#include <nlohmann/json.hpp> + +bool checkOverwriteDb( wxWindow* aFrame, wxString& libname, wxString& newName ) +{ + wxString msg = wxString::Format( _( "Design block '%s' already exists in library '%s'." ), newName.GetData(), + libname.GetData() ); + + if( OKOrCancelDialog( aFrame, _( "Confirmation" ), msg, _( "Overwrite existing design block?" ), _( "Overwrite" ) ) + != wxID_OK ) + { + return false; + } + + return true; +} + + +bool checkOverwriteDbSchematic( wxWindow* aFrame, const LIB_ID& aLibId ) +{ + wxString msg = + wxString::Format( _( "Design block '%s' already has a schematic." ), aLibId.GetUniStringLibItemName() ); + + if( OKOrCancelDialog( aFrame, _( "Confirmation" ), msg, _( "Overwrite existing schematic?" ), _( "Overwrite" ) ) + != wxID_OK ) + { + return false; + } + + return true; +} + + +bool SCH_EDIT_FRAME::SaveSheetAsDesignBlock( const wxString& aLibraryName, SCH_SHEET_PATH& aSheetPath ) +{ + // Make sure the user has selected a library to save into + if( m_designBlocksPane->GetSelectedLibId().GetLibNickname().empty() ) + { + DisplayErrorMessage( this, _( "Please select a library to save the design block to." ) ); + return false; + } + + // Just block all attempts to create design blocks with nested sheets at this point + std::vector<SCH_ITEM*> sheets; + aSheetPath.LastScreen()->GetSheets( &sheets ); + + if( !sheets.empty() ) + { + DisplayErrorMessage( this, _( "Design blocks with nested sheets are not supported." ) ); + return false; + } + + DESIGN_BLOCK blk; + wxFileName fn = wxFileNameFromPath( aSheetPath.Last()->GetName() ); + + blk.SetLibId( LIB_ID( aLibraryName, fn.GetName() ) ); + + // Copy all fields from the sheet to the design block + std::vector<SCH_FIELD>& shFields = aSheetPath.Last()->GetFields(); + nlohmann::ordered_map<wxString, wxString> dbFields; + + for( SCH_FIELD& f : shFields ) + { + if( f.GetId() == FIELD_T::SHEET_NAME || f.GetId() == FIELD_T::SHEET_FILENAME ) + continue; + + dbFields[f.GetCanonicalName()] = f.GetText(); + } + + blk.SetFields( dbFields ); + + DIALOG_DESIGN_BLOCK_PROPERTIES dlg( this, &blk ); + + if( dlg.ShowModal() != wxID_OK ) + return false; + + wxString libName = blk.GetLibId().GetLibNickname(); + wxString newName = blk.GetLibId().GetLibItemName(); + + if( Prj().DesignBlockLibs()->DesignBlockExists( libName, newName ) && !checkOverwriteDb( this, libName, newName ) ) + { + return false; + } + + // Save a temporary copy of the schematic file, as the plugin is just going to move it + wxString tempFile = wxFileName::CreateTempFileName( "design_block" ); + if( !saveSchematicFile( aSheetPath.Last(), tempFile ) ) + { + DisplayErrorMessage( this, _( "Error saving temporary schematic file to create design block." ) ); + wxRemoveFile( tempFile ); + return false; + } + + blk.SetSchematicFile( tempFile ); + + bool success = false; + + try + { + success = Prj().DesignBlockLibs()->DesignBlockSave( aLibraryName, &blk ) == DESIGN_BLOCK_LIB_TABLE::SAVE_OK; + } + catch( const IO_ERROR& ioe ) + { + DisplayError( this, ioe.What() ); + } + + // Clean up the temporary file + wxRemoveFile( tempFile ); + + m_designBlocksPane->RefreshLibs(); + m_designBlocksPane->SelectLibId( blk.GetLibId() ); + + return success; +} + + +bool SCH_EDIT_FRAME::SaveSheetToDesignBlock( const LIB_ID& aLibId, SCH_SHEET_PATH& aSheetPath ) +{ + // Make sure the user has selected a library to save into + if( !Prj().DesignBlockLibs()->DesignBlockExists( aLibId.GetLibNickname(), aLibId.GetLibItemName() ) ) + { + DisplayErrorMessage( this, _( "Please select a design block to save the schematic to." ) ); + return false; + } + + // Just block all attempts to create design blocks with nested sheets at this point + std::vector<SCH_ITEM*> sheets; + aSheetPath.LastScreen()->GetSheets( &sheets ); + + if( !sheets.empty() ) + { + DisplayErrorMessage( this, _( "Design blocks with nested sheets are not supported." ) ); + return false; + } + + DESIGN_BLOCK* blk = nullptr; + + try + { + blk = Prj().DesignBlockLibs()->DesignBlockLoad( aLibId.GetLibNickname(), aLibId.GetLibItemName() ); + } + catch( const IO_ERROR& ioe ) + { + DisplayError( this, ioe.What() ); + return false; + } + + if( !blk->GetSchematicFile().IsEmpty() && !checkOverwriteDbSchematic( this, aLibId ) ) + { + return false; + } + + // Copy all fields from the sheet to the design block. + // Note: this will overwrite any existing fields in the design block, but + // will leave extra fields not in this source sheet alone. + std::vector<SCH_FIELD>& shFields = aSheetPath.Last()->GetFields(); + nlohmann::ordered_map<wxString, wxString> dbFields = blk->GetFields(); + + for( SCH_FIELD& f : shFields ) + { + if( f.GetId() == FIELD_T::SHEET_NAME || f.GetId() == FIELD_T::SHEET_FILENAME ) + continue; + + dbFields[f.GetCanonicalName()] = f.GetText(); + } + + blk->SetFields( dbFields ); + + DIALOG_DESIGN_BLOCK_PROPERTIES dlg( this, blk, true ); + + if( dlg.ShowModal() != wxID_OK ) + return false; + + // Save a temporary copy of the schematic file, as the plugin is just going to move it + wxString tempFile = wxFileName::CreateTempFileName( "design_block" ); + if( !saveSchematicFile( aSheetPath.Last(), tempFile ) ) + { + DisplayErrorMessage( this, _( "Error saving temporary schematic file to create design block." ) ); + wxRemoveFile( tempFile ); + return false; + } + + blk->SetSchematicFile( tempFile ); + + bool success = false; + + try + { + success = Prj().DesignBlockLibs()->DesignBlockSave( aLibId.GetLibNickname(), blk ) + == DESIGN_BLOCK_LIB_TABLE::SAVE_OK; + } + catch( const IO_ERROR& ioe ) + { + DisplayError( this, ioe.What() ); + } + + // Clean up the temporary file + wxRemoveFile( tempFile ); + + m_designBlocksPane->RefreshLibs(); + m_designBlocksPane->SelectLibId( blk->GetLibId() ); + + return success; +} + + +bool SCH_EDIT_FRAME::SaveSelectionAsDesignBlock( const wxString& aLibraryName ) +{ + // Get all selected items + SCH_SELECTION selection = m_toolManager->GetTool<SCH_SELECTION_TOOL>()->GetSelection(); + + if( selection.Empty() ) + { + DisplayErrorMessage( this, _( "Please select some items to save as a design block." ) ); + return false; + } + + // Make sure the user has selected a library to save into + if( m_designBlocksPane->GetSelectedLibId().GetLibNickname().empty() ) + { + DisplayErrorMessage( this, _( "Please select a library to save the design block to." ) ); + return false; + } + + // Just block all attempts to create design blocks with nested sheets at this point + if( selection.HasType( SCH_SHEET_T ) ) + { + if( selection.Size() == 1 ) + { + SCH_SHEET* sheet = static_cast<SCH_SHEET*>( selection.Front() ); + SCH_SHEET_PATH curPath = GetCurrentSheet(); + + curPath.push_back( sheet ); + SaveSheetAsDesignBlock( aLibraryName, curPath ); + } + else + DisplayErrorMessage( this, _( "Design blocks with nested sheets are not supported." ) ); + + return false; + } + + DESIGN_BLOCK blk; + wxFileName fn = wxFileNameFromPath( GetScreen()->GetFileName() ); + + blk.SetLibId( LIB_ID( aLibraryName, fn.GetName() ) ); + + DIALOG_DESIGN_BLOCK_PROPERTIES dlg( this, &blk ); + + if( dlg.ShowModal() != wxID_OK ) + return false; + + wxString libName = blk.GetLibId().GetLibNickname(); + wxString newName = blk.GetLibId().GetLibItemName(); + + if( Prj().DesignBlockLibs()->DesignBlockExists( libName, newName ) && !checkOverwriteDb( this, libName, newName ) ) + { + return false; + } + + // Create a temporary screen + SCH_SCREEN* tempScreen = new SCH_SCREEN( m_schematic ); + + // Copy the selected items to the temporary screen + for( EDA_ITEM* item : selection ) + { + EDA_ITEM* copy = item->Clone(); + tempScreen->Append( static_cast<SCH_ITEM*>( copy ) ); + } + + // Create a sheet for the temporary screen + SCH_SHEET* tempSheet = new SCH_SHEET( m_schematic ); + tempSheet->SetScreen( tempScreen ); + + // Save a temporary copy of the schematic file, as the plugin is just going to move it + wxString tempFile = wxFileName::CreateTempFileName( "design_block" ); + if( !saveSchematicFile( tempSheet, tempFile ) ) + { + DisplayErrorMessage( this, _( "Error saving temporary schematic file to create design block." ) ); + wxRemoveFile( tempFile ); + return false; + } + + blk.SetSchematicFile( tempFile ); + + bool success = false; + + try + { + success = Prj().DesignBlockLibs()->DesignBlockSave( aLibraryName, &blk ) == DESIGN_BLOCK_LIB_TABLE::SAVE_OK; + } + catch( const IO_ERROR& ioe ) + { + DisplayError( this, ioe.What() ); + } + + // Clean up the temporaries + wxRemoveFile( tempFile ); + // This will also delete the screen + delete tempSheet; + + m_designBlocksPane->RefreshLibs(); + m_designBlocksPane->SelectLibId( blk.GetLibId() ); + + return success; +} + + +bool SCH_EDIT_FRAME::SaveSelectionToDesignBlock( const LIB_ID& aLibId ) +{ + // Get all selected items + SCH_SELECTION selection = m_toolManager->GetTool<SCH_SELECTION_TOOL>()->GetSelection(); + + if( selection.Empty() ) + { + DisplayErrorMessage( this, _( "Please select some items to save as a design block." ) ); + return false; + } + + // Make sure the user has selected a library to save into + if( !Prj().DesignBlockLibs()->DesignBlockExists( aLibId.GetLibNickname(), aLibId.GetLibItemName() ) ) + { + DisplayErrorMessage( this, _( "Please select a design block to save the schematic to." ) ); + return false; + } + + // Just block all attempts to create design blocks with nested sheets at this point + if( selection.HasType( SCH_SHEET_T ) ) + { + if( selection.Size() == 1 ) + { + SCH_SHEET* sheet = static_cast<SCH_SHEET*>( selection.Front() ); + SCH_SHEET_PATH curPath = GetCurrentSheet(); + + curPath.push_back( sheet ); + SaveSheetToDesignBlock( aLibId, curPath ); + } + else + DisplayErrorMessage( this, _( "Design blocks with nested sheets are not supported." ) ); + + return false; + } + + DESIGN_BLOCK* blk = nullptr; + + try + { + blk = Prj().DesignBlockLibs()->DesignBlockLoad( aLibId.GetLibNickname(), aLibId.GetLibItemName() ); + } + catch( const IO_ERROR& ioe ) + { + DisplayError( this, ioe.What() ); + return false; + } + + if( !blk->GetSchematicFile().IsEmpty() && !checkOverwriteDbSchematic( this, aLibId ) ) + { + return false; + } + + // Create a temporary screen + SCH_SCREEN* tempScreen = new SCH_SCREEN( m_schematic ); + + // Copy the selected items to the temporary screen + for( EDA_ITEM* item : selection ) + { + EDA_ITEM* copy = item->Clone(); + tempScreen->Append( static_cast<SCH_ITEM*>( copy ) ); + } + + // Create a sheet for the temporary screen + SCH_SHEET* tempSheet = new SCH_SHEET( m_schematic ); + tempSheet->SetScreen( tempScreen ); + + // Save a temporary copy of the schematic file, as the plugin is just going to move it + wxString tempFile = wxFileName::CreateTempFileName( "design_block" ); + if( !saveSchematicFile( tempSheet, tempFile ) ) + { + DisplayErrorMessage( this, _( "Error saving temporary schematic file to create design block." ) ); + wxRemoveFile( tempFile ); + return false; + } + + blk->SetSchematicFile( tempFile ); + + bool success = false; + + try + { + success = Prj().DesignBlockLibs()->DesignBlockSave( aLibId.GetLibNickname(), blk ) + == DESIGN_BLOCK_LIB_TABLE::SAVE_OK; + } + catch( const IO_ERROR& ioe ) + { + DisplayError( this, ioe.What() ); + } + + // Clean up the temporaries + wxRemoveFile( tempFile ); + // This will also delete the screen + delete tempSheet; + + m_designBlocksPane->RefreshLibs(); + m_designBlocksPane->SelectLibId( blk->GetLibId() ); + + return success; +} diff --git a/eeschema/sch_edit_frame.cpp b/eeschema/sch_edit_frame.cpp index 5e2cdf1e2b..aeded91d31 100644 --- a/eeschema/sch_edit_frame.cpp +++ b/eeschema/sch_edit_frame.cpp @@ -34,7 +34,7 @@ #include <dialogs/dialog_schematic_find.h> #include <dialogs/dialog_book_reporter.h> #include <dialogs/dialog_symbol_fields_table.h> -#include <widgets/design_block_pane.h> +#include <widgets/sch_design_block_pane.h> #include <eeschema_id.h> #include <executable_names.h> #include <gal/graphics_abstraction_layer.h> @@ -214,7 +214,7 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : m_propertiesPanel->SetSplitterProportion( eeconfig()->m_AuiPanels.properties_splitter ); m_selectionFilterPanel = new PANEL_SCH_SELECTION_FILTER( this ); - m_designBlocksPane = new DESIGN_BLOCK_PANE( this, nullptr, m_designBlockHistoryList ); + m_designBlocksPane = new SCH_DESIGN_BLOCK_PANE( this, nullptr, m_designBlockHistoryList ); m_auimgr.SetManagedWindow( this ); diff --git a/eeschema/sch_edit_frame.h b/eeschema/sch_edit_frame.h index f90a934326..a59147a890 100644 --- a/eeschema/sch_edit_frame.h +++ b/eeschema/sch_edit_frame.h @@ -56,8 +56,7 @@ class SCH_FIELD; class SCH_JUNCTION; class SCHEMATIC; class SCH_COMMIT; -class DESIGN_BLOCK; -class DESIGN_BLOCK_PANE; +class SCH_DESIGN_BLOCK_PANE; class DIALOG_BOOK_REPORTER; class DIALOG_ERC; class DIALOG_SYMBOL_FIELDS_TABLE; @@ -761,53 +760,15 @@ public: */ bool CreateArchiveLibrary( const wxString& aFileName ); - /** - * If a library name is given, creates a new design block library in the project folder - * with the given name. - * - * If no library name is given it prompts user for a library path, then creates a new design - * block library at that location. If library exists, user is warned about that, and is given - * a chance to abort the new creation, and in that case existing library is first deleted. - * - * @param aProposedName is the initial path and filename shown in the file chooser dialog. - * @return The newly created library path if library was successfully created, else - * wxEmptyString because user aborted or error. - */ - wxString CreateNewDesignBlockLibrary( const wxString& aLibName = wxEmptyString, - const wxString& aProposedName = wxEmptyString ); + bool SaveSheetAsDesignBlock( const wxString& aLibraryName, SCH_SHEET_PATH& aSheetPath ); - /** - * Add an existing library to either the global or project library table. - * - * @param aFileName the library to add; a file open dialog will be displayed if empty. - * @return true if successfully added. - */ - bool AddDesignBlockLibrary( const wxString& aFilename, DESIGN_BLOCK_LIB_TABLE* aTable ); + bool SaveSelectionAsDesignBlock( const wxString& aLibraryName ); - void SaveSheetAsDesignBlock( const wxString& aLibraryName, SCH_SHEET_PATH& aSheetPath ); + bool SaveSheetToDesignBlock( const LIB_ID& aLibId, SCH_SHEET_PATH& aSheetPath ); - void SaveSelectionAsDesignBlock( const wxString& aLibraryName ); + bool SaveSelectionToDesignBlock( const LIB_ID& aLibId ); - bool DeleteDesignBlockLibrary( const wxString& aLibName, bool aConfirm ); - - bool DeleteDesignBlockFromLibrary( const LIB_ID& aLibId, bool aConfirm ); - - bool EditDesignBlockProperties( const LIB_ID& aLibId ); - - - /** - * Load design block from design block library table. - * - * @param aLibId is the design block library identifier to load. - * @param aUseCacheLib set to true to fall back to cache library if design block is not found in - * design block library table. - * @param aShowErrorMessage set to true to show any error messages. - * @return The design block found in the library or NULL if the design block was not found. - */ - DESIGN_BLOCK* GetDesignBlock( const LIB_ID& aLibId, bool aUseCacheLib = false, - bool aShowErrorMsg = false ); - - DESIGN_BLOCK_PANE* GetDesignBlockPane() const { return m_designBlocksPane; } + SCH_DESIGN_BLOCK_PANE* GetDesignBlockPane() const { return m_designBlocksPane; } void SetNetListerCommand( const wxString& aCommand ) { m_netListerCommand = aCommand; } @@ -1001,20 +962,6 @@ protected: void onPluginAvailabilityChanged( wxCommandEvent& aEvt ); #endif - /** - * Prompt user to select global or project library tables. - * - * @return Pointer to library table selected or nullptr if none selected/canceled. - */ - DESIGN_BLOCK_LIB_TABLE* selectDesignBlockLibTable( bool aOptional = false ); - - /** - * Create a new library in the given table (presumed to be either the global or project - * library table). - */ - wxString createNewDesignBlockLibrary( const wxString& aLibName, const wxString& aProposedName, - DESIGN_BLOCK_LIB_TABLE* aTable ); - private: // Called when resizing the Hierarchy Navigator panel void OnResizeHierarchyNavigator( wxSizeEvent& aEvent ); @@ -1133,7 +1080,7 @@ private: std::vector<LIB_ID> m_designBlockHistoryList; - DESIGN_BLOCK_PANE* m_designBlocksPane; + SCH_DESIGN_BLOCK_PANE* m_designBlocksPane; #ifdef KICAD_IPC_API std::unique_ptr<API_HANDLER_SCH> m_apiHandler; diff --git a/eeschema/toolbars_sch_editor.cpp b/eeschema/toolbars_sch_editor.cpp index 8db9a17cb4..058e47dfc8 100644 --- a/eeschema/toolbars_sch_editor.cpp +++ b/eeschema/toolbars_sch_editor.cpp @@ -37,9 +37,9 @@ #include <tool/action_toolbar.h> #include <tools/sch_actions.h> #include <tools/sch_selection_tool.h> -#include <widgets/design_block_pane.h> #include <widgets/hierarchy_pane.h> #include <widgets/wx_aui_utils.h> +#include <widgets/sch_design_block_pane.h> #include <widgets/sch_properties_panel.h> #include <widgets/sch_search_pane.h> #include <toolbars_sch_editor.h> @@ -229,5 +229,4 @@ void SCH_EDIT_FRAME::configureToolbars() }; RegisterCustomToolbarControlFactory( ACTION_TOOLBAR_CONTROLS::ipcScripting, pluginControlFactory ); - -} \ No newline at end of file +} diff --git a/eeschema/tools/sch_actions.cpp b/eeschema/tools/sch_actions.cpp index f2f5276218..b0c9bedec6 100644 --- a/eeschema/tools/sch_actions.cpp +++ b/eeschema/tools/sch_actions.cpp @@ -31,6 +31,8 @@ #include <sch_line_wire_bus_tool.h> #include <tool/tool_action.h> +class DESIGN_BLOCK; + // Actions, being statically-defined, require specialized I18N handling. We continue to // use the _() macro so that string harvesting by the I18N framework doesn't have to be // specialized, but we don't translate on initialization and instead do it in the getters. @@ -164,6 +166,20 @@ TOOL_ACTION SCH_ACTIONS::saveSelectionAsDesignBlock( TOOL_ACTION_ARGS() .Tooltip( _( "Create a new design block from the current selection" ) ) .Icon( BITMAPS::new_component ) ); +TOOL_ACTION SCH_ACTIONS::saveSheetToDesignBlock( TOOL_ACTION_ARGS() + .Name( "eeschema.SchDesignBlockControl.saveSheetToDesignBlock" ) + .Scope( AS_GLOBAL ) + .FriendlyName( _( "Save Current Sheet to Design Block..." ) ) + .Tooltip( _( "Add current sheet to design block" ) ) + .Icon( BITMAPS::save ) ); + +TOOL_ACTION SCH_ACTIONS::saveSelectionToDesignBlock( TOOL_ACTION_ARGS() + .Name( "eeschema.SchDesignBlockControl.saveSelectionToDesignBlock" ) + .Scope( AS_GLOBAL ) + .FriendlyName( _( "Save Selection to Design Block..." ) ) + .Tooltip( _( "Add current selection to design block" ) ) + .Icon( BITMAPS::save ) ); + TOOL_ACTION SCH_ACTIONS::deleteDesignBlock( TOOL_ACTION_ARGS() .Name( "eeschema.SchDesignBlockControl.saveDeleteDesignBlock" ) .Scope( AS_GLOBAL ) diff --git a/eeschema/tools/sch_actions.h b/eeschema/tools/sch_actions.h index 78e149820d..670577cf8f 100644 --- a/eeschema/tools/sch_actions.h +++ b/eeschema/tools/sch_actions.h @@ -27,6 +27,7 @@ #include <tool/tool_action.h> #include <tool/actions.h> +class DESIGN_BLOCK; class SCH_SYMBOL; class TOOL_EVENT; class TOOL_MANAGER; @@ -202,6 +203,8 @@ public: static TOOL_ACTION showDesignBlockPanel; static TOOL_ACTION saveSheetAsDesignBlock; static TOOL_ACTION saveSelectionAsDesignBlock; + static TOOL_ACTION saveSheetToDesignBlock; + static TOOL_ACTION saveSelectionToDesignBlock; static TOOL_ACTION deleteDesignBlock; static TOOL_ACTION editDesignBlockProperties; diff --git a/eeschema/tools/sch_design_block_control.cpp b/eeschema/tools/sch_design_block_control.cpp index 77e044ffb1..1dc85e0119 100644 --- a/eeschema/tools/sch_design_block_control.cpp +++ b/eeschema/tools/sch_design_block_control.cpp @@ -20,112 +20,63 @@ * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include <tool/library_editor_control.h> -#include <sch_design_block_control.h> -#include <design_block_pane.h> -#include <panel_design_block_chooser.h> #include <dialog_design_block_properties.h> #include <sch_actions.h> +#include <sch_selection_tool.h> +#include <sch_design_block_control.h> +#include <sch_edit_frame.h> +#include <tool/tool_manager.h> +#include <widgets/sch_design_block_pane.h> +#include <widgets/panel_design_block_chooser.h> + + +SCH_DESIGN_BLOCK_CONTROL::~SCH_DESIGN_BLOCK_CONTROL() +{ +} + bool SCH_DESIGN_BLOCK_CONTROL::Init() { m_editFrame = getEditFrame<SCH_EDIT_FRAME>(); m_frame = m_editFrame; - m_selectionTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>(); - - auto pinnedLib = - [this]( const SELECTION& aSel ) - { - // - LIB_TREE_NODE* current = getCurrentTreeNode(); - return current && current->m_Type == LIB_TREE_NODE::TYPE::LIBRARY - && current->m_Pinned; - }; - auto unpinnedLib = - [this](const SELECTION& aSel ) - { - LIB_TREE_NODE* current = getCurrentTreeNode(); - return current && current->m_Type == LIB_TREE_NODE::TYPE::LIBRARY - && !current->m_Pinned; - }; + m_framesToNotify = { FRAME_PCB_EDITOR }; auto isInLibrary = [this](const SELECTION& aSel ) { - LIB_TREE_NODE* current = getCurrentTreeNode(); - return current - && ( current->m_Type == LIB_TREE_NODE::TYPE::LIBRARY - || current->m_Type == LIB_TREE_NODE::TYPE::ITEM ); + return this->selIsInLibrary(aSel); }; auto isDesignBlock = [this](const SELECTION& aSel ) { - LIB_TREE_NODE* current = getCurrentTreeNode(); - return current && current->m_Type == LIB_TREE_NODE::TYPE::ITEM; + return this->selIsDesignBlock(aSel); + }; + + auto hasSelection = + [this](const SELECTION& aSel ) + { + return !m_editFrame->GetCurrentSelection().Empty(); }; CONDITIONAL_MENU& ctxMenu = m_menu->GetMenu(); - - ctxMenu.AddItem( ACTIONS::pinLibrary, unpinnedLib, 1 ); - ctxMenu.AddItem( ACTIONS::unpinLibrary, pinnedLib, 1 ); - ctxMenu.AddItem( ACTIONS::newLibrary, !isDesignBlock, 1 ); - ctxMenu.AddSeparator( 1 ); + AddContextMenuItems( &ctxMenu ); ctxMenu.AddItem( SCH_ACTIONS::placeDesignBlock, isDesignBlock, 50 ); ctxMenu.AddSeparator( 50 ); ctxMenu.AddItem( SCH_ACTIONS::editDesignBlockProperties, isDesignBlock, 100 ); ctxMenu.AddItem( SCH_ACTIONS::saveSheetAsDesignBlock, isInLibrary, 100 ); - ctxMenu.AddItem( SCH_ACTIONS::saveSelectionAsDesignBlock, isInLibrary, 100 ); + ctxMenu.AddItem( SCH_ACTIONS::saveSelectionAsDesignBlock, isInLibrary && hasSelection, 100 ); + ctxMenu.AddItem( SCH_ACTIONS::saveSheetToDesignBlock, isDesignBlock, 100 ); + ctxMenu.AddItem( SCH_ACTIONS::saveSelectionToDesignBlock, isDesignBlock && hasSelection, 100 ); ctxMenu.AddItem( SCH_ACTIONS::deleteDesignBlock, isDesignBlock, 100 ); ctxMenu.AddSeparator( 100 ); - ctxMenu.AddItem( ACTIONS::hideLibraryTree, SELECTION_CONDITIONS::ShowAlways, 400 ); - return true; } -int SCH_DESIGN_BLOCK_CONTROL::PinLibrary( const TOOL_EVENT& aEvent ) -{ - LIB_TREE_NODE* current = getCurrentTreeNode(); - - if( current && !current->m_Pinned ) - { - m_frame->Prj().PinLibrary( current->m_LibId.GetLibNickname(), - PROJECT::LIB_TYPE_T::DESIGN_BLOCK_LIB ); - current->m_Pinned = true; - getDesignBlockPane()->RefreshLibs(); - } - - return 0; -} - - -int SCH_DESIGN_BLOCK_CONTROL::UnpinLibrary( const TOOL_EVENT& aEvent ) -{ - LIB_TREE_NODE* current = getCurrentTreeNode(); - - if( current && current->m_Pinned ) - { - m_frame->Prj().UnpinLibrary( current->m_LibId.GetLibNickname(), - PROJECT::LIB_TYPE_T::DESIGN_BLOCK_LIB ); - current->m_Pinned = false; - getDesignBlockPane()->RefreshLibs(); - } - - return 0; -} - - -int SCH_DESIGN_BLOCK_CONTROL::NewLibrary( const TOOL_EVENT& aEvent ) -{ - m_editFrame->CreateNewDesignBlockLibrary(); - return 0; -} - - int SCH_DESIGN_BLOCK_CONTROL::SaveSheetAsDesignBlock( const TOOL_EVENT& aEvent ) { LIB_TREE_NODE* current = getCurrentTreeNode(); @@ -133,8 +84,10 @@ int SCH_DESIGN_BLOCK_CONTROL::SaveSheetAsDesignBlock( const TOOL_EVENT& aEvent ) if( !current ) return -1; - m_editFrame->SaveSheetAsDesignBlock( current->m_LibId.GetLibNickname(), - m_editFrame->GetCurrentSheet() ); + if( !m_editFrame->SaveSheetAsDesignBlock( current->m_LibId.GetLibNickname(), m_editFrame->GetCurrentSheet() ) ) + return -1; + + notifyOtherFrames(); return 0; } @@ -147,67 +100,57 @@ int SCH_DESIGN_BLOCK_CONTROL::SaveSelectionAsDesignBlock( const TOOL_EVENT& aEve if( !current ) return -1; - m_editFrame->SaveSelectionAsDesignBlock( current->m_LibId.GetLibNickname() ); + if( !m_editFrame->SaveSelectionAsDesignBlock( current->m_LibId.GetLibNickname() ) ) + return -1; + + notifyOtherFrames(); return 0; } -int SCH_DESIGN_BLOCK_CONTROL::DeleteDesignBlock( const TOOL_EVENT& aEvent ) +int SCH_DESIGN_BLOCK_CONTROL::SaveSheetToDesignBlock( const TOOL_EVENT& aEvent ) { LIB_TREE_NODE* current = getCurrentTreeNode(); if( !current ) return -1; - m_editFrame->DeleteDesignBlockFromLibrary( current->m_LibId, true ); + if( !m_editFrame->SaveSheetToDesignBlock( current->m_LibId, m_editFrame->GetCurrentSheet() ) ) + return -1; + + notifyOtherFrames(); return 0; } -int SCH_DESIGN_BLOCK_CONTROL::EditDesignBlockProperties( const TOOL_EVENT& aEvent ) +int SCH_DESIGN_BLOCK_CONTROL::SaveSelectionToDesignBlock( const TOOL_EVENT& aEvent ) { LIB_TREE_NODE* current = getCurrentTreeNode(); if( !current ) return -1; - if( m_editFrame->EditDesignBlockProperties( current->m_LibId ) ) - return 0; + if( !m_editFrame->SaveSelectionToDesignBlock( current->m_LibId ) ) + return -1; - return -1; -} + notifyOtherFrames(); - -int SCH_DESIGN_BLOCK_CONTROL::HideLibraryTree( const TOOL_EVENT& aEvent ) -{ - m_editFrame->ToggleLibraryTree(); return 0; } void SCH_DESIGN_BLOCK_CONTROL::setTransitions() { - Go( &SCH_DESIGN_BLOCK_CONTROL::PinLibrary, ACTIONS::pinLibrary.MakeEvent() ); - Go( &SCH_DESIGN_BLOCK_CONTROL::UnpinLibrary, ACTIONS::unpinLibrary.MakeEvent() ); - - Go( &SCH_DESIGN_BLOCK_CONTROL::NewLibrary, ACTIONS::newLibrary.MakeEvent() ); + DESIGN_BLOCK_CONTROL::setTransitions(); Go( &SCH_DESIGN_BLOCK_CONTROL::SaveSheetAsDesignBlock, SCH_ACTIONS::saveSheetAsDesignBlock.MakeEvent() ); Go( &SCH_DESIGN_BLOCK_CONTROL::SaveSelectionAsDesignBlock, SCH_ACTIONS::saveSelectionAsDesignBlock.MakeEvent() ); + Go( &SCH_DESIGN_BLOCK_CONTROL::SaveSheetToDesignBlock, SCH_ACTIONS::saveSheetToDesignBlock.MakeEvent() ); + Go( &SCH_DESIGN_BLOCK_CONTROL::SaveSelectionToDesignBlock, SCH_ACTIONS::saveSelectionToDesignBlock.MakeEvent() ); Go( &SCH_DESIGN_BLOCK_CONTROL::DeleteDesignBlock, SCH_ACTIONS::deleteDesignBlock.MakeEvent() ); Go( &SCH_DESIGN_BLOCK_CONTROL::EditDesignBlockProperties, SCH_ACTIONS::editDesignBlockProperties.MakeEvent() ); - - Go( &SCH_DESIGN_BLOCK_CONTROL::HideLibraryTree, ACTIONS::hideLibraryTree.MakeEvent() ); -} - - -LIB_ID SCH_DESIGN_BLOCK_CONTROL::getSelectedLibId() -{ - m_editFrame->GetDesignBlockPane()->GetSelectedLibId(); - - return LIB_ID(); } @@ -215,10 +158,3 @@ DESIGN_BLOCK_PANE* SCH_DESIGN_BLOCK_CONTROL::getDesignBlockPane() { return m_editFrame->GetDesignBlockPane(); } - - -LIB_TREE_NODE* SCH_DESIGN_BLOCK_CONTROL::getCurrentTreeNode() -{ - LIB_TREE* libTree = getDesignBlockPane()->GetDesignBlockPanel()->GetLibTree(); - return libTree ? libTree->GetCurrentTreeNode() : nullptr; -} diff --git a/eeschema/tools/sch_design_block_control.h b/eeschema/tools/sch_design_block_control.h index cb71680a70..fa0235b2f2 100644 --- a/eeschema/tools/sch_design_block_control.h +++ b/eeschema/tools/sch_design_block_control.h @@ -25,42 +25,35 @@ #ifndef SCH_DESIGN_BLOCK_CONTROL_H #define SCH_DESIGN_BLOCK_CONTROL_H -#include <sch_base_frame.h> -#include <tools/sch_tool_base.h> +#include <tool/design_block_control.h> + +class DESIGN_BLOCK_PANE; +class SCH_EDIT_FRAME; /** - * Handle schematic design block actions in the schematic editor. + * Handle design block actions in the schematic editor. */ -class SCH_DESIGN_BLOCK_CONTROL : public wxEvtHandler, public SCH_TOOL_BASE<SCH_BASE_FRAME> +class SCH_DESIGN_BLOCK_CONTROL : public DESIGN_BLOCK_CONTROL { public: - SCH_DESIGN_BLOCK_CONTROL() : - SCH_TOOL_BASE<SCH_BASE_FRAME>( "eeschema.SchDesignBlockControl" ) - {} + SCH_DESIGN_BLOCK_CONTROL() : DESIGN_BLOCK_CONTROL( "eeschema.SchDesignBlockControl" ) {} + virtual ~SCH_DESIGN_BLOCK_CONTROL(); /// @copydoc TOOL_INTERACTIVE::Init() bool Init() override; - int PinLibrary( const TOOL_EVENT& aEvent ); - int UnpinLibrary( const TOOL_EVENT& aEvent ); - - int NewLibrary( const TOOL_EVENT& aEvent ); - int DeleteLibrary( const TOOL_EVENT& aEvent ); - int SaveSheetAsDesignBlock( const TOOL_EVENT& aEvent ); int SaveSelectionAsDesignBlock( const TOOL_EVENT& aEvent ); - int DeleteDesignBlock( const TOOL_EVENT& aEvent ); - int EditDesignBlockProperties( const TOOL_EVENT& aEvent ); - int HideLibraryTree( const TOOL_EVENT& aEvent ); + int SaveSheetToDesignBlock( const TOOL_EVENT& aEvent ); + int SaveSelectionToDesignBlock( const TOOL_EVENT& aEvent ); private: LIB_ID getSelectedLibId(); ///< Set up handlers for various events. void setTransitions() override; - DESIGN_BLOCK_PANE* getDesignBlockPane(); - LIB_TREE_NODE* getCurrentTreeNode(); + DESIGN_BLOCK_PANE* getDesignBlockPane() override; SCH_EDIT_FRAME* m_editFrame = nullptr; }; diff --git a/eeschema/tools/sch_drawing_tools.cpp b/eeschema/tools/sch_drawing_tools.cpp index a96e2d34cb..726cac581c 100644 --- a/eeschema/tools/sch_drawing_tools.cpp +++ b/eeschema/tools/sch_drawing_tools.cpp @@ -40,7 +40,7 @@ #include <sch_edit_frame.h> #include <pgm_base.h> #include <design_block.h> -#include <design_block_pane.h> +#include <widgets/sch_design_block_pane.h> #include <eeschema_id.h> #include <confirm.h> #include <view/view_controls.h> @@ -654,8 +654,8 @@ int SCH_DRAWING_TOOLS::ImportSheet( const TOOL_EVENT& aEvent ) { if( m_frame->GetDesignBlockPane()->GetSelectedLibId().IsValid() ) { - designBlock = - m_frame->GetDesignBlock( m_frame->GetDesignBlockPane()->GetSelectedLibId() ); + designBlock = m_frame->GetDesignBlockPane()->GetDesignBlock( + m_frame->GetDesignBlockPane()->GetSelectedLibId(), true, true ); if( !designBlock ) return 0; @@ -826,7 +826,7 @@ int SCH_DRAWING_TOOLS::ImportSheet( const TOOL_EVENT& aEvent ) sheetFileName = dlg.GetPath(); - m_frame->UpdateDesignBlockOptions(); + m_frame->GetDesignBlockPane()->UpdateCheckboxes(); } if( sheetFileName.IsEmpty() ) diff --git a/eeschema/widgets/design_block_pane.h b/eeschema/widgets/design_block_pane.h deleted file mode 100644 index af9a51ecdf..0000000000 --- a/eeschema/widgets/design_block_pane.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright The KiCad Developers, see AUTHORS.txt for contributors. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ -#ifndef DESIGN_BLOCK_PANE_H -#define DESIGN_BLOCK_PANE_H - -#include <design_block_tree_model_adapter.h> -#include <widgets/html_window.h> -#include <widgets/wx_panel.h> -#include <wx/checkbox.h> -#include <wx/filedlgcustomize.h> -#include <eeschema_settings.h> - - -class SCH_EDIT_FRAME; -class PANEL_DESIGN_BLOCK_CHOOSER; - - -class DESIGN_BLOCK_PANE : public WX_PANEL -{ -public: - /** - * Create dialog to choose design_block. - * - * @param aParent a SCH_BASE_FRAME parent window. - * @param aAllowFieldEdits if false, all functions that allow the user to edit fields - * (currently just footprint selection) will not be available. - * @param aShowFootprints if false, all footprint preview and selection features are - * disabled. This forces aAllowFieldEdits false too. - */ - DESIGN_BLOCK_PANE( SCH_EDIT_FRAME* aParent, const LIB_ID* aPreselect, - std::vector<LIB_ID>& aHistoryList ); - - ~DESIGN_BLOCK_PANE() override; - - void SaveSettings(); - - /** - * To be called after this dialog returns from ShowModal(). - * - * For multi-unit design_blocks, if the user selects the design_block itself rather than picking - * an individual unit, 0 will be returned in aUnit. - * Beware that this is an invalid unit number - this should be replaced with whatever - * default is desired (usually 1). - * - * @param aUnit if not NULL, the selected unit is filled in here. - * @return the #LIB_ID of the design_block that has been selected. - */ - LIB_ID GetSelectedLibId( int* aUnit = nullptr ) const; - void SelectLibId( const LIB_ID& aLibId ); - - void RefreshLibs(); - - /* Handler for checkbox events */ - void OnCheckBox( wxCommandEvent& aEvent ); - void UpdateCheckboxes(); - - void OnSaveSheetAsDesignBlock( wxCommandEvent& aEvent ); - void OnSaveSelectionAsDesignBlock( wxCommandEvent& aEvent ); - - void OnDeleteLibrary( wxCommandEvent& aEvent ); - void OnDeleteDesignBlock( wxCommandEvent& aEvent ); - - PANEL_DESIGN_BLOCK_CHOOSER* GetDesignBlockPanel() const { return m_chooserPanel; } - -protected: - void setLabelsAndTooltips(); - - virtual void OnLanguageChanged( wxCommandEvent& aEvent ); - void OnClosed( wxAuiManagerEvent& aEvent ); - -protected: - PANEL_DESIGN_BLOCK_CHOOSER* m_chooserPanel; - - wxCheckBox* m_repeatedPlacement; - wxCheckBox* m_placeAsSheet; - wxCheckBox* m_keepAnnotations; - - SCH_EDIT_FRAME* m_frame; -}; - - -// This is a helper class for the file dialog to allow the user to choose similar options -// as the design block chooser when importing a sheet. -class FILEDLG_IMPORT_SHEET_CONTENTS : public wxFileDialogCustomizeHook -{ -public: - FILEDLG_IMPORT_SHEET_CONTENTS( EESCHEMA_SETTINGS* aSettings ); - - virtual void AddCustomControls( wxFileDialogCustomize& customizer ) override; - - virtual void TransferDataFromCustomControls() override; - -private: - EESCHEMA_SETTINGS* m_settings; - - wxFileDialogCheckBox* m_cbRepeatedPlacement; - wxFileDialogCheckBox* m_cbPlaceAsSheet; - wxFileDialogCheckBox* m_cbKeepAnnotations; - - wxDECLARE_NO_COPY_CLASS( FILEDLG_IMPORT_SHEET_CONTENTS ); -}; - -#endif diff --git a/eeschema/widgets/design_block_pane.cpp b/eeschema/widgets/sch_design_block_pane.cpp similarity index 68% rename from eeschema/widgets/design_block_pane.cpp rename to eeschema/widgets/sch_design_block_pane.cpp index 459d7bdf39..508e2e7e9b 100644 --- a/eeschema/widgets/design_block_pane.cpp +++ b/eeschema/widgets/sch_design_block_pane.cpp @@ -22,7 +22,8 @@ */ #include <design_block.h> -#include <widgets/design_block_pane.h> +#include <widgets/sch_design_block_pane.h> +#include <widgets/sch_design_block_preview_widget.h> #include <widgets/panel_design_block_chooser.h> #include <kiface_base.h> #include <sch_edit_frame.h> @@ -33,6 +34,7 @@ #include <wx/sizer.h> #include <sch_actions.h> #include <tool/tool_manager.h> +#include <tools/sch_design_block_control.h> // Do not make these static wxStrings; they need to respond to language changes @@ -40,20 +42,24 @@ #define PLACE_AS_SHEET _( "Place as sheet" ) #define KEEP_ANNOTATIONS _( "Keep annotations" ) -DESIGN_BLOCK_PANE::DESIGN_BLOCK_PANE( SCH_EDIT_FRAME* aParent, const LIB_ID* aPreselect, - std::vector<LIB_ID>& aHistoryList ) : - WX_PANEL( aParent ), - m_frame( aParent ) +SCH_DESIGN_BLOCK_PANE::SCH_DESIGN_BLOCK_PANE( SCH_EDIT_FRAME* aParent, const LIB_ID* aPreselect, + std::vector<LIB_ID>& aHistoryList ) : + DESIGN_BLOCK_PANE( aParent, aPreselect, aHistoryList ) { wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL ); m_chooserPanel = new PANEL_DESIGN_BLOCK_CHOOSER( aParent, this, aHistoryList, - // Accept handler - [this]() - { - m_frame->GetToolManager()->RunAction( SCH_ACTIONS::placeDesignBlock ); - } ); - + [aParent]() + { + aParent->GetToolManager()->RunAction( + SCH_ACTIONS::placeDesignBlock ); + }, + aParent->GetToolManager()->GetTool<SCH_DESIGN_BLOCK_CONTROL>() + ); + // Use the same draw engine type as the one used in parent frame m_frame + EDA_DRAW_PANEL_GAL::GAL_TYPE canvasType = m_frame->GetCanvas()->GetBackend(); + m_chooserPanel->SetPreviewWidget( + new SCH_DESIGN_BLOCK_PREVIEW_WIDGET( m_chooserPanel->GetDetailsPanel(), canvasType, true ) ); sizer->Add( m_chooserPanel, 1, wxEXPAND, 5 ); if( aPreselect && aPreselect->IsValid() ) @@ -70,13 +76,13 @@ DESIGN_BLOCK_PANE::DESIGN_BLOCK_PANE( SCH_EDIT_FRAME* aParent, const LIB_ID* aPr UpdateCheckboxes(); // Set all checkbox handlers to the same function - m_repeatedPlacement->Bind( wxEVT_CHECKBOX, &DESIGN_BLOCK_PANE::OnCheckBox, this ); - m_placeAsSheet->Bind( wxEVT_CHECKBOX, &DESIGN_BLOCK_PANE::OnCheckBox, this ); - m_keepAnnotations->Bind( wxEVT_CHECKBOX, &DESIGN_BLOCK_PANE::OnCheckBox, this ); + m_repeatedPlacement->Bind( wxEVT_CHECKBOX, &SCH_DESIGN_BLOCK_PANE::OnCheckBox, this ); + m_placeAsSheet->Bind( wxEVT_CHECKBOX, &SCH_DESIGN_BLOCK_PANE::OnCheckBox, this ); + m_keepAnnotations->Bind( wxEVT_CHECKBOX, &SCH_DESIGN_BLOCK_PANE::OnCheckBox, this ); - cbSizer->Add( m_repeatedPlacement, 0, wxTOP|wxLEFT, 2 ); - cbSizer->Add( m_placeAsSheet, 0, wxTOP|wxLEFT, 2 ); - cbSizer->Add( m_keepAnnotations, 0, wxTOP|wxLEFT|wxBOTTOM, 2 ); + cbSizer->Add( m_repeatedPlacement, 0, wxTOP | wxLEFT, 2 ); + cbSizer->Add( m_placeAsSheet, 0, wxTOP | wxLEFT, 2 ); + cbSizer->Add( m_keepAnnotations, 0, wxTOP | wxLEFT | wxBOTTOM, 2 ); sizer->Add( cbSizer, 0, wxEXPAND, 5 ); SetSizer( sizer ); @@ -85,31 +91,10 @@ DESIGN_BLOCK_PANE::DESIGN_BLOCK_PANE( SCH_EDIT_FRAME* aParent, const LIB_ID* aPr Layout(); Bind( wxEVT_CHAR_HOOK, &PANEL_DESIGN_BLOCK_CHOOSER::OnChar, m_chooserPanel ); - m_frame->Bind( EDA_LANG_CHANGED, &DESIGN_BLOCK_PANE::OnLanguageChanged, this ); - m_frame->Bind( wxEVT_AUI_PANE_CLOSE, &DESIGN_BLOCK_PANE::OnClosed, this ); } -DESIGN_BLOCK_PANE::~DESIGN_BLOCK_PANE() -{ - m_frame->Unbind( wxEVT_AUI_PANE_CLOSE, &DESIGN_BLOCK_PANE::OnClosed, this ); - m_frame->Unbind( EDA_LANG_CHANGED, &DESIGN_BLOCK_PANE::OnLanguageChanged, this ); -} - - -void DESIGN_BLOCK_PANE::OnClosed( wxAuiManagerEvent &aEvent ) -{ - if( APP_SETTINGS_BASE* cfg = m_frame->config() ) - { - if( IsShownOnScreen() ) // Ensure the panel is shown when trying to save its size - m_frame->SaveSettings( cfg ); - } - - aEvent.Skip(); -} - - -void DESIGN_BLOCK_PANE::setLabelsAndTooltips() +void SCH_DESIGN_BLOCK_PANE::setLabelsAndTooltips() { if( m_repeatedPlacement ) { @@ -134,18 +119,7 @@ void DESIGN_BLOCK_PANE::setLabelsAndTooltips() } -void DESIGN_BLOCK_PANE::OnLanguageChanged( wxCommandEvent& aEvent ) -{ - if( m_chooserPanel ) - m_chooserPanel->ShowChangedLanguage(); - - setLabelsAndTooltips(); - - aEvent.Skip(); -} - - -void DESIGN_BLOCK_PANE::OnCheckBox( wxCommandEvent& aEvent ) +void SCH_DESIGN_BLOCK_PANE::OnCheckBox( wxCommandEvent& aEvent ) { if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) ) { @@ -156,7 +130,7 @@ void DESIGN_BLOCK_PANE::OnCheckBox( wxCommandEvent& aEvent ) } -void DESIGN_BLOCK_PANE::UpdateCheckboxes() +void SCH_DESIGN_BLOCK_PANE::UpdateCheckboxes() { if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) ) { @@ -167,34 +141,8 @@ void DESIGN_BLOCK_PANE::UpdateCheckboxes() } -void DESIGN_BLOCK_PANE::SaveSettings() -{ - m_chooserPanel->SaveSettings(); -} - - -LIB_ID DESIGN_BLOCK_PANE::GetSelectedLibId( int* aUnit ) const -{ - return m_chooserPanel->GetSelectedLibId( aUnit ); -} - - -void DESIGN_BLOCK_PANE::SelectLibId( const LIB_ID& aLibId ) -{ - m_chooserPanel->SelectLibId( aLibId ); -} - - -void DESIGN_BLOCK_PANE::RefreshLibs() -{ - m_chooserPanel->RefreshLibs(); -} - - FILEDLG_IMPORT_SHEET_CONTENTS::FILEDLG_IMPORT_SHEET_CONTENTS( EESCHEMA_SETTINGS* aSettings ) : - m_cbRepeatedPlacement( nullptr ), - m_cbPlaceAsSheet( nullptr ), - m_cbKeepAnnotations( nullptr ) + m_cbRepeatedPlacement( nullptr ), m_cbPlaceAsSheet( nullptr ), m_cbKeepAnnotations( nullptr ) { wxASSERT( aSettings ); m_settings = aSettings; diff --git a/eeschema/widgets/sch_design_block_pane.h b/eeschema/widgets/sch_design_block_pane.h new file mode 100644 index 0000000000..f4f8e863fc --- /dev/null +++ b/eeschema/widgets/sch_design_block_pane.h @@ -0,0 +1,79 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright The KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +#ifndef SCH_DESIGN_BLOCK_PANE_H +#define SCH_DESIGN_BLOCK_PANE_H + +#include <widgets/design_block_pane.h> +#include <design_block_tree_model_adapter.h> +#include <widgets/html_window.h> +#include <widgets/wx_panel.h> +#include <wx/checkbox.h> +#include <wx/filedlgcustomize.h> +#include <eeschema_settings.h> + + +class SCH_EDIT_FRAME; +class PANEL_DESIGN_BLOCK_CHOOSER; + + +class SCH_DESIGN_BLOCK_PANE : public DESIGN_BLOCK_PANE +{ +public: + SCH_DESIGN_BLOCK_PANE( SCH_EDIT_FRAME* aParent, const LIB_ID* aPreselect, std::vector<LIB_ID>& aHistoryList ); + + /* Handler for checkbox events */ + void OnCheckBox( wxCommandEvent& aEvent ); + void UpdateCheckboxes(); + +protected: + void setLabelsAndTooltips() override; + +protected: + wxCheckBox* m_repeatedPlacement; + wxCheckBox* m_placeAsSheet; + wxCheckBox* m_keepAnnotations; +}; + + +// This is a helper class for the file dialog to allow the user to choose similar options +// as the design block chooser when importing a sheet. +class FILEDLG_IMPORT_SHEET_CONTENTS : public wxFileDialogCustomizeHook +{ +public: + FILEDLG_IMPORT_SHEET_CONTENTS( EESCHEMA_SETTINGS* aSettings ); + + void AddCustomControls( wxFileDialogCustomize& customizer ) override; + + void TransferDataFromCustomControls() override; + +private: + EESCHEMA_SETTINGS* m_settings; + + wxFileDialogCheckBox* m_cbRepeatedPlacement; + wxFileDialogCheckBox* m_cbPlaceAsSheet; + wxFileDialogCheckBox* m_cbKeepAnnotations; + + wxDECLARE_NO_COPY_CLASS( FILEDLG_IMPORT_SHEET_CONTENTS ); +}; + +#endif diff --git a/eeschema/widgets/design_block_preview_widget.cpp b/eeschema/widgets/sch_design_block_preview_widget.cpp similarity index 82% rename from eeschema/widgets/design_block_preview_widget.cpp rename to eeschema/widgets/sch_design_block_preview_widget.cpp index 4a048ab0df..56d64a2ed6 100644 --- a/eeschema/widgets/design_block_preview_widget.cpp +++ b/eeschema/widgets/sch_design_block_preview_widget.cpp @@ -17,7 +17,6 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "design_block_preview_widget.h" #include <schematic.h> #include <sch_sheet.h> #include <sch_view.h> @@ -35,13 +34,16 @@ #include <eeschema_settings.h> #include <eeschema_helpers.h> #include <settings/settings_manager.h> +#include <widgets/sch_design_block_preview_widget.h> #include <wx/log.h> #include <wx/stattext.h> +#include <wx/panel.h> -DESIGN_BLOCK_PREVIEW_WIDGET::DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent, bool aIncludeStatus, - EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType ) : - wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ), +SCH_DESIGN_BLOCK_PREVIEW_WIDGET::SCH_DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent, + EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType, + bool aIncludeStatus ) : + DESIGN_BLOCK_PREVIEW_WIDGET( aParent ), m_preview( nullptr ), m_status( nullptr ), m_statusPanel( nullptr ), @@ -58,14 +60,13 @@ DESIGN_BLOCK_PREVIEW_WIDGET::DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent, boo EDA_DRAW_PANEL_GAL::GAL_TYPE canvasType = aCanvasType; // Allows only a CAIRO or OPENGL canvas: - if( canvasType != EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL - && canvasType != EDA_DRAW_PANEL_GAL::GAL_FALLBACK ) + if( canvasType != EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL && canvasType != EDA_DRAW_PANEL_GAL::GAL_FALLBACK ) { canvasType = EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL; } - m_preview = new SCH_PREVIEW_PANEL( this, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ), - m_galDisplayOptions, canvasType ); + m_preview = new SCH_PREVIEW_PANEL( this, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ), m_galDisplayOptions, + canvasType ); m_preview->SetStealsFocus( false ); m_preview->ShowScrollbars( wxSHOW_SB_NEVER, wxSHOW_SB_NEVER ); m_preview->GetGAL()->SetAxesEnabled( false ); @@ -131,11 +132,11 @@ DESIGN_BLOCK_PREVIEW_WIDGET::DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent, boo SetSizer( m_outerSizer ); Layout(); - Connect( wxEVT_SIZE, wxSizeEventHandler( DESIGN_BLOCK_PREVIEW_WIDGET::onSize ), nullptr, this ); + Connect( wxEVT_SIZE, wxSizeEventHandler( SCH_DESIGN_BLOCK_PREVIEW_WIDGET::onSize ), nullptr, this ); } -DESIGN_BLOCK_PREVIEW_WIDGET::~DESIGN_BLOCK_PREVIEW_WIDGET() +SCH_DESIGN_BLOCK_PREVIEW_WIDGET::~SCH_DESIGN_BLOCK_PREVIEW_WIDGET() { if( m_previewItem ) m_preview->GetView()->Remove( m_previewItem ); @@ -144,7 +145,7 @@ DESIGN_BLOCK_PREVIEW_WIDGET::~DESIGN_BLOCK_PREVIEW_WIDGET() } -void DESIGN_BLOCK_PREVIEW_WIDGET::SetStatusText( wxString const& aText ) +void SCH_DESIGN_BLOCK_PREVIEW_WIDGET::SetStatusText( wxString const& aText ) { wxCHECK( m_statusPanel, /* void */ ); @@ -155,7 +156,7 @@ void DESIGN_BLOCK_PREVIEW_WIDGET::SetStatusText( wxString const& aText ) } -void DESIGN_BLOCK_PREVIEW_WIDGET::onSize( wxSizeEvent& aEvent ) +void SCH_DESIGN_BLOCK_PREVIEW_WIDGET::onSize( wxSizeEvent& aEvent ) { if( m_previewItem ) { @@ -167,7 +168,7 @@ void DESIGN_BLOCK_PREVIEW_WIDGET::onSize( wxSizeEvent& aEvent ) } -void DESIGN_BLOCK_PREVIEW_WIDGET::fitOnDrawArea() +void SCH_DESIGN_BLOCK_PREVIEW_WIDGET::fitOnDrawArea() { if( !m_previewItem ) return; @@ -179,8 +180,8 @@ void DESIGN_BLOCK_PREVIEW_WIDGET::fitOnDrawArea() view->SetScale( 1.0 ); VECTOR2D clientSize = view->ToWorld( ToVECTOR2D( m_preview->GetClientSize() ), false ); // Calculate the draw scale to fit the drawing area - double scale = std::min( fabs( clientSize.x / m_itemBBox.GetWidth() ), - fabs( clientSize.y / m_itemBBox.GetHeight() ) ); + double scale = + std::min( fabs( clientSize.x / m_itemBBox.GetWidth() ), fabs( clientSize.y / m_itemBBox.GetHeight() ) ); // Above calculation will yield an exact fit; add a bit of whitespace around block scale /= 1.2; @@ -191,7 +192,7 @@ void DESIGN_BLOCK_PREVIEW_WIDGET::fitOnDrawArea() } -void DESIGN_BLOCK_PREVIEW_WIDGET::DisplayDesignBlock( DESIGN_BLOCK* aDesignBlock ) +void SCH_DESIGN_BLOCK_PREVIEW_WIDGET::DisplayDesignBlock( DESIGN_BLOCK* aDesignBlock ) { KIGFX::VIEW* view = m_preview->GetView(); @@ -202,10 +203,10 @@ void DESIGN_BLOCK_PREVIEW_WIDGET::DisplayDesignBlock( DESIGN_BLOCK* aDesignBlock m_previewItem = nullptr; } - if( aDesignBlock ) + if( aDesignBlock && wxFileExists( aDesignBlock->GetSchematicFile() ) ) { - m_previewItem = EESCHEMA_HELPERS::LoadSchematic( aDesignBlock->GetSchematicFile(), - SCH_IO_MGR::SCH_KICAD, false, true ); + m_previewItem = + EESCHEMA_HELPERS::LoadSchematic( aDesignBlock->GetSchematicFile(), SCH_IO_MGR::SCH_KICAD, false, true ); BOX2I bBox; if( m_previewItem ) diff --git a/eeschema/widgets/design_block_preview_widget.h b/eeschema/widgets/sch_design_block_preview_widget.h similarity index 74% rename from eeschema/widgets/design_block_preview_widget.h rename to eeschema/widgets/sch_design_block_preview_widget.h index da95f38581..0b20600dc4 100644 --- a/eeschema/widgets/design_block_preview_widget.h +++ b/eeschema/widgets/sch_design_block_preview_widget.h @@ -17,10 +17,11 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef DESIGN_BLOCK_PREVIEW_WIDGET_H -#define DESIGN_BLOCK_PREVIEW_WIDGET_H +#ifndef SCH_DESIGN_BLOCK_PREVIEW_WIDGET_H +#define SCH_DESIGN_BLOCK_PREVIEW_WIDGET_H #include <wx/panel.h> +#include <widgets/design_block_preview_widget.h> #include <kiway.h> #include <gal_display_options_common.h> #include <class_draw_panel_gal.h> @@ -34,29 +35,27 @@ class wxStaticText; class wxSizer; -class DESIGN_BLOCK_PREVIEW_WIDGET : public wxPanel +class SCH_DESIGN_BLOCK_PREVIEW_WIDGET : public DESIGN_BLOCK_PREVIEW_WIDGET { public: /** - * Construct a symbol preview widget. + * Construct a schematic design block preview widget. * * @param aParent - parent window - * @param aCanvasType = the type of canvas (GAL_TYPE_OPENGL or GAL_TYPE_CAIRO only) */ - DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent, bool aIncludeStatus, - EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType ); + SCH_DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent, EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType, bool aIncludeStatus ); - ~DESIGN_BLOCK_PREVIEW_WIDGET() override; + ~SCH_DESIGN_BLOCK_PREVIEW_WIDGET() override; /** * Set the contents of the status label and display it. */ - void SetStatusText( const wxString& aText ); + void SetStatusText( const wxString& aText ) override; /** * Set the currently displayed symbol. */ - void DisplayDesignBlock( DESIGN_BLOCK* aDesignBlock ); + void DisplayDesignBlock( DESIGN_BLOCK* aDesignBlock ) override; protected: void onSize( wxSizeEvent& aEvent ); @@ -78,4 +77,4 @@ protected: }; -#endif // DESIGN_BLOCK_PREVIEW_WIDGET_H +#endif diff --git a/include/advanced_config.h b/include/advanced_config.h index 0e0fec8ea8..3e0f55d6a4 100644 --- a/include/advanced_config.h +++ b/include/advanced_config.h @@ -471,13 +471,13 @@ public: int m_DisambiguationMenuDelay; /** - * Enable the new Design Blocks feature + * Enable the new PCB Design Blocks feature * - * Setting name: "EnableDesignBlocks" + * Setting name: "EnablePcbDesignBlocks" * Valid values: true or false * Default value: false */ - bool m_EnableDesignBlocks; + bool m_EnablePcbDesignBlocks; /** * Enable support for generators. diff --git a/include/design_block_lib_table.h b/include/design_block_lib_table.h index dbbfbbfbc7..0c42bd92fe 100644 --- a/include/design_block_lib_table.h +++ b/include/design_block_lib_table.h @@ -79,6 +79,14 @@ public: DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T GetFileType() { return type; } + /** + * Attempt to reload the library. + * + * @return true if a reload was required + * @throw IO_ERROR if the reload was unsuccessful. + */ + bool Refresh() override; + protected: DESIGN_BLOCK_LIB_TABLE_ROW( const DESIGN_BLOCK_LIB_TABLE_ROW& aRow ) : LIB_TABLE_ROW( aRow ), diff --git a/include/settings/app_settings.h b/include/settings/app_settings.h index 390072f590..b127b5198f 100644 --- a/include/settings/app_settings.h +++ b/include/settings/app_settings.h @@ -135,6 +135,21 @@ public: std::vector<wxString> open_libs; ///< list of libraries the user has open in the tree. }; + struct PANEL_DESIGN_BLOCK_CHOOSER + { + int sash_pos_h; + int sash_pos_v; + int width; + int height; + int sort_mode; + bool repeated_placement; + bool place_as_sheet; + bool keep_annotations; + + // For saving tree columns and widths + LIB_TREE tree; + }; + struct PRINTING { bool background; ///< Whether or not to print background color. @@ -179,6 +194,8 @@ public: FIND_REPLACE m_FindReplace; + PANEL_DESIGN_BLOCK_CHOOSER m_DesignBlockChooserPanel; + GRAPHICS m_Graphics; COLOR_PICKER m_ColorPicker; diff --git a/kicad/menubar.cpp b/kicad/menubar.cpp index be9eae9ba0..69df89106b 100644 --- a/kicad/menubar.cpp +++ b/kicad/menubar.cpp @@ -208,8 +208,7 @@ void KICAD_MANAGER_FRAME::doReCreateMenuBar() prefsMenu->Add( ACTIONS::configurePaths ); prefsMenu->Add( ACTIONS::showSymbolLibTable ); prefsMenu->Add( ACTIONS::showFootprintLibTable ); - if( ADVANCED_CFG::GetCfg().m_EnableDesignBlocks ) - prefsMenu->Add( ACTIONS::showDesignBlockLibTable ); + prefsMenu->Add( ACTIONS::showDesignBlockLibTable ); prefsMenu->Add( ACTIONS::openPreferences ); prefsMenu->AppendSeparator(); diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index fd553b4bf8..40e6cf26a6 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -356,6 +356,7 @@ set( PCBNEW_CLASS_SRCS menubar_footprint_editor.cpp menubar_pcb_editor.cpp pcb_base_edit_frame.cpp + pcb_design_block_utils.cpp pcb_layer_box_selector.cpp pcb_edit_frame.cpp pcb_plotter.cpp @@ -399,6 +400,7 @@ set( PCBNEW_CLASS_SRCS tools/item_modification_routine.cpp tools/pad_tool.cpp tools/pcb_control.cpp + tools/pcb_design_block_control.cpp tools/pcb_picker_tool.cpp tools/pcb_selection.cpp tools/pcb_selection_conditions.cpp @@ -423,6 +425,8 @@ set( PCBNEW_CLASS_SRCS widgets/panel_footprint_chooser.cpp widgets/panel_selection_filter.cpp widgets/panel_selection_filter_base.cpp + widgets/pcb_design_block_pane.cpp + widgets/pcb_design_block_preview_widget.cpp widgets/pcb_properties_panel.cpp widgets/pcb_search_pane.cpp widgets/search_handlers.cpp diff --git a/pcbnew/board.cpp b/pcbnew/board.cpp index 9a92f535a7..6955b87c9a 100644 --- a/pcbnew/board.cpp +++ b/pcbnew/board.cpp @@ -944,6 +944,12 @@ BOARD_DESIGN_SETTINGS& BOARD::GetDesignSettings() const } +void BOARD::SetDesignSettings( const BOARD_DESIGN_SETTINGS& aSettings ) +{ + *m_designSettings = aSettings; +} + + int BOARD::GetMaxClearanceValue() const { if( !m_maxClearanceValue.has_value() ) diff --git a/pcbnew/board.h b/pcbnew/board.h index b910fa5326..2385eb3045 100644 --- a/pcbnew/board.h +++ b/pcbnew/board.h @@ -708,6 +708,7 @@ public: * @return the BOARD_DESIGN_SETTINGS for this BOARD */ BOARD_DESIGN_SETTINGS& GetDesignSettings() const; + void SetDesignSettings( const BOARD_DESIGN_SETTINGS& aSettings ); BOARD_STACKUP GetStackupOrDefault() const; diff --git a/pcbnew/cross-probing.cpp b/pcbnew/cross-probing.cpp index f31f1b881c..b94ea40146 100644 --- a/pcbnew/cross-probing.cpp +++ b/pcbnew/cross-probing.cpp @@ -54,6 +54,7 @@ #include <tools/pcb_actions.h> #include <tools/pcb_selection_tool.h> #include <netlist_reader/netlist_reader.h> +#include <widgets/pcb_design_block_pane.h> #include <wx/log.h> /* Execute a remote command sent via a socket on port KICAD_PCB_PORT_SERVICE_NUMBER @@ -718,6 +719,10 @@ void PCB_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail ) GetToolManager()->RunAction( ACTIONS::pluginsReload ); break; + case MAIL_RELOAD_LIB: + m_designBlocksPane->RefreshLibs(); + break; + // many many others. default: ; diff --git a/pcbnew/files.cpp b/pcbnew/files.cpp index bdab0e9bdc..717c5611be 100644 --- a/pcbnew/files.cpp +++ b/pcbnew/files.cpp @@ -441,7 +441,7 @@ bool PCB_EDIT_FRAME::Files_io_from_id( int id ) { if( id == ID_COPY_BOARD_AS ) { - success = SavePcbCopy( filename, createProject ); + success = SavePcbCopy( EnsureFileExtension( filename, FILEEXT::KiCadPcbFileExtension ), createProject ); } else { @@ -1135,7 +1135,7 @@ bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool addToHistory, bool PCB_EDIT_FRAME::SavePcbCopy( const wxString& aFileName, bool aCreateProject, bool aHeadless ) { - wxFileName pcbFileName( EnsureFileExtension( aFileName, FILEEXT::KiCadPcbFileExtension ) ); + wxFileName pcbFileName( aFileName ); if( !IsWritable( pcbFileName ) ) { @@ -1194,12 +1194,6 @@ bool PCB_EDIT_FRAME::SavePcbCopy( const wxString& aFileName, bool aCreateProject rulesFile.GetFullPath() ) ); } - if( !aHeadless ) - { - DisplayInfoMessage( this, wxString::Format( _( "Board copied to:\n%s" ), - pcbFileName.GetFullPath() ) ); - } - return true; } diff --git a/pcbnew/menubar_pcb_editor.cpp b/pcbnew/menubar_pcb_editor.cpp index f932116509..d192f55b08 100644 --- a/pcbnew/menubar_pcb_editor.cpp +++ b/pcbnew/menubar_pcb_editor.cpp @@ -234,6 +234,10 @@ void PCB_EDIT_FRAME::doReCreateMenuBar() showHidePanels->Add( PCB_ACTIONS::showSearch, ACTION_MENU::CHECK ); showHidePanels->Add( PCB_ACTIONS::showLayersManager, ACTION_MENU::CHECK ); showHidePanels->Add( PCB_ACTIONS::showNetInspector, ACTION_MENU::CHECK ); + + if( ADVANCED_CFG::GetCfg().m_EnablePcbDesignBlocks ) + showHidePanels->Add( PCB_ACTIONS::showDesignBlockPanel, ACTION_MENU::CHECK, _( "Design Blocks" ) ); + viewMenu->Add( showHidePanels ); viewMenu->AppendSeparator(); @@ -465,6 +469,8 @@ void PCB_EDIT_FRAME::doReCreateMenuBar() prefsMenu->Add( ACTIONS::configurePaths ); prefsMenu->Add( ACTIONS::showFootprintLibTable ); + if( ADVANCED_CFG::GetCfg().m_EnablePcbDesignBlocks ) + prefsMenu->Add( ACTIONS::showDesignBlockLibTable ); prefsMenu->Add( ACTIONS::openPreferences ); prefsMenu->AppendSeparator(); diff --git a/pcbnew/pcb_design_block_utils.cpp b/pcbnew/pcb_design_block_utils.cpp new file mode 100644 index 0000000000..8499cf8740 --- /dev/null +++ b/pcbnew/pcb_design_block_utils.cpp @@ -0,0 +1,422 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright The KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <pgm_base.h> +#include <kiway.h> +#include <board_commit.h> +#include <design_block.h> +#include <design_block_lib_table.h> +#include <widgets/pcb_design_block_pane.h> +#include <pcb_edit_frame.h> +#include <pcb_io/pcb_io.h> +#include <pcb_io/pcb_io_mgr.h> +#include <wx/choicdlg.h> +#include <wx/msgdlg.h> +#include <wx/textdlg.h> +#include <wildcards_and_files_ext.h> +#include <paths.h> +#include <env_paths.h> +#include <common.h> +#include <confirm.h> +#include <kidialog.h> +#include <locale_io.h> +#include <netinfo.h> +#include <tool/tool_manager.h> +#include <tools/pcb_selection_tool.h> +#include <dialogs/dialog_design_block_properties.h> +#include <nlohmann/json.hpp> + +bool checkOverwriteDb( wxWindow* aFrame, wxString& libname, wxString& newName ) +{ + wxString msg = wxString::Format( _( "Design block '%s' already exists in library '%s'." ), newName.GetData(), + libname.GetData() ); + + if( OKOrCancelDialog( aFrame, _( "Confirmation" ), msg, _( "Overwrite existing design block?" ), _( "Overwrite" ) ) + != wxID_OK ) + { + return false; + } + + return true; +} + + +bool checkOverwriteDbLayout( wxWindow* aFrame, const LIB_ID& aLibId ) +{ + wxString msg = wxString::Format( _( "Design block '%s' already has a layout." ), aLibId.GetUniStringLibItemName() ); + + if( OKOrCancelDialog( aFrame, _( "Confirmation" ), msg, _( "Overwrite existing layout?" ), _( "Overwrite" ) ) + != wxID_OK ) + { + return false; + } + + return true; +} + + +bool PCB_EDIT_FRAME::saveBoardAsFile( BOARD* aBoard, const wxString& aFileName, bool aHeadless ) +{ + // Ensure the "C" locale is temporary set, before saving any file + // It also avoid wxWidget alerts about locale issues, later, when using Python 3 + LOCALE_IO dummy; + + wxFileName pcbFileName( aFileName ); + + if( !IsWritable( pcbFileName ) ) + { + if( !aHeadless ) + { + DisplayError( this, wxString::Format( _( "Insufficient permissions to write file '%s'." ), + pcbFileName.GetFullPath() ) ); + } + return false; + } + + try + { + IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::PluginFind( PCB_IO_MGR::KICAD_SEXP ) ); + + wxASSERT( pcbFileName.IsAbsolute() ); + + pi->SaveBoard( pcbFileName.GetFullPath(), aBoard, nullptr ); + } + catch( const IO_ERROR& ioe ) + { + if( !aHeadless ) + { + DisplayError( this, wxString::Format( _( "Error saving board file '%s'.\n%s" ), pcbFileName.GetFullPath(), + ioe.What() ) ); + } + + return false; + } + + return true; +} + + +bool PCB_EDIT_FRAME::SaveBoardAsDesignBlock( const wxString& aLibraryName ) +{ + // Make sure the user has selected a library to save into + if( m_designBlocksPane->GetSelectedLibId().GetLibNickname().empty() ) + { + DisplayErrorMessage( this, _( "Please select a library to save the design block to." ) ); + return false; + } + + DESIGN_BLOCK blk; + wxFileName fn = wxFileNameFromPath( GetBoard()->GetFileName() ); + + blk.SetLibId( LIB_ID( aLibraryName, fn.GetName() ) ); + + DIALOG_DESIGN_BLOCK_PROPERTIES dlg( this, &blk ); + + if( dlg.ShowModal() != wxID_OK ) + return false; + + wxString libName = blk.GetLibId().GetLibNickname(); + wxString newName = blk.GetLibId().GetLibItemName(); + + if( Prj().DesignBlockLibs()->DesignBlockExists( libName, newName ) && !checkOverwriteDb( this, libName, newName ) ) + { + return false; + } + + // Save a temporary copy of the schematic file, as the plugin is just going to move it + wxString tempFile = wxFileName::CreateTempFileName( "design_block" ); + + if( !SavePcbCopy( tempFile, false, false ) ) + { + DisplayErrorMessage( this, _( "Error saving temporary board file to create design block." ) ); + wxRemoveFile( tempFile ); + return false; + } + + blk.SetBoardFile( tempFile ); + + bool success = false; + + try + { + success = Prj().DesignBlockLibs()->DesignBlockSave( aLibraryName, &blk ) == DESIGN_BLOCK_LIB_TABLE::SAVE_OK; + } + catch( const IO_ERROR& ioe ) + { + DisplayError( this, ioe.What() ); + } + + // Clean up the temporary file + wxRemoveFile( tempFile ); + + m_designBlocksPane->RefreshLibs(); + m_designBlocksPane->SelectLibId( blk.GetLibId() ); + + return success; +} + + +bool PCB_EDIT_FRAME::SaveBoardToDesignBlock( const LIB_ID& aLibId ) +{ + // Make sure the user has selected a library to save into + if( m_designBlocksPane->GetSelectedLibId().GetLibNickname().empty() ) + { + DisplayErrorMessage( this, _( "Please select a library to save the design block to." ) ); + return false; + } + + DESIGN_BLOCK* blk = nullptr; + + try + { + blk = Prj().DesignBlockLibs()->DesignBlockLoad( aLibId.GetLibNickname(), aLibId.GetLibItemName() ); + } + catch( const IO_ERROR& ioe ) + { + DisplayError( this, ioe.What() ); + return false; + } + + if( !blk->GetBoardFile().IsEmpty() && !checkOverwriteDbLayout( this, aLibId ) ) + { + return false; + } + + // Save a temporary copy of the schematic file, as the plugin is just going to move it + wxString tempFile = wxFileName::CreateTempFileName( "design_block" ); + + if( !SavePcbCopy( tempFile, false, false ) ) + { + DisplayErrorMessage( this, _( "Error saving temporary board file to create design block." ) ); + wxRemoveFile( tempFile ); + return false; + } + + blk->SetBoardFile( tempFile ); + + bool success = false; + + try + { + success = Prj().DesignBlockLibs()->DesignBlockSave( aLibId.GetLibNickname(), blk ) + == DESIGN_BLOCK_LIB_TABLE::SAVE_OK; + } + catch( const IO_ERROR& ioe ) + { + DisplayError( this, ioe.What() ); + } + + // Clean up the temporary file + wxRemoveFile( tempFile ); + + m_designBlocksPane->RefreshLibs(); + m_designBlocksPane->SelectLibId( blk->GetLibId() ); + + return success; +} + + +bool PCB_EDIT_FRAME::SaveSelectionAsDesignBlock( const wxString& aLibraryName ) +{ + // Make sure the user has selected a library to save into + if( m_designBlocksPane->GetSelectedLibId().GetLibNickname().empty() ) + { + DisplayErrorMessage( this, _( "Please select a library to save the design block to." ) ); + return false; + } + + // Get all selected items + PCB_SELECTION selection = m_toolManager->GetTool<PCB_SELECTION_TOOL>()->GetSelection(); + + if( selection.Empty() ) + { + DisplayErrorMessage( this, _( "Please select some items to save as a design block." ) ); + return false; + } + + DESIGN_BLOCK blk; + wxFileName fn = wxFileNameFromPath( GetBoard()->GetFileName() ); + + blk.SetLibId( LIB_ID( aLibraryName, fn.GetName() ) ); + + DIALOG_DESIGN_BLOCK_PROPERTIES dlg( this, &blk ); + + if( dlg.ShowModal() != wxID_OK ) + return false; + + wxString libName = blk.GetLibId().GetLibNickname(); + wxString newName = blk.GetLibId().GetLibItemName(); + + if( Prj().DesignBlockLibs()->DesignBlockExists( libName, newName ) && !checkOverwriteDb( this, libName, newName ) ) + { + return false; + } + + // Create a temporary board + BOARD* tempBoard = new BOARD(); + tempBoard->SetDesignSettings( GetBoard()->GetDesignSettings() ); + tempBoard->SetProject( &Prj(), true ); + tempBoard->SynchronizeProperties(); + + // Copy all net info into the new board + for( NETINFO_ITEM* netInfo : GetBoard()->GetNetInfo() ) + { + EDA_ITEM* copy = netInfo->Clone(); + tempBoard->Add( static_cast<BOARD_ITEM*>( copy ), ADD_MODE::APPEND, false ); + } + + // Copy the selected items to the temporary board + for( EDA_ITEM* item : selection ) + { + EDA_ITEM* copy = item->Clone(); + tempBoard->Add( static_cast<BOARD_ITEM*>( copy ), ADD_MODE::APPEND, false ); + } + + // Rebuild connectivity, remove any unused nets + tempBoard->BuildListOfNets(); + tempBoard->BuildConnectivity(); + tempBoard->RemoveUnusedNets( nullptr ); + + wxString tempFile = wxFileName::CreateTempFileName( "design_block" ); + + if( !saveBoardAsFile( tempBoard, tempFile, false ) ) + { + DisplayErrorMessage( this, _( "Error saving temporary board file to create design block." ) ); + wxRemoveFile( tempFile ); + return false; + } + + blk.SetBoardFile( tempFile ); + + bool success = false; + + try + { + success = Prj().DesignBlockLibs()->DesignBlockSave( aLibraryName, &blk ) == DESIGN_BLOCK_LIB_TABLE::SAVE_OK; + } + catch( const IO_ERROR& ioe ) + { + DisplayError( this, ioe.What() ); + } + + // Clean up the temporary file + wxRemoveFile( tempFile ); + + m_designBlocksPane->RefreshLibs(); + m_designBlocksPane->SelectLibId( blk.GetLibId() ); + + return success; +} + + +bool PCB_EDIT_FRAME::SaveSelectionToDesignBlock( const LIB_ID& aLibId ) +{ + // Make sure the user has selected a library to save into + if( m_designBlocksPane->GetSelectedLibId().GetLibNickname().empty() ) + { + DisplayErrorMessage( this, _( "Please select a library to save the design block to." ) ); + return false; + } + + // Get all selected items + PCB_SELECTION selection = m_toolManager->GetTool<PCB_SELECTION_TOOL>()->GetSelection(); + + if( selection.Empty() ) + { + DisplayErrorMessage( this, _( "Please select some items to save as a design block." ) ); + return false; + } + + DESIGN_BLOCK* blk = nullptr; + + try + { + blk = Prj().DesignBlockLibs()->DesignBlockLoad( aLibId.GetLibNickname(), aLibId.GetLibItemName() ); + } + catch( const IO_ERROR& ioe ) + { + DisplayError( this, ioe.What() ); + return false; + } + + if( !blk->GetBoardFile().IsEmpty() && !checkOverwriteDbLayout( this, aLibId ) ) + { + return false; + } + + // Create a temporary board + BOARD* tempBoard = new BOARD(); + tempBoard->SetDesignSettings( GetBoard()->GetDesignSettings() ); + tempBoard->SetProject( &Prj(), true ); + tempBoard->SynchronizeProperties(); + + // Copy all net info into the new board + for( NETINFO_ITEM* netInfo : GetBoard()->GetNetInfo() ) + { + EDA_ITEM* copy = netInfo->Clone(); + tempBoard->Add( static_cast<BOARD_ITEM*>( copy ), ADD_MODE::APPEND, false ); + } + + // Copy the selected items to the temporary board + for( EDA_ITEM* item : selection ) + { + EDA_ITEM* copy = item->Clone(); + tempBoard->Add( static_cast<BOARD_ITEM*>( copy ), ADD_MODE::APPEND, false ); + } + + // Rebuild connectivity, remove any unused nets + tempBoard->BuildListOfNets(); + tempBoard->BuildConnectivity(); + tempBoard->RemoveUnusedNets( nullptr ); + + wxString tempFile = wxFileName::CreateTempFileName( "design_block" ); + + if( !saveBoardAsFile( tempBoard, tempFile, false ) ) + { + DisplayErrorMessage( this, _( "Error saving temporary board file to create design block." ) ); + wxRemoveFile( tempFile ); + return false; + } + + blk->SetBoardFile( tempFile ); + + bool success = false; + + try + { + success = Prj().DesignBlockLibs()->DesignBlockSave( aLibId.GetLibNickname(), blk ) + == DESIGN_BLOCK_LIB_TABLE::SAVE_OK; + } + catch( const IO_ERROR& ioe ) + { + DisplayError( this, ioe.What() ); + } + + // Clean up the temporary file + wxRemoveFile( tempFile ); + + m_designBlocksPane->RefreshLibs(); + m_designBlocksPane->SelectLibId( blk->GetLibId() ); + + return success; +} diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp index e7603021ab..bf69df5243 100644 --- a/pcbnew/pcb_edit_frame.cpp +++ b/pcbnew/pcb_edit_frame.cpp @@ -81,6 +81,7 @@ #include <tools/convert_tool.h> #include <tools/drawing_tool.h> #include <tools/pcb_control.h> +#include <tools/pcb_design_block_control.h> #include <tools/board_editor_control.h> #include <tools/board_inspection_tool.h> #include <tools/pcb_editor_conditions.h> @@ -104,6 +105,7 @@ #include <dialog_drc.h> // for DIALOG_DRC_WINDOW_NAME definition #include <ratsnest/ratsnest_view_item.h> #include <widgets/appearance_controls.h> +#include <widgets/pcb_design_block_pane.h> #include <widgets/pcb_search_pane.h> #include <widgets/wx_infobar.h> #include <widgets/panel_selection_filter.h> @@ -205,6 +207,7 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : m_inspectConstraintsDlg( nullptr ), m_footprintDiffDlg( nullptr ), m_boardSetupDlg( nullptr ), + m_designBlocksPane( nullptr ), m_importProperties( nullptr ), m_eventCounterTimer( nullptr ) { @@ -292,6 +295,7 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : m_appearancePanel = new APPEARANCE_CONTROLS( this, GetCanvas() ); m_searchPane = new PCB_SEARCH_PANE( this ); m_netInspectorPanel = new PCB_NET_INSPECTOR_PANEL( this, this ); + m_designBlocksPane = new PCB_DESIGN_BLOCK_PANE( this, nullptr, m_designBlockHistoryList ); m_auimgr.SetManagedWindow( this ); @@ -341,6 +345,20 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : .FloatingSize( m_selectionFilterPanel->GetBestSize() ) .CloseButton( false ) ); + m_auimgr.AddPane( m_designBlocksPane, EDA_PANE().Name( DesignBlocksPaneName() ) + .Right().Layer( 5 ) + .Caption( _( "Design Blocks" ) ) + .CaptionVisible( true ) + .PaneBorder( true ) + .TopDockable( false ) + .BottomDockable( false ) + .CloseButton( true ) + .MinSize( FromDIP( wxSize( 240, 60 ) ) ) + .BestSize( FromDIP( wxSize( 300, 200 ) ) ) + .FloatingSize( FromDIP( wxSize( 800, 600 ) ) ) + .FloatingPosition( FromDIP( wxPoint( 50, 200 ) ) ) + .Show( true ) ); + m_auimgr.AddPane( m_propertiesPanel, EDA_PANE().Name( PropertiesPaneName() ) .Left().Layer( 5 ) .Caption( _( "Properties" ) ).PaneBorder( false ) @@ -378,6 +396,7 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : m_auimgr.GetPane( PropertiesPaneName() ).Show( GetPcbNewSettings()->m_AuiPanels.show_properties ); m_auimgr.GetPane( NetInspectorPanelName() ).Show( m_show_net_inspector ); m_auimgr.GetPane( SearchPaneName() ).Show( m_show_search ); + m_auimgr.GetPane( DesignBlocksPaneName() ).Show( GetPcbNewSettings()->m_AuiPanels.design_blocks_show ); // The selection filter doesn't need to grow in the vertical direction when docked m_auimgr.GetPane( "SelectionFilter" ).dock_proportion = 0; @@ -388,6 +407,9 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : { wxAuiPaneInfo& layersManager = m_auimgr.GetPane( wxS( "LayersManager" ) ); SetAuiPaneSize( m_auimgr, layersManager, aui_cfg.right_panel_width, -1 ); + + wxAuiPaneInfo& designBlocksPane = m_auimgr.GetPane( DesignBlocksPaneName() ); + SetAuiPaneSize( m_auimgr, designBlocksPane, aui_cfg.design_blocks_panel_docked_width, -1 ); } if( aui_cfg.properties_panel_width > 0 && m_propertiesPanel ) @@ -741,6 +763,7 @@ void PCB_EDIT_FRAME::setupTools() m_toolManager->RegisterTool( new DRAWING_TOOL ); m_toolManager->RegisterTool( new PCB_POINT_EDITOR ); m_toolManager->RegisterTool( new PCB_CONTROL ); + m_toolManager->RegisterTool( new PCB_DESIGN_BLOCK_CONTROL ); m_toolManager->RegisterTool( new BOARD_EDITOR_CONTROL ); m_toolManager->RegisterTool( new BOARD_INSPECTION_TOOL ); m_toolManager->RegisterTool( new BOARD_REANNOTATE_TOOL ); @@ -860,6 +883,13 @@ void PCB_EDIT_FRAME::setupUIConditions() mgr->SetConditions( ACTIONS::toggleBoundingBoxes, CHECK( cond.BoundingBoxes() ) ); + auto hasElements = + [ this ] ( const SELECTION& aSel ) + { + return GetBoard() && + ( !GetBoard()->IsEmpty() || !SELECTION_CONDITIONS::Idle( aSel ) ); + }; + auto constrainedDrawingModeCond = [this]( const SELECTION& ) { @@ -896,6 +926,12 @@ void PCB_EDIT_FRAME::setupUIConditions() return m_auimgr.GetPane( SearchPaneName() ).IsShown(); }; + auto designBlockCond = + [ this ] (const SELECTION& aSel ) + { + return m_auimgr.GetPane( DesignBlocksPaneName() ).IsShown(); + }; + auto highContrastCond = [this] ( const SELECTION& ) { @@ -939,6 +975,10 @@ void PCB_EDIT_FRAME::setupUIConditions() mgr->SetConditions( ACTIONS::showProperties, CHECK( propertiesCond ) ); mgr->SetConditions( PCB_ACTIONS::showNetInspector, CHECK( netInspectorCond ) ); mgr->SetConditions( PCB_ACTIONS::showSearch, CHECK( searchPaneCond ) ); + mgr->SetConditions( PCB_ACTIONS::showDesignBlockPanel, CHECK( designBlockCond ) ); + + mgr->SetConditions( PCB_ACTIONS::saveBoardAsDesignBlock, ENABLE( hasElements ) ); + mgr->SetConditions( PCB_ACTIONS::saveSelectionAsDesignBlock, ENABLE( SELECTION_CONDITIONS::NotEmpty ) ); auto isArcKeepCenterMode = [this]( const SELECTION& ) @@ -1057,6 +1097,7 @@ void PCB_EDIT_FRAME::setupUIConditions() CURRENT_EDIT_TOOL( ACTIONS::embeddedFiles ); CURRENT_EDIT_TOOL( ACTIONS::deleteTool ); CURRENT_EDIT_TOOL( PCB_ACTIONS::placeFootprint ); + CURRENT_EDIT_TOOL( PCB_ACTIONS::placeDesignBlock ); CURRENT_EDIT_TOOL( PCB_ACTIONS::routeSingleTrack); CURRENT_EDIT_TOOL( PCB_ACTIONS::routeDiffPair ); CURRENT_EDIT_TOOL( PCB_ACTIONS::tuneSingleTrack); @@ -1499,6 +1540,19 @@ void PCB_EDIT_FRAME::SaveSettings( APP_SETTINGS_BASE* aCfg ) cfg->m_AuiPanels.appearance_expand_layer_display = m_appearancePanel->IsLayerOptionsExpanded(); cfg->m_AuiPanels.appearance_expand_net_display = m_appearancePanel->IsNetOptionsExpanded(); } + + wxAuiPaneInfo& designBlocksPane = m_auimgr.GetPane( DesignBlocksPaneName() ); + cfg->m_AuiPanels.design_blocks_show = designBlocksPane.IsShown(); + + if( designBlocksPane.IsDocked() ) + cfg->m_AuiPanels.design_blocks_panel_docked_width = m_designBlocksPane->GetSize().x; + else + { + cfg->m_AuiPanels.design_blocks_panel_float_height = designBlocksPane.floating_size.y; + cfg->m_AuiPanels.design_blocks_panel_float_width = designBlocksPane.floating_size.x; + } + + m_designBlocksPane->SaveSettings(); } } diff --git a/pcbnew/pcb_edit_frame.h b/pcbnew/pcb_edit_frame.h index d3d1b1073b..36c31daf06 100644 --- a/pcbnew/pcb_edit_frame.h +++ b/pcbnew/pcb_edit_frame.h @@ -58,6 +58,7 @@ class BOARD_NETLIST_UPDATER; class ACTION_MENU; class TOOL_ACTION; class DIALOG_BOARD_SETUP; +class PCB_DESIGN_BLOCK_PANE; #ifdef KICAD_IPC_API class KICAD_API_SERVER; @@ -318,6 +319,8 @@ public: bool IsSearchPaneShown() { return m_auimgr.GetPane( SearchPaneName() ).IsShown(); } void FocusSearch(); + void ToggleLibraryTree() override; + /** * Create an ASCII footprint position file. * @@ -465,6 +468,16 @@ public: */ void RecreateCmpFileFromBoard( wxCommandEvent& aEvent ); + bool SaveBoardAsDesignBlock( const wxString& aLibraryName ); + + bool SaveSelectionAsDesignBlock( const wxString& aLibraryName ); + + bool SaveBoardToDesignBlock( const LIB_ID& aLibId ); + + bool SaveSelectionToDesignBlock( const LIB_ID& aLibId ); + + PCB_DESIGN_BLOCK_PANE* GetDesignBlockPane() const { return m_designBlocksPane; } + /** * Save footprints in a library: * @@ -829,6 +842,18 @@ protected: bool importFile( const wxString& aFileName, int aFileType, const std::map<std::string, UTF8>* aProperties = nullptr ); + /** + * @brief Save a board object to a file + * + * @param aBoard The board object to save + * @param aFileName The file name to save the board to + * @param aHeadless If true, suppresses informational output (e.g. to be used from the API) + * + * @return + */ + bool saveBoardAsFile( BOARD* aBoard, const wxString& aFileName, bool aHeadless = false ); + + bool canCloseWindow( wxCloseEvent& aCloseEvent ) override; void doCloseWindow() override; @@ -881,6 +906,9 @@ private: DIALOG_BOOK_REPORTER* m_footprintDiffDlg; DIALOG_BOARD_SETUP* m_boardSetupDlg; + std::vector<LIB_ID> m_designBlockHistoryList; + PCB_DESIGN_BLOCK_PANE* m_designBlocksPane; + const std::map<std::string, UTF8>* m_importProperties; // Properties used for non-KiCad import. /** diff --git a/pcbnew/pcbnew_settings.cpp b/pcbnew/pcbnew_settings.cpp index 3db86a2175..19ac27c08e 100644 --- a/pcbnew/pcbnew_settings.cpp +++ b/pcbnew/pcbnew_settings.cpp @@ -132,6 +132,17 @@ PCBNEW_SETTINGS::PCBNEW_SETTINGS() m_params.emplace_back( new PARAM<bool>( "aui.show_net_inspector", &m_AuiPanels.show_net_inspector, false ) ); + m_params.emplace_back( new PARAM<bool>( "aui.design_blocks_show", &m_AuiPanels.design_blocks_show, false ) ); + + m_params.emplace_back( new PARAM<int>( "aui.design_blocks_panel_docked_width", + &m_AuiPanels.design_blocks_panel_docked_width, -1 ) ); + + m_params.emplace_back( + new PARAM<int>( "aui.design_blocks_panel_float_width", &m_AuiPanels.design_blocks_panel_float_width, -1 ) ); + + m_params.emplace_back( new PARAM<int>( "aui.design_blocks_panel_float_height", + &m_AuiPanels.design_blocks_panel_float_height, -1 ) ); + m_params.emplace_back( new PARAM<int>( "footprint_chooser.width", &m_FootprintChooser.width, -1 ) ); diff --git a/pcbnew/pcbnew_settings.h b/pcbnew/pcbnew_settings.h index 102f4064b6..29d4325f89 100644 --- a/pcbnew/pcbnew_settings.h +++ b/pcbnew/pcbnew_settings.h @@ -155,6 +155,10 @@ public: bool show_properties; bool show_search; bool show_net_inspector; + bool design_blocks_show; + int design_blocks_panel_docked_width; + int design_blocks_panel_float_width; + int design_blocks_panel_float_height; }; struct DIALOG_CLEANUP diff --git a/pcbnew/toolbars_pcb_editor.cpp b/pcbnew/toolbars_pcb_editor.cpp index d6b143861c..65e79ef0bd 100644 --- a/pcbnew/toolbars_pcb_editor.cpp +++ b/pcbnew/toolbars_pcb_editor.cpp @@ -50,6 +50,7 @@ #include <tools/pcb_actions.h> #include <tools/pcb_selection_tool.h> #include <widgets/appearance_controls.h> +#include <widgets/pcb_design_block_pane.h> #include <widgets/layer_box_selector.h> #include <widgets/layer_presentation.h> #include <widgets/pcb_properties_panel.h> @@ -701,3 +702,44 @@ void PCB_EDIT_FRAME::OnUpdateSelectViaSize( wxUpdateUIEvent& aEvent ) m_SelViaSizeBox->SetSelection( sel ); } } + + +void PCB_EDIT_FRAME::ToggleLibraryTree() +{ + PCBNEW_SETTINGS* cfg = GetPcbNewSettings(); + + wxCHECK( cfg, /* void */ ); + + wxAuiPaneInfo& db_library_pane = m_auimgr.GetPane( DesignBlocksPaneName() ); + + db_library_pane.Show( !db_library_pane.IsShown() ); + + if( db_library_pane.IsShown() ) + { + if( db_library_pane.IsFloating() ) + { + db_library_pane.FloatingSize( cfg->m_AuiPanels.design_blocks_panel_float_width, + cfg->m_AuiPanels.design_blocks_panel_float_height ); + m_auimgr.Update(); + } + else if( cfg->m_AuiPanels.design_blocks_panel_docked_width > 0 ) + { + // SetAuiPaneSize also updates m_auimgr + SetAuiPaneSize( m_auimgr, db_library_pane, cfg->m_AuiPanels.design_blocks_panel_docked_width, -1 ); + } + } + else + { + if( db_library_pane.IsFloating() ) + { + cfg->m_AuiPanels.design_blocks_panel_float_width = db_library_pane.floating_size.x; + cfg->m_AuiPanels.design_blocks_panel_float_height = db_library_pane.floating_size.y; + } + else + { + cfg->m_AuiPanels.design_blocks_panel_docked_width = m_designBlocksPane->GetSize().x; + } + + m_auimgr.Update(); + } +} diff --git a/pcbnew/tools/board_editor_control.cpp b/pcbnew/tools/board_editor_control.cpp index 201039edd3..03047911fa 100644 --- a/pcbnew/tools/board_editor_control.cpp +++ b/pcbnew/tools/board_editor_control.cpp @@ -709,6 +709,13 @@ int BOARD_EDITOR_CONTROL::ToggleNetInspector( const TOOL_EVENT& aEvent ) } +int BOARD_EDITOR_CONTROL::ToggleLibraryTree( const TOOL_EVENT& aEvent ) +{ + getEditFrame<PCB_EDIT_FRAME>()->ToggleLibraryTree(); + return 0; +} + + int BOARD_EDITOR_CONTROL::ToggleSearch( const TOOL_EVENT& aEvent ) { getEditFrame<PCB_EDIT_FRAME>()->ToggleSearch(); @@ -1771,6 +1778,7 @@ void BOARD_EDITOR_CONTROL::setTransitions() Go( &BOARD_EDITOR_CONTROL::ToggleLayersManager, PCB_ACTIONS::showLayersManager.MakeEvent() ); Go( &BOARD_EDITOR_CONTROL::ToggleProperties, ACTIONS::showProperties.MakeEvent() ); Go( &BOARD_EDITOR_CONTROL::ToggleNetInspector, PCB_ACTIONS::showNetInspector.MakeEvent() ); + Go( &BOARD_EDITOR_CONTROL::ToggleLibraryTree, PCB_ACTIONS::showDesignBlockPanel.MakeEvent() ); Go( &BOARD_EDITOR_CONTROL::ToggleSearch, PCB_ACTIONS::showSearch.MakeEvent() ); Go( &BOARD_EDITOR_CONTROL::TogglePythonConsole, PCB_ACTIONS::showPythonConsole.MakeEvent() ); Go( &BOARD_EDITOR_CONTROL::RepairBoard, PCB_ACTIONS::repairBoard.MakeEvent() ); diff --git a/pcbnew/tools/board_editor_control.h b/pcbnew/tools/board_editor_control.h index bc3e7d1e68..10c30ad940 100644 --- a/pcbnew/tools/board_editor_control.h +++ b/pcbnew/tools/board_editor_control.h @@ -82,6 +82,7 @@ public: int ToggleNetInspector( const TOOL_EVENT& aEvent ); int ToggleSearch( const TOOL_EVENT& aEvent ); int TogglePythonConsole( const TOOL_EVENT& aEvent ); + int ToggleLibraryTree( const TOOL_EVENT& aEvent ); // Track & via size control int TrackWidthInc( const TOOL_EVENT& aEvent ); diff --git a/pcbnew/tools/pcb_actions.cpp b/pcbnew/tools/pcb_actions.cpp index e5e4cc052e..ff02f62c76 100644 --- a/pcbnew/tools/pcb_actions.cpp +++ b/pcbnew/tools/pcb_actions.cpp @@ -444,6 +444,65 @@ TOOL_ACTION PCB_ACTIONS::runDRC( TOOL_ACTION_ARGS() .Tooltip( _( "Show the design rules checker window" ) ) .Icon( BITMAPS::erc ) ); +// PCB_DESIGN_BLOCK_CONTROL +TOOL_ACTION PCB_ACTIONS::placeDesignBlock( TOOL_ACTION_ARGS() + .Name( "pcbnew.InteractiveDrawing.placeDesignBlock" ) + .Scope( AS_GLOBAL ) + .DefaultHotkey( MD_SHIFT + 'B' ) + .FriendlyName( _( "Place Design Block" ) ) + .Tooltip( _( "Add selected design block to current board" ) ) + .Icon( BITMAPS::add_component ) + .Flags( AF_ACTIVATE ) + .Parameter<DESIGN_BLOCK*>( nullptr ) ); + +TOOL_ACTION PCB_ACTIONS::showDesignBlockPanel( TOOL_ACTION_ARGS() + .Name( "pcbnew.PcbDesignBlockControl.showDesignBlockPanel" ) + .Scope( AS_GLOBAL ) + .FriendlyName( _( "Design Blocks" ) ) + .Tooltip( _( "Show/hide design blocks library" ) ) + .Icon( BITMAPS::search_tree ) ); + +TOOL_ACTION PCB_ACTIONS::saveBoardAsDesignBlock( TOOL_ACTION_ARGS() + .Name( "pcbnew.PcbDesignBlockControl.saveBoardAsDesignBlock" ) + .Scope( AS_GLOBAL ) + .FriendlyName( _( "Save Current Board as Design Block..." ) ) + .Tooltip( _( "Create a new design block from the current board" ) ) + .Icon( BITMAPS::new_component ) ); + +TOOL_ACTION PCB_ACTIONS::saveSelectionAsDesignBlock( TOOL_ACTION_ARGS() + .Name( "pcbnew.PcbDesignBlockControl.saveSelectionAsDesignBlock" ) + .Scope( AS_GLOBAL ) + .FriendlyName( _( "Save Selection as Design Block..." ) ) + .Tooltip( _( "Create a new design block from the current selection" ) ) + .Icon( BITMAPS::new_component ) ); + +TOOL_ACTION PCB_ACTIONS::saveBoardToDesignBlock( TOOL_ACTION_ARGS() + .Name( "pcbnew.PcbDesignBlockControl.saveBoardToDesignBlock" ) + .Scope( AS_GLOBAL ) + .FriendlyName( _( "Save Current Board to Design Block..." ) ) + .Tooltip( _( "Add current board to design block" ) ) + .Icon( BITMAPS::save ) ); + +TOOL_ACTION PCB_ACTIONS::saveSelectionToDesignBlock( TOOL_ACTION_ARGS() + .Name( "pcbnew.PcbDesignBlockControl.saveSelectionToDesignBlock" ) + .Scope( AS_GLOBAL ) + .FriendlyName( _( "Save Selection to Design Block..." ) ) + .Tooltip( _( "Add current selection to design block" ) ) + .Icon( BITMAPS::save ) ); + +TOOL_ACTION PCB_ACTIONS::deleteDesignBlock( TOOL_ACTION_ARGS() + .Name( "pcbnew.PcbDesignBlockControl.saveDeleteDesignBlock" ) + .Scope( AS_GLOBAL ) + .FriendlyName( _( "Delete Design Block" ) ) + .Tooltip( _( "Remove the selected design block from its library" ) ) + .Icon( BITMAPS::trash ) ); + +TOOL_ACTION PCB_ACTIONS::editDesignBlockProperties( TOOL_ACTION_ARGS() + .Name( "pcbnew.PcbDesignBlockControl.editDesignBlockProperties" ) + .Scope( AS_GLOBAL ) + .FriendlyName( _( "Properties..." ) ) + .Tooltip( _( "Edit properies of design block" ) ) + .Icon( BITMAPS::edit ) ); // EDIT_TOOL // diff --git a/pcbnew/tools/pcb_actions.h b/pcbnew/tools/pcb_actions.h index 356c732108..44efb1ebdd 100644 --- a/pcbnew/tools/pcb_actions.h +++ b/pcbnew/tools/pcb_actions.h @@ -39,6 +39,7 @@ enum class ZONE_MODE GRAPHIC_POLYGON }; +class DESIGN_BLOCK; /** * Gather all the actions that are shared by tools. @@ -459,6 +460,16 @@ public: static TOOL_ACTION showPythonConsole; static TOOL_ACTION zonesManager; + // Design Block management + static TOOL_ACTION placeDesignBlock; + static TOOL_ACTION showDesignBlockPanel; + static TOOL_ACTION saveBoardAsDesignBlock; + static TOOL_ACTION saveSelectionAsDesignBlock; + static TOOL_ACTION saveBoardToDesignBlock; + static TOOL_ACTION saveSelectionToDesignBlock; + static TOOL_ACTION deleteDesignBlock; + static TOOL_ACTION editDesignBlockProperties; + // Footprint editor tools // We don't use ACTION::new here because we need to distinguish between New Library diff --git a/pcbnew/tools/pcb_control.cpp b/pcbnew/tools/pcb_control.cpp index 822848dc95..d97afa9861 100644 --- a/pcbnew/tools/pcb_control.cpp +++ b/pcbnew/tools/pcb_control.cpp @@ -40,6 +40,7 @@ #include <board_design_settings.h> #include <board_item.h> #include <clipboard.h> +#include <design_block.h> #include <dialogs/dialog_paste_special.h> #include <pcb_dimension.h> #include <gal/graphics_abstraction_layer.h> @@ -69,6 +70,7 @@ #include <footprint_editor_settings.h> #include <footprint_viewer_frame.h> #include <widgets/appearance_controls.h> +#include <widgets/pcb_design_block_pane.h> #include <widgets/wx_progress_reporters.h> #include <widgets/wx_infobar.h> #include <wx/hyperlink.h> @@ -1306,6 +1308,48 @@ int PCB_CONTROL::AppendBoardFromFile( const TOOL_EVENT& aEvent ) } +int PCB_CONTROL::AppendDesignBlock( const TOOL_EVENT& aEvent ) +{ + PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame ); + + if( !editFrame ) + return 1; + + DESIGN_BLOCK* designBlock = nullptr; + + if( !editFrame->GetDesignBlockPane()->GetSelectedLibId().IsValid() ) + return 1; + + + designBlock = editFrame->GetDesignBlockPane()->GetDesignBlock( editFrame->GetDesignBlockPane()->GetSelectedLibId(), + true, true ); + + if( !designBlock || designBlock->GetBoardFile().IsEmpty() ) + return 1; + + + PCB_IO_MGR::PCB_FILE_T pluginType = PCB_IO_MGR::KICAD_SEXP; + IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::PluginFind( pluginType ) ); + + if( !pi ) + return 1; + + bool repeatPlacement = false; + + if( APP_SETTINGS_BASE* cfg = editFrame->config() ) + repeatPlacement = cfg->m_DesignBlockChooserPanel.repeated_placement; + + int ret = 0; + + do + { + ret = AppendBoard( *pi, designBlock->GetBoardFile() ); + } while( repeatPlacement && ret == 0 ); + + return ret; +} + + template<typename T> static void moveUnflaggedItems( const std::deque<T>& aList, std::vector<BOARD_ITEM*>& aTarget, bool aIsNew ) @@ -1477,7 +1521,7 @@ bool PCB_CONTROL::placeBoardItems( BOARD_COMMIT* aCommit, std::vector<BOARD_ITEM } -int PCB_CONTROL::AppendBoard( PCB_IO& pi, wxString& fileName ) +int PCB_CONTROL::AppendBoard( PCB_IO& pi, const wxString& fileName ) { PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame ); @@ -1585,15 +1629,64 @@ int PCB_CONTROL::AppendBoard( PCB_IO& pi, wxString& fileName ) brd->SetEnabledLayers( enabledLayers ); brd->SetVisibleLayers( enabledLayers ); + int ret = 0; + + bool placeAsGroup = editFrame->config() ? editFrame->config()->m_DesignBlockChooserPanel.place_as_sheet : false; + if( placeBoardItems( &commit, brd, false, false /* Don't reannotate dupes on Append Board */ ) ) + { commit.Push( _( "Append Board" ) ); + + if( placeAsGroup ) + { + BOARD_COMMIT grpCommit( m_toolMgr ); + PCB_GROUP* group = new PCB_GROUP( brd ); + + // Get the selection tool selection + PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>(); + PCB_SELECTION selection = selTool->GetSelection(); + + for( EDA_ITEM* eda_item : selection ) + { + if( eda_item->IsBOARD_ITEM() ) + { + if( static_cast<BOARD_ITEM*>( eda_item )->IsLocked() ) + group->SetLocked( true ); + } + } + + grpCommit.Add( group ); + + for( EDA_ITEM* eda_item : selection ) + { + if( eda_item->IsBOARD_ITEM() && !static_cast<BOARD_ITEM*>( eda_item )->GetParentFootprint() ) + { + grpCommit.Stage( static_cast<BOARD_ITEM*>( eda_item ), CHT_GROUP ); + } + } + + grpCommit.Push( _( "Group Items" ), APPEND_UNDO ); + + selTool->ClearSelection(); + selTool->select( group ); + + m_toolMgr->PostEvent( EVENTS::SelectedItemsModified ); + m_frame->OnModify(); + m_frame->Refresh(); + } + + ret = 0; + } else + { commit.Revert(); + ret = 1; + } // Refresh the UI for the updated board properties editFrame->GetAppearancePanel()->OnBoardChanged(); - return 0; + return ret; } @@ -2093,6 +2186,7 @@ void PCB_CONTROL::setTransitions() Go( &PCB_CONTROL::InteractiveDelete, ACTIONS::deleteTool.MakeEvent() ); // Append control + Go( &PCB_CONTROL::AppendDesignBlock, PCB_ACTIONS::placeDesignBlock.MakeEvent() ); Go( &PCB_CONTROL::AppendBoardFromFile, PCB_ACTIONS::appendBoard.MakeEvent() ); Go( &PCB_CONTROL::DdAppendBoard, PCB_ACTIONS::ddAppendBoard.MakeEvent() ); @@ -2110,4 +2204,4 @@ void PCB_CONTROL::setTransitions() Go( &PCB_CONTROL::DdAddLibrary, ACTIONS::ddAddLibrary.MakeEvent() ); Go( &PCB_CONTROL::DdImportFootprint, PCB_ACTIONS::ddImportFootprint.MakeEvent() ); } -// clang-format on \ No newline at end of file +// clang-format on diff --git a/pcbnew/tools/pcb_control.h b/pcbnew/tools/pcb_control.h index 300a48533a..b0caf56ca3 100644 --- a/pcbnew/tools/pcb_control.h +++ b/pcbnew/tools/pcb_control.h @@ -106,7 +106,8 @@ public: int InteractiveDelete( const TOOL_EVENT& aEvent ); int Paste( const TOOL_EVENT& aEvent ); int AppendBoardFromFile( const TOOL_EVENT& aEvent ); - int AppendBoard( PCB_IO& pi, wxString& fileName ); + int AppendDesignBlock( const TOOL_EVENT& aEvent ); + int AppendBoard( PCB_IO& pi, const wxString& fileName ); int UpdateMessagePanel( const TOOL_EVENT& aEvent ); int FlipPcbView( const TOOL_EVENT& aEvent ); @@ -142,7 +143,7 @@ private: * @param aAnchorAtOrigin = true if the items are translated so that the anchor is {0, 0} * (if false, the top-left item's origin will be used) * @param aReannotateDuplicates = true to reannotate any footprints with a designator - that already exist in the board. + * that already exist in the board. */ bool placeBoardItems( BOARD_COMMIT* aCommit, std::vector<BOARD_ITEM*>& aItems, bool aIsNew, bool aAnchorAtOrigin, bool aReannotateDuplicates ); diff --git a/pcbnew/tools/pcb_design_block_control.cpp b/pcbnew/tools/pcb_design_block_control.cpp new file mode 100644 index 0000000000..aa34640cfd --- /dev/null +++ b/pcbnew/tools/pcb_design_block_control.cpp @@ -0,0 +1,165 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright The KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +#include <dialog_design_block_properties.h> +#include <pcb_edit_frame.h> +#include <tool/tool_manager.h> +#include <tools/pcb_actions.h> +#include <tools/pcb_selection_tool.h> +#include <tools/pcb_design_block_control.h> +#include <widgets/pcb_design_block_pane.h> +#include <widgets/panel_design_block_chooser.h> + + +PCB_DESIGN_BLOCK_CONTROL::~PCB_DESIGN_BLOCK_CONTROL() +{ +} + + +bool PCB_DESIGN_BLOCK_CONTROL::Init() +{ + m_editFrame = getEditFrame<PCB_EDIT_FRAME>(); + m_frame = m_editFrame; + m_framesToNotify = { FRAME_SCH }; + + auto isInLibrary = + [this](const SELECTION& aSel ) + { + return this->selIsInLibrary(aSel); + }; + + auto isDesignBlock = + [this](const SELECTION& aSel ) + { + return this->selIsDesignBlock(aSel); + }; + + auto hasSelection = + [this](const SELECTION& aSel ) + { + return !m_editFrame->GetCurrentSelection().Empty(); + }; + + CONDITIONAL_MENU& ctxMenu = m_menu->GetMenu(); + AddContextMenuItems( &ctxMenu ); + + ctxMenu.AddItem( PCB_ACTIONS::placeDesignBlock, isDesignBlock, 50 ); + ctxMenu.AddSeparator( 50 ); + + ctxMenu.AddItem( PCB_ACTIONS::editDesignBlockProperties, isDesignBlock, 100 ); + ctxMenu.AddItem( PCB_ACTIONS::saveBoardAsDesignBlock, isInLibrary, 100 ); + ctxMenu.AddItem( PCB_ACTIONS::saveSelectionAsDesignBlock, isInLibrary && hasSelection, 100 ); + ctxMenu.AddItem( PCB_ACTIONS::saveBoardToDesignBlock, isDesignBlock, 100 ); + ctxMenu.AddItem( PCB_ACTIONS::saveSelectionToDesignBlock, isDesignBlock && hasSelection, 100 ); + ctxMenu.AddItem( PCB_ACTIONS::deleteDesignBlock, isDesignBlock, 100 ); + ctxMenu.AddSeparator( 100 ); + + return true; +} + + +int PCB_DESIGN_BLOCK_CONTROL::SaveBoardAsDesignBlock( const TOOL_EVENT& aEvent ) +{ + LIB_TREE_NODE* current = getCurrentTreeNode(); + + if( !current ) + return -1; + + if( !m_editFrame->SaveBoardAsDesignBlock( current->m_LibId.GetLibNickname() ) ) + return -1; + + notifyOtherFrames(); + + return 0; +} + + +int PCB_DESIGN_BLOCK_CONTROL::SaveSelectionAsDesignBlock( const TOOL_EVENT& aEvent ) +{ + LIB_TREE_NODE* current = getCurrentTreeNode(); + + if( !current ) + return -1; + + if( !m_editFrame->SaveSelectionAsDesignBlock( current->m_LibId.GetLibNickname() ) ) + return -1; + + notifyOtherFrames(); + + return 0; +} + + +int PCB_DESIGN_BLOCK_CONTROL::SaveBoardToDesignBlock( const TOOL_EVENT& aEvent ) +{ + LIB_TREE_NODE* current = getCurrentTreeNode(); + + if( !current ) + return -1; + + if( !m_editFrame->SaveBoardToDesignBlock( current->m_LibId ) ) + return -1; + + notifyOtherFrames(); + + return 0; +} + + +int PCB_DESIGN_BLOCK_CONTROL::SaveSelectionToDesignBlock( const TOOL_EVENT& aEvent ) +{ + LIB_TREE_NODE* current = getCurrentTreeNode(); + + if( !current ) + return -1; + + if( !m_editFrame->SaveSelectionToDesignBlock( current->m_LibId ) ) + return -1; + + notifyOtherFrames(); + + return 0; +} + +void PCB_DESIGN_BLOCK_CONTROL::setTransitions() +{ + Go( &PCB_DESIGN_BLOCK_CONTROL::SaveBoardAsDesignBlock, PCB_ACTIONS::saveBoardAsDesignBlock.MakeEvent() ); + Go( &PCB_DESIGN_BLOCK_CONTROL::SaveSelectionAsDesignBlock, PCB_ACTIONS::saveSelectionAsDesignBlock.MakeEvent() ); + Go( &PCB_DESIGN_BLOCK_CONTROL::SaveBoardToDesignBlock, PCB_ACTIONS::saveBoardToDesignBlock.MakeEvent() ); + Go( &PCB_DESIGN_BLOCK_CONTROL::SaveSelectionToDesignBlock, PCB_ACTIONS::saveSelectionToDesignBlock.MakeEvent() ); + Go( &PCB_DESIGN_BLOCK_CONTROL::DeleteDesignBlock, PCB_ACTIONS::deleteDesignBlock.MakeEvent() ); + Go( &PCB_DESIGN_BLOCK_CONTROL::EditDesignBlockProperties, PCB_ACTIONS::editDesignBlockProperties.MakeEvent() ); +} + + +LIB_ID PCB_DESIGN_BLOCK_CONTROL::getSelectedLibId() +{ + getDesignBlockPane()->GetSelectedLibId(); + + return LIB_ID(); +} + + +DESIGN_BLOCK_PANE* PCB_DESIGN_BLOCK_CONTROL::getDesignBlockPane() +{ + return m_editFrame->GetDesignBlockPane(); +} diff --git a/pcbnew/tools/pcb_design_block_control.h b/pcbnew/tools/pcb_design_block_control.h new file mode 100644 index 0000000000..69c9e14c1d --- /dev/null +++ b/pcbnew/tools/pcb_design_block_control.h @@ -0,0 +1,62 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright The KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + +#ifndef PCB_DESIGN_BLOCK_CONTROL_H +#define PCB_DESIGN_BLOCK_CONTROL_H + +#include <tool/design_block_control.h> + +class DESIGN_BLOCK_PANE; +class PCB_EDIT_FRAME; + +/** + * Handle design block actions in the PCB editor. + */ +class PCB_DESIGN_BLOCK_CONTROL : public DESIGN_BLOCK_CONTROL +{ +public: + PCB_DESIGN_BLOCK_CONTROL() : DESIGN_BLOCK_CONTROL( "pcbnew.PcbDesignBlockControl" ) {} + virtual ~PCB_DESIGN_BLOCK_CONTROL(); + + /// @copydoc TOOL_INTERACTIVE::Init() + bool Init() override; + + int SaveBoardAsDesignBlock( const TOOL_EVENT& aEvent ); + int SaveSelectionAsDesignBlock( const TOOL_EVENT& aEvent ); + + int SaveBoardToDesignBlock( const TOOL_EVENT& aEvent ); + int SaveSelectionToDesignBlock( const TOOL_EVENT& aEvent ); + +private: + LIB_ID getSelectedLibId(); + ///< Set up handlers for various events. + void setTransitions() override; + + DESIGN_BLOCK_PANE* getDesignBlockPane() override; + + PCB_EDIT_FRAME* m_editFrame = nullptr; +}; + + +#endif diff --git a/pcbnew/widgets/pcb_design_block_pane.cpp b/pcbnew/widgets/pcb_design_block_pane.cpp new file mode 100644 index 0000000000..0f3bebed75 --- /dev/null +++ b/pcbnew/widgets/pcb_design_block_pane.cpp @@ -0,0 +1,164 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright The KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <design_block.h> +#include <widgets/pcb_design_block_pane.h> +#include <widgets/pcb_design_block_preview_widget.h> +#include <widgets/panel_design_block_chooser.h> +#include <pcbnew_settings.h> +#include <kiface_base.h> +#include <pcb_edit_frame.h> +#include <core/kicad_algo.h> +#include <template_fieldnames.h> +#include <wx/button.h> +#include <wx/checkbox.h> +#include <wx/sizer.h> +#include <confirm.h> +#include <wildcards_and_files_ext.h> +#include <tool/tool_manager.h> +#include <tools/pcb_actions.h> +#include <tools/pcb_design_block_control.h> + +// Do not make these static wxStrings; they need to respond to language changes +#define REPEATED_PLACEMENT _( "Place repeated copies" ) +#define PLACE_AS_GROUP _( "Place as group" ) +#define KEEP_ANNOTATIONS _( "Keep annotations" ) + +PCB_DESIGN_BLOCK_PANE::PCB_DESIGN_BLOCK_PANE( PCB_EDIT_FRAME* aParent, const LIB_ID* aPreselect, + std::vector<LIB_ID>& aHistoryList ) : + DESIGN_BLOCK_PANE( aParent, aPreselect, aHistoryList ) +{ + wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL ); + m_chooserPanel = new PANEL_DESIGN_BLOCK_CHOOSER( aParent, this, aHistoryList, + [aParent]() + { + aParent->GetToolManager()->RunAction( + PCB_ACTIONS::placeDesignBlock ); + }, + aParent->GetToolManager()->GetTool<PCB_DESIGN_BLOCK_CONTROL>() + ); + m_chooserPanel->SetPreviewWidget(new PCB_DESIGN_BLOCK_PREVIEW_WIDGET( m_chooserPanel->GetDetailsPanel(), aParent ) ); + sizer->Add( m_chooserPanel, 1, wxEXPAND, 5 ); + + if( aPreselect && aPreselect->IsValid() ) + m_chooserPanel->SetPreselect( *aPreselect ); + + SetName( wxT( "Design Blocks" ) ); + + wxBoxSizer* cbSizer = new wxBoxSizer( wxVERTICAL ); + + m_repeatedPlacement = new wxCheckBox( this, wxID_ANY, REPEATED_PLACEMENT ); + m_placeAsGroup = new wxCheckBox( this, wxID_ANY, PLACE_AS_GROUP ); + m_keepAnnotations = new wxCheckBox( this, wxID_ANY, KEEP_ANNOTATIONS ); + setLabelsAndTooltips(); + UpdateCheckboxes(); + + // Set all checkbox handlers to the same function + m_repeatedPlacement->Bind( wxEVT_CHECKBOX, &PCB_DESIGN_BLOCK_PANE::OnCheckBox, this ); + m_placeAsGroup->Bind( wxEVT_CHECKBOX, &PCB_DESIGN_BLOCK_PANE::OnCheckBox, this ); + m_keepAnnotations->Bind( wxEVT_CHECKBOX, &PCB_DESIGN_BLOCK_PANE::OnCheckBox, this ); + + cbSizer->Add( m_repeatedPlacement, 0, wxTOP | wxLEFT, 2 ); + cbSizer->Add( m_placeAsGroup, 0, wxTOP | wxLEFT, 2 ); + cbSizer->Add( m_keepAnnotations, 0, wxTOP | wxLEFT | wxBOTTOM, 2 ); + + sizer->Add( cbSizer, 0, wxEXPAND, 5 ); + SetSizer( sizer ); + + m_chooserPanel->FinishSetup(); + Layout(); + + Bind( wxEVT_CHAR_HOOK, &PANEL_DESIGN_BLOCK_CHOOSER::OnChar, m_chooserPanel ); +} + + +void PCB_DESIGN_BLOCK_PANE::setLabelsAndTooltips() +{ + if( m_repeatedPlacement ) + { + m_repeatedPlacement->SetLabel( REPEATED_PLACEMENT ); + m_repeatedPlacement->SetToolTip( _( "Place copies of the design block on subsequent " + "clicks." ) ); + } + + if( m_placeAsGroup ) + { + m_placeAsGroup->SetLabel( PLACE_AS_GROUP ); + m_placeAsGroup->SetToolTip( _( "Place the design block as a group." ) ); + } + + if( m_keepAnnotations ) + { + m_keepAnnotations->SetLabel( KEEP_ANNOTATIONS ); + m_keepAnnotations->SetToolTip( _( "Preserve reference designators in the source " + "layout. Otherwise, clear them." ) ); + } +} + +void PCB_DESIGN_BLOCK_PANE::OnCheckBox( wxCommandEvent& aEvent ) +{ + if( PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() ) ) + { + cfg->m_DesignBlockChooserPanel.repeated_placement = m_repeatedPlacement->GetValue(); + cfg->m_DesignBlockChooserPanel.place_as_sheet = m_placeAsGroup->GetValue(); + cfg->m_DesignBlockChooserPanel.keep_annotations = m_keepAnnotations->GetValue(); + } +} + + +void PCB_DESIGN_BLOCK_PANE::UpdateCheckboxes() +{ + if( PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() ) ) + { + m_repeatedPlacement->SetValue( cfg->m_DesignBlockChooserPanel.repeated_placement ); + m_placeAsGroup->SetValue( cfg->m_DesignBlockChooserPanel.place_as_sheet ); + m_keepAnnotations->SetValue( cfg->m_DesignBlockChooserPanel.keep_annotations ); + } +} + + +FILEDLG_IMPORT_SHEET_CONTENTS::FILEDLG_IMPORT_SHEET_CONTENTS( PCBNEW_SETTINGS* aSettings ) : + m_cbRepeatedPlacement( nullptr ), m_cbPlaceAsSheet( nullptr ), m_cbKeepAnnotations( nullptr ) +{ + wxASSERT( aSettings ); + m_settings = aSettings; +}; + + +void FILEDLG_IMPORT_SHEET_CONTENTS::TransferDataFromCustomControls() +{ + m_settings->m_DesignBlockChooserPanel.repeated_placement = m_cbRepeatedPlacement->GetValue(); + m_settings->m_DesignBlockChooserPanel.place_as_sheet = m_cbPlaceAsSheet->GetValue(); + m_settings->m_DesignBlockChooserPanel.keep_annotations = m_cbKeepAnnotations->GetValue(); +} + + +void FILEDLG_IMPORT_SHEET_CONTENTS::AddCustomControls( wxFileDialogCustomize& customizer ) +{ + m_cbRepeatedPlacement = customizer.AddCheckBox( REPEATED_PLACEMENT ); + m_cbRepeatedPlacement->SetValue( m_settings->m_DesignBlockChooserPanel.repeated_placement ); + m_cbPlaceAsSheet = customizer.AddCheckBox( PLACE_AS_GROUP ); + m_cbPlaceAsSheet->SetValue( m_settings->m_DesignBlockChooserPanel.place_as_sheet ); + m_cbKeepAnnotations = customizer.AddCheckBox( KEEP_ANNOTATIONS ); + m_cbKeepAnnotations->SetValue( m_settings->m_DesignBlockChooserPanel.keep_annotations ); +} diff --git a/pcbnew/widgets/pcb_design_block_pane.h b/pcbnew/widgets/pcb_design_block_pane.h new file mode 100644 index 0000000000..c9c8b20d63 --- /dev/null +++ b/pcbnew/widgets/pcb_design_block_pane.h @@ -0,0 +1,80 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright The KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +#ifndef PCB_DESIGN_BLOCK_PANE_H +#define PCB_DESIGN_BLOCK_PANE_H + +#include <widgets/design_block_pane.h> +#include <design_block_tree_model_adapter.h> +#include <widgets/html_window.h> +#include <widgets/wx_panel.h> +#include <wx/checkbox.h> +#include <wx/filedlgcustomize.h> +#include <pcbnew_settings.h> + + +class PCB_EDIT_FRAME; +class PANEL_DESIGN_BLOCK_CHOOSER; + + +class PCB_DESIGN_BLOCK_PANE : public DESIGN_BLOCK_PANE +{ +public: + PCB_DESIGN_BLOCK_PANE( PCB_EDIT_FRAME* aParent, const LIB_ID* aPreselect, std::vector<LIB_ID>& aHistoryList ); + + void OnCheckBox( wxCommandEvent& aEvent ); + void UpdateCheckboxes(); + + void OnSaveSheetAsDesignBlock( wxCommandEvent& aEvent ); + void OnSaveSelectionAsDesignBlock( wxCommandEvent& aEvent ); + +protected: + void setLabelsAndTooltips() override; + + wxCheckBox* m_repeatedPlacement; + wxCheckBox* m_placeAsGroup; + wxCheckBox* m_keepAnnotations; +}; + + +// This is a helper class for the file dialog to allow the user to choose similar options +// as the design block chooser when importing a sheet. +class FILEDLG_IMPORT_SHEET_CONTENTS : public wxFileDialogCustomizeHook +{ +public: + FILEDLG_IMPORT_SHEET_CONTENTS( PCBNEW_SETTINGS* aSettings ); + + void AddCustomControls( wxFileDialogCustomize& customizer ) override; + + void TransferDataFromCustomControls() override; + +private: + PCBNEW_SETTINGS* m_settings; + + wxFileDialogCheckBox* m_cbRepeatedPlacement; + wxFileDialogCheckBox* m_cbPlaceAsSheet; + wxFileDialogCheckBox* m_cbKeepAnnotations; + + wxDECLARE_NO_COPY_CLASS( FILEDLG_IMPORT_SHEET_CONTENTS ); +}; + +#endif diff --git a/pcbnew/widgets/pcb_design_block_preview_widget.cpp b/pcbnew/widgets/pcb_design_block_preview_widget.cpp new file mode 100644 index 0000000000..383f617d45 --- /dev/null +++ b/pcbnew/widgets/pcb_design_block_preview_widget.cpp @@ -0,0 +1,250 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright The KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <board.h> +#include <confirm.h> +#include <pcb_view.h> +#include <pcb_screen.h> +#include <gal/gal_display_options.h> +#include <gal/graphics_abstraction_layer.h> +#include <math/vector2wx.h> +#include <design_block_lib_table.h> +#include <design_block.h> +#include <footprint_preview_panel.h> +#include <pgm_base.h> +#include <pcb_painter.h> +#include <pcb_edit_frame.h> +#include <pcb_field.h> +#include <pcb_io/pcb_io.h> +#include <pcb_io/pcb_io_mgr.h> +#include <project_pcb.h> +#include <pcbnew_settings.h> +//#include <pcb_helpers.h> +#include <settings/settings_manager.h> +#include <widgets/pcb_design_block_preview_widget.h> +#include <widgets/wx_progress_reporters.h> +#include <wx/log.h> +#include <wx/stattext.h> +#include <wx/panel.h> + + +PCB_DESIGN_BLOCK_PREVIEW_WIDGET::PCB_DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent, PCB_EDIT_FRAME* aFrame ) : + DESIGN_BLOCK_PREVIEW_WIDGET( aParent ), m_preview( nullptr ), m_status( nullptr ), m_statusPanel( nullptr ), + m_statusSizer( nullptr ), m_previewItem( nullptr ) +{ + SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager(); + COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings(); + PCBNEW_SETTINGS* app_settings = mgr.GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" ); + + m_galDisplayOptions.ReadConfig( *common_settings, app_settings->m_Window, this ); + m_galDisplayOptions.m_forceDisplayCursor = false; + + m_preview = FOOTPRINT_PREVIEW_PANEL::New( &aFrame->Kiway(), this, aFrame ); + m_preview->SetStealsFocus( false ); + m_preview->ShowScrollbars( wxSHOW_SB_NEVER, wxSHOW_SB_NEVER ); + m_preview->GetGAL()->SetAxesEnabled( false ); + + // Do not display the grid: the look is not good for a small canvas area. + // But mainly, due to some strange bug I (JPC) was unable to fix, the grid creates + // strange artifacts on Windows when Pcb is run from KiCad manager (but not in + // stand alone...). + m_preview->GetGAL()->SetGridVisibility( true ); + + // Early initialization of the canvas background color, + // before any OnPaint event is fired for the canvas using a wrong bg color + KIGFX::VIEW* view = m_preview->GetView(); + auto settings = static_cast<KIGFX::PCB_RENDER_SETTINGS*>( view->GetPainter()->GetSettings() ); + + if( auto* theme = Pgm().GetSettingsManager().GetColorSettings( app_settings->m_ColorTheme ) ) + settings->LoadColors( theme ); + + const COLOR4D& backgroundColor = settings->GetBackgroundColor(); + const COLOR4D& foregroundColor = settings->GetCursorColor(); + + m_preview->GetGAL()->SetClearColor( backgroundColor ); + + m_outerSizer = new wxBoxSizer( wxVERTICAL ); + + m_statusPanel = new wxPanel( this ); + m_statusPanel->SetBackgroundColour( backgroundColor.ToColour() ); + m_status = new wxStaticText( m_statusPanel, wxID_ANY, wxEmptyString ); + m_status->SetForegroundColour( settings->GetLayerColor( LAYER_REFERENCEPART ).ToColour() ); + m_statusSizer = new wxBoxSizer( wxVERTICAL ); + m_statusSizer->Add( 0, 0, 1 ); // add a spacer + m_statusSizer->Add( m_status, 0, wxALIGN_CENTER ); + m_statusSizer->Add( 0, 0, 1 ); // add a spacer + m_statusPanel->SetSizer( m_statusSizer ); + + // Give the status panel the same color scheme as the canvas so it isn't jarring when + // switched to. + m_statusPanel->SetBackgroundColour( backgroundColor.ToColour() ); + m_statusPanel->SetForegroundColour( foregroundColor.ToColour() ); + + // Give the preview panel a small top border to align its top with the status panel, + // and give the status panel a small bottom border to align its bottom with the preview + // panel. + m_outerSizer->Add( m_preview, 1, wxTOP | wxEXPAND, 5 ); + m_outerSizer->Add( m_statusPanel, 1, wxBOTTOM | wxEXPAND, 5 ); + + // Hide the status panel to start + m_statusPanel->Hide(); + + SetSizer( m_outerSizer ); + Layout(); + + Connect( wxEVT_SIZE, wxSizeEventHandler( PCB_DESIGN_BLOCK_PREVIEW_WIDGET::onSize ), nullptr, this ); +} + + +PCB_DESIGN_BLOCK_PREVIEW_WIDGET::~PCB_DESIGN_BLOCK_PREVIEW_WIDGET() +{ + if( m_previewItem ) + m_preview->GetView()->Remove( m_previewItem ); + + delete m_previewItem; +} + + +void PCB_DESIGN_BLOCK_PREVIEW_WIDGET::SetStatusText( wxString const& aText ) +{ + wxCHECK( m_statusPanel, /* void */ ); + + m_status->SetLabel( aText ); + m_preview->Hide(); + m_statusPanel->Show(); + Layout(); +} + + +void PCB_DESIGN_BLOCK_PREVIEW_WIDGET::onSize( wxSizeEvent& aEvent ) +{ + if( m_previewItem ) + { + fitOnDrawArea(); + m_preview->ForceRefresh(); + } + + aEvent.Skip(); +} + + +void PCB_DESIGN_BLOCK_PREVIEW_WIDGET::fitOnDrawArea() +{ + if( !m_previewItem ) + return; + + // set the view scale to fit the item on screen + KIGFX::VIEW* view = m_preview->GetView(); + + // Calculate the drawing area size, in internal units, for a scaling factor = 1.0 + view->SetScale( 1.0 ); + VECTOR2D clientSize = view->ToWorld( ToVECTOR2D( m_preview->GetClientSize() ), false ); + // Calculate the draw scale to fit the drawing area + double scale = + std::min( fabs( clientSize.x / m_itemBBox.GetWidth() ), fabs( clientSize.y / m_itemBBox.GetHeight() ) ); + + // Above calculation will yield an exact fit; add a bit of whitespace around block + scale /= 1.2; + + // Now fix the best scale + view->SetScale( scale ); + view->SetCenter( m_itemBBox.Centre() ); +} + + +void PCB_DESIGN_BLOCK_PREVIEW_WIDGET::DisplayDesignBlock( DESIGN_BLOCK* aDesignBlock ) +{ + KIGFX::VIEW* view = m_preview->GetView(); + + if( m_previewItem ) + { + view->Clear(); + delete m_previewItem; + m_previewItem = nullptr; + } + + if( aDesignBlock && wxFileExists( aDesignBlock->GetBoardFile() ) ) + { + try + { + IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::PluginFind( PCB_IO_MGR::KICAD_SEXP ) ); + WX_PROGRESS_REPORTER progressReporter( this, _( "Loading PCB" ), 1 ); + + pi->SetProgressReporter( &progressReporter ); + + m_previewItem = pi->LoadBoard( aDesignBlock->GetBoardFile(), nullptr ); + } + catch( const IO_ERROR& ioe ) + { + // You wouldn't think boardFn.GetFullPath() would throw, but we get a stack buffer + // underflow from ASAN. While it's probably an ASAN error, a second try/catch doesn't + // cost us much. + try + { + if( ioe.Problem() != wxT( "CANCEL" ) ) + { + wxString msg = + wxString::Format( _( "Error loading board file:\n%s" ), aDesignBlock->GetBoardFile() ); + DisplayErrorMessage( this, msg, ioe.What() ); + } + } + catch( ... ) + { + // That was already our best-efforts + } + } + + + BOX2I bBox; + + if( m_previewItem ) + { + for( BOARD_ITEM* item : m_previewItem->GetItemSet() ) + { + view->Add( item ); + + if( item->Type() == PCB_FIELD_T ) + { + if( !static_cast<const PCB_FIELD*>( item )->IsVisible() ) + continue; + } + + bBox.Merge( item->GetBoundingBox() ); + } + } + + m_itemBBox = bBox; + + if( !m_preview->IsShownOnScreen() ) + { + m_preview->Show(); + + if( m_statusPanel ) + m_statusPanel->Hide(); + + Layout(); // Ensure panel size is up to date. + } + + // Calculate the draw scale to fit the drawing area + fitOnDrawArea(); + } + + m_preview->ForceRefresh(); + m_preview->Show(); +} diff --git a/pcbnew/widgets/pcb_design_block_preview_widget.h b/pcbnew/widgets/pcb_design_block_preview_widget.h new file mode 100644 index 0000000000..7a80ec3e7e --- /dev/null +++ b/pcbnew/widgets/pcb_design_block_preview_widget.h @@ -0,0 +1,80 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright The KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PCB_DESIGN_BLOCK_PREVIEW_WIDGET_H +#define PCB_DESIGN_BLOCK_PREVIEW_WIDGET_H + +#include <wx/panel.h> +#include <widgets/design_block_preview_widget.h> +#include <kiway.h> +#include <gal_display_options_common.h> +#include <class_draw_panel_gal.h> + + +class LIB_ID; +class DESIGN_BLOCK; +class SCHEMATIC; +class PCB_SHEET; +class wxStaticText; +class wxSizer; + + +class PCB_DESIGN_BLOCK_PREVIEW_WIDGET : public DESIGN_BLOCK_PREVIEW_WIDGET +{ +public: + /** + * Construct a schematic design block preview widget. + * + * @param aParent - parent window + */ + PCB_DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent, PCB_EDIT_FRAME* aFrame ); + + ~PCB_DESIGN_BLOCK_PREVIEW_WIDGET() override; + + /** + * Set the contents of the status label and display it. + */ + void SetStatusText( const wxString& aText ) override; + + /** + * Set the currently displayed symbol. + */ + void DisplayDesignBlock( DESIGN_BLOCK* aDesignBlock ) override; + +protected: + void onSize( wxSizeEvent& aEvent ); + + void fitOnDrawArea(); // set the view scale to fit the item on screen and center + + GAL_DISPLAY_OPTIONS_IMPL m_galDisplayOptions; + EDA_DRAW_PANEL_GAL* m_preview; + + wxStaticText* m_status; + wxPanel* m_statusPanel; + wxSizer* m_statusSizer; + wxSizer* m_outerSizer; + + BOARD* m_previewItem; + + /// The bounding box of the current item + BOX2I m_itemBBox; +}; + + +#endif