diff --git a/common/dialogs/dialog_group_properties.cpp b/common/dialogs/dialog_group_properties.cpp index 42f15c6ecd..b52826cfa4 100644 --- a/common/dialogs/dialog_group_properties.cpp +++ b/common/dialogs/dialog_group_properties.cpp @@ -30,6 +30,7 @@ #include #include #include +#include DIALOG_GROUP_PROPERTIES::DIALOG_GROUP_PROPERTIES( EDA_DRAW_FRAME* aParent, EDA_GROUP* aGroup, @@ -45,6 +46,7 @@ DIALOG_GROUP_PROPERTIES::DIALOG_GROUP_PROPERTIES( EDA_DRAW_FRAME* aParent, EDA_G m_nameCtrl->SetValue( m_group->GetName() ); m_locked->SetValue( aGroup->AsEdaItem()->IsLocked() ); + m_libraryLink->SetValue( m_group->GetDesignBlockLibId().Format() ); if( aGroup->AsEdaItem()->Type() != PCB_GROUP_T ) m_locked->Hide(); @@ -100,6 +102,23 @@ bool DIALOG_GROUP_PROPERTIES::TransferDataFromWindow() m_group->SetName( m_nameCtrl->GetValue() ); m_group->AsEdaItem()->SetLocked( m_locked->GetValue() ); + if( !m_libraryLink->GetValue().IsEmpty() ) + { + LIB_ID libId; + + if( libId.Parse( m_libraryLink->GetValue(), true ) >= 0 ) + { + wxString error; + error.Printf( _( "Invalid library link: '%s'" ), m_libraryLink->GetValue() ); + wxMessageBox( error, _( "Error" ), wxOK | wxICON_ERROR, m_frame ); + return false; + } + + m_group->SetDesignBlockLibId( libId ); + } + else + m_group->SetDesignBlockLibId( LIB_ID() ); + m_toolMgr->RunAction( ACTIONS::selectionClear ); m_group->RemoveAll(); diff --git a/common/dialogs/dialog_group_properties_base.cpp b/common/dialogs/dialog_group_properties_base.cpp index 2ed9a1d3f7..b66c072ff4 100644 --- a/common/dialogs/dialog_group_properties_base.cpp +++ b/common/dialogs/dialog_group_properties_base.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b) +// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -34,6 +34,13 @@ DIALOG_GROUP_PROPERTIES_BASE::DIALOG_GROUP_PROPERTIES_BASE( wxWindow* parent, wx m_nameCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); fgSizer1->Add( m_nameCtrl, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + m_libraryLinkLabel = new wxStaticText( this, wxID_ANY, _("Library link:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_libraryLinkLabel->Wrap( -1 ); + fgSizer1->Add( m_libraryLinkLabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_libraryLink = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer1->Add( m_libraryLink, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 ); + bSizerUpper->Add( fgSizer1, 0, wxEXPAND, 5 ); diff --git a/common/dialogs/dialog_group_properties_base.fbp b/common/dialogs/dialog_group_properties_base.fbp index 6e11ce9311..23cd5c4dee 100644 --- a/common/dialogs/dialog_group_properties_base.fbp +++ b/common/dialogs/dialog_group_properties_base.fbp @@ -1,598 +1,735 @@ - + - - - - C++ - 1 - source_name - 0 - 0 - res - UTF-8 - connect - dialog_group_properties_base - 1000 - none - - - 1 - dialog_group_properties - - . - - 1 - 1 - 1 - 1 - UI - 0 - 0 - 0 - - 0 - wxAUI_MGR_DEFAULT - - wxBOTH - - 1 - 1 - impl_virtual - - - - 0 - wxID_ANY - + + + C++ + + 1 + connect + none + + + 0 + 0 + res + UTF-8 + dialog_group_properties_base + 1000 + 1 + 1 + UI + dialog_group_properties + . + 0 + source_name + 1 + 0 + source_name + + + 1 + 1 + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 0 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + DIALOG_GROUP_PROPERTIES_BASE + + -1,-1 + wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER + DIALOG_SHIM; dialog_shim.h + Group Properties + + 0 + + + + onClose + + + bSizerMain + wxVERTICAL + none + + 10 + wxEXPAND|wxTOP|wxRIGHT|wxLEFT + 1 + - DIALOG_GROUP_PROPERTIES_BASE - - -1,-1 - wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER - DIALOG_SHIM; dialog_shim.h - Group Properties - - 0 - - - - onClose - + bSizerUpper + wxVERTICAL + none + + 5 + wxEXPAND + 0 + + 2 + wxBOTH + 1 + + 0 - bSizerMain - wxVERTICAL + fgSizer1 + wxFLEX_GROWMODE_SPECIFIED none - - 10 - wxEXPAND|wxTOP|wxRIGHT|wxLEFT - 1 - - - bSizerUpper - wxVERTICAL - none - - 5 - wxEXPAND - 0 - - 2 - wxBOTH - 1 - - 0 - - fgSizer1 - wxFLEX_GROWMODE_SPECIFIED - none - 0 - 0 - - 5 - wxALL|wxALIGN_CENTER_VERTICAL - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - Group name: - 0 - - 0 - - - 0 - - 1 - m_nameLabel - 1 - - - protected - 1 - - Resizable - 1 - - - ; ; forward_declare - 0 - - - - - -1 - - - - 5 - wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - - 0 - - - - 0 - - 1 - m_nameCtrl - 1 - - - protected - 1 - - Resizable - 1 - - - ; ; forward_declare - 0 - - - wxFILTER_NONE - wxDefaultValidator - - - - - - - - - - - 5 - wxALL - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - Locked - - 0 - - - 0 - - 1 - m_locked - 1 - - - protected - 1 - - Resizable - 1 - - - ; ; forward_declare - 0 - Prevents group from being moved on canvas - - wxFILTER_NONE - wxDefaultValidator - - - - - - - - 5 - wxTOP|wxRIGHT|wxLEFT - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - Group members: - 0 - - 0 - - - 0 - - 1 - m_membersLabel - 1 - - - protected - 1 - - Resizable - 1 - - - ; ; forward_declare - 0 - - - - - -1 - - - - 5 - wxEXPAND|wxTOP|wxRIGHT|wxLEFT - 1 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - - 1 - - 1 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - - 0 - - - 0 - 360,200 - 1 - m_membersList - 1 - - - protected - 1 - - Resizable - 1 - - - ; ; forward_declare - 0 - - - wxFILTER_NONE - wxDefaultValidator - - - - - OnMemberSelected - - - - 5 - wxBOTTOM|wxEXPAND - 0 - - - bMembershipButtons - wxHORIZONTAL - none - - 5 - wxALL - 0 - - 1 - 1 - 1 - 1 - - - - - 0 - - - - - 1 - 0 - 1 - - 1 - - 0 - 0 - - Dock - 0 - Left - 1 - - 1 - - - 0 - 0 - wxID_ANY - MyButton - - 0 - - 0 - - - 0 - - 1 - m_bpAddMember - 1 - - - protected - 1 - - - - Resizable - 1 - - - STD_BITMAP_BUTTON; widgets/std_bitmap_button.h; forward_declare - 0 - - - wxFILTER_NONE - wxDefaultValidator - - - - - OnAddMember - - - - 5 - wxEXPAND|wxRIGHT|wxLEFT - 0 - - 0 - protected - 10 - - - - 5 - wxALL - 0 - - 1 - 1 - 1 - 1 - - - - - 0 - - - - - 1 - 0 - 1 - - 1 - - 0 - 0 - - Dock - 0 - Left - 1 - - 1 - - - 0 - 0 - wxID_ANY - MyButton - - 0 - - 0 - - - 0 - - 1 - m_bpRemoveMember - 1 - - - protected - 1 - - - - Resizable - 1 - - - STD_BITMAP_BUTTON; widgets/std_bitmap_button.h; forward_declare - 0 - - - wxFILTER_NONE - wxDefaultValidator - - - - - OnRemoveMember - - - - - + 0 + 0 + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Group name: + 0 + + 0 + + + 0 + + 1 + m_nameLabel + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + - - 5 - wxBOTTOM|wxEXPAND|wxTOP - 0 - - 0 - 1 - 0 - 0 - 0 - 1 - 0 - 0 - - m_sdbSizer - protected - + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 0 + + 0 + + 1 + m_nameCtrl + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Library link: + 0 + + 0 + + + 0 + + 1 + m_libraryLinkLabel + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 0 + + 0 + + 1 + m_libraryLink + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Locked + + 0 + + + 0 + + 1 + m_locked + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + Prevents group from being moved on canvas + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 5 + wxTOP|wxRIGHT|wxLEFT + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Group members: + 0 + + 0 + + + 0 + + 1 + m_membersLabel + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxEXPAND|wxTOP|wxRIGHT|wxLEFT + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + 360,200 + 1 + m_membersList + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + OnMemberSelected + + + + 5 + wxBOTTOM|wxEXPAND + 0 + + + bMembershipButtons + wxHORIZONTAL + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + 0 + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 0 + 1 + + 1 + + + 0 + 0 + wxID_ANY + MyButton + + 0 + + 0 + + + 0 + + 1 + m_bpAddMember + 1 + + + protected + 1 + + + + Resizable + 1 + + + STD_BITMAP_BUTTON; widgets/std_bitmap_button.h; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + OnAddMember + + + + 5 + wxEXPAND|wxRIGHT|wxLEFT + 0 + + 0 + protected + 10 + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + 0 + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 0 + 1 + + 1 + + + 0 + 0 + wxID_ANY + MyButton + + 0 + + 0 + + + 0 + + 1 + m_bpRemoveMember + 1 + + + protected + 1 + + + + Resizable + 1 + + + STD_BITMAP_BUTTON; widgets/std_bitmap_button.h; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + OnRemoveMember + + + + + + + 5 + wxBOTTOM|wxEXPAND|wxTOP + 0 + + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + + m_sdbSizer + protected + + + + diff --git a/common/dialogs/dialog_group_properties_base.h b/common/dialogs/dialog_group_properties_base.h index 06bce8cb50..814752efa9 100644 --- a/common/dialogs/dialog_group_properties_base.h +++ b/common/dialogs/dialog_group_properties_base.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b) +// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -32,7 +32,6 @@ class STD_BITMAP_BUTTON; /////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////// /// Class DIALOG_GROUP_PROPERTIES_BASE /////////////////////////////////////////////////////////////////////////////// @@ -43,6 +42,8 @@ class DIALOG_GROUP_PROPERTIES_BASE : public DIALOG_SHIM protected: wxStaticText* m_nameLabel; wxTextCtrl* m_nameCtrl; + wxStaticText* m_libraryLinkLabel; + wxTextCtrl* m_libraryLink; wxCheckBox* m_locked; wxStaticText* m_membersLabel; wxListBox* m_membersList; diff --git a/common/eda_group.h b/common/eda_group.h index 30073e8a7f..fabceba65a 100644 --- a/common/eda_group.h +++ b/common/eda_group.h @@ -25,6 +25,7 @@ #define EDA_GROUP_H #include +#include #include #include @@ -80,9 +81,18 @@ public: */ virtual EDA_GROUP* DeepDuplicate() const = 0; + bool HasDesignBlockLink() const { return m_designBlockLibId.IsValid(); } + + void SetDesignBlockLibId( const LIB_ID& aLibId ) { m_designBlockLibId = aLibId; } + + const LIB_ID& GetDesignBlockLibId() const { return m_designBlockLibId; } + protected: std::unordered_set m_items; // Members of the group wxString m_name; // Optional group name + + // Optional link to a design block + LIB_ID m_designBlockLibId; }; #endif // CLASS_PCB_GROUP_H_ diff --git a/common/netlist.keywords b/common/netlist.keywords index 96146e6d2b..d5483908f2 100644 --- a/common/netlist.keywords +++ b/common/netlist.keywords @@ -19,6 +19,7 @@ group groups jumper_pin_groups lib +lib_id libpart libparts libraries diff --git a/common/pcb.keywords b/common/pcb.keywords index ae480c4976..6ddf2524a6 100644 --- a/common/pcb.keywords +++ b/common/pcb.keywords @@ -206,6 +206,7 @@ leader leader_length left legacy_teardrops +lib_id linear line_spacing links diff --git a/eeschema/netlist_exporters/netlist_exporter_xml.cpp b/eeschema/netlist_exporters/netlist_exporter_xml.cpp index b7bbd168e8..b59b5a54af 100644 --- a/eeschema/netlist_exporters/netlist_exporter_xml.cpp +++ b/eeschema/netlist_exporters/netlist_exporter_xml.cpp @@ -515,6 +515,7 @@ XNODE* NETLIST_EXPORTER_XML::makeGroups() xgroup->AddAttribute( wxT( "name" ), group->GetName() ); xgroup->AddAttribute( wxT( "uuid" ), group->m_Uuid.AsString() ); + xgroup->AddAttribute( wxT( "lib_id" ), group->GetDesignBlockLibId().Format() ); XNODE* xmembers; xgroup->AddChild( xmembers = node( wxT( "members" ) ) ); diff --git a/eeschema/sch_file_versions.h b/eeschema/sch_file_versions.h index d4c325e0c6..8ee42fb6c6 100644 --- a/eeschema/sch_file_versions.h +++ b/eeschema/sch_file_versions.h @@ -121,4 +121,5 @@ //#define SEXPR_SCHEMATIC_FILE_VERSION 20250222 // Hatched fills for shapes //#define SEXPR_SCHEMATIC_FILE_VERSION 20250227 // Support for local power symbols //#define SEXPR_SCHEMATIC_FILE_VERSION 20250318 // ~ no longer means empty text -#define SEXPR_SCHEMATIC_FILE_VERSION 20250425 // uuids for tables +//#define SEXPR_SCHEMATIC_FILE_VERSION 20250425 // uuids for tables +#define SEXPR_SCHEMATIC_FILE_VERSION 20250513 // Groups can have design block lib_id diff --git a/eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr.cpp b/eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr.cpp index 0eaf31231c..4c1dd29eb4 100644 --- a/eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr.cpp +++ b/eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr.cpp @@ -1479,6 +1479,9 @@ void SCH_IO_KICAD_SEXPR::saveGroup( SCH_GROUP* aGroup ) if( aGroup->IsLocked() ) KICAD_FORMAT::FormatBool( m_out, "locked", true ); + if( aGroup->HasDesignBlockLink() ) + m_out->Print( "(lib_id \"%s\")", aGroup->GetDesignBlockLibId().Format().c_str() ); + wxArrayString memberIds; for( EDA_ITEM* member : aGroup->GetItems() ) diff --git a/eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr_parser.cpp b/eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr_parser.cpp index a9864f02d1..9c00b04812 100644 --- a/eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr_parser.cpp +++ b/eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr_parser.cpp @@ -4858,6 +4858,38 @@ void SCH_IO_KICAD_SEXPR_PARSER::parseGroup() NeedRIGHT(); break; + case T_lib_id: + { + token = NextTok(); + + if( !IsSymbol( token ) && token != T_NUMBER ) + Expecting( "symbol|number" ); + + wxString name = FromUTF8(); + // Some symbol LIB_IDs have the '/' character escaped which can break + // symbol links. The '/' character is no longer an illegal LIB_ID character so + // it doesn't need to be escaped. + name.Replace( "{slash}", "/" ); + + int bad_pos = groupInfo.libId.Parse( name ); + + if( bad_pos >= 0 ) + { + if( static_cast( name.size() ) > bad_pos ) + { + wxString msg = wxString::Format( _( "Group library link %s contains invalid character '%c'" ), name, + name[bad_pos] ); + + THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() ); + } + + THROW_PARSE_ERROR( _( "Invalid library ID" ), CurSource(), CurLine(), CurLineNumber(), CurOffset() ); + } + + NeedRIGHT(); + break; + } + case T_members: { parseGroupMembers( groupInfo ); @@ -4865,7 +4897,7 @@ void SCH_IO_KICAD_SEXPR_PARSER::parseGroup() } default: - Expecting( "uuid, members" ); + Expecting( "uuid, lib_id, members" ); } } } @@ -4906,6 +4938,9 @@ void SCH_IO_KICAD_SEXPR_PARSER::resolveGroups( SCH_SCREEN* aParent ) const_cast( group->m_Uuid ) = groupInfo.uuid; + if( groupInfo.libId.IsValid() ) + group->SetDesignBlockLibId( groupInfo.libId ); + aParent->Append( group ); } diff --git a/eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr_parser.h b/eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr_parser.h index d8afddbd99..00b1e50199 100644 --- a/eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr_parser.h +++ b/eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr_parser.h @@ -123,6 +123,7 @@ private: wxString name; KIID uuid; + LIB_ID libId; std::vector memberUuids; }; diff --git a/eeschema/tools/sch_drawing_tools.cpp b/eeschema/tools/sch_drawing_tools.cpp index 61ffe9c3af..7e12adb23f 100644 --- a/eeschema/tools/sch_drawing_tools.cpp +++ b/eeschema/tools/sch_drawing_tools.cpp @@ -729,7 +729,14 @@ int SCH_DRAWING_TOOLS::ImportSheet( const TOOL_EVENT& aEvent ) group = new SCH_GROUP( screen ); if( designBlock ) + { group->SetName( designBlock->GetLibId().GetLibItemName() ); + group->SetDesignBlockLibId( designBlock->GetLibId() ); + } + else + { + group->SetName( wxFileName( sheetFileName ).GetName() ); + } } // Select all new items diff --git a/pcbnew/netlist_reader/board_netlist_updater.cpp b/pcbnew/netlist_reader/board_netlist_updater.cpp index 897adab1ae..f255da1665 100644 --- a/pcbnew/netlist_reader/board_netlist_updater.cpp +++ b/pcbnew/netlist_reader/board_netlist_updater.cpp @@ -895,16 +895,14 @@ bool BOARD_NETLIST_UPDATER::updateFootprintGroup( FOOTPRINT* aPcbFootprint, const_cast( newGroup->m_Uuid ) = newGroupKIID; newGroup->SetName( aNetlistComponent->GetGroup()->name ); + // Add the group to the board manually so we can find it by checking + // board groups for later footprints that are checking for existing groups m_board->Add( newGroup ); - newGroup->AddItem( aPcbFootprint ); - m_commit.Add( newGroup ); - } - else - { - newGroup->AddItem( aPcbFootprint ); - m_commit.Modify( newGroup->AsEdaItem() ); + m_commit.Added( newGroup ); } + m_commit.Stage( aPcbFootprint, CHT_GROUP ); + aPcbFootprint->SetParentGroup( newGroup ); } @@ -1356,25 +1354,42 @@ bool BOARD_NETLIST_UPDATER::updateGroups( NETLIST& aNetlist ) if( netlistGroup == nullptr ) continue; - if( netlistGroup->name == pcbGroup->GetName() ) - continue; - - if( m_isDryRun ) + if( netlistGroup->name != pcbGroup->GetName() ) { - wxString msg; - msg.Printf( _( "Change group name to '%s' to '%s'." ), - EscapeHTML( pcbGroup->GetName() ), - EscapeHTML( netlistGroup->name ) ); - m_reporter->Report( msg, RPT_SEVERITY_ACTION ); + if( m_isDryRun ) + { + wxString msg; + msg.Printf( _( "Change group name to '%s' to '%s'." ), EscapeHTML( pcbGroup->GetName() ), + EscapeHTML( netlistGroup->name ) ); + m_reporter->Report( msg, RPT_SEVERITY_ACTION ); + } + else + { + wxString msg; + msg.Printf( _( "Changed group name to '%s' to '%s'." ), EscapeHTML( pcbGroup->GetName() ), + EscapeHTML( netlistGroup->name ) ); + m_commit.Modify( pcbGroup->AsEdaItem() ); + pcbGroup->SetName( netlistGroup->name ); + } } - else + + if( netlistGroup->libId != pcbGroup->GetDesignBlockLibId() ) { - wxString msg; - msg.Printf( _( "Changed group name to '%s' to '%s'." ), - EscapeHTML( pcbGroup->GetName() ), - EscapeHTML( netlistGroup->name ) ); - m_commit.Modify( pcbGroup->AsEdaItem() ); - pcbGroup->SetName( netlistGroup->name ); + if( m_isDryRun ) + { + wxString msg; + msg.Printf( _( "Change group library link to '%s'." ), + EscapeHTML( netlistGroup->libId.GetUniStringLibId() ) ); + m_reporter->Report( msg, RPT_SEVERITY_ACTION ); + } + else + { + wxString msg; + msg.Printf( _( "Changed group library link to '%s'." ), + EscapeHTML( netlistGroup->libId.GetUniStringLibId() ) ); + m_commit.Modify( pcbGroup->AsEdaItem() ); + pcbGroup->SetDesignBlockLibId( netlistGroup->libId ); + } } } diff --git a/pcbnew/netlist_reader/kicad_netlist_reader.cpp b/pcbnew/netlist_reader/kicad_netlist_reader.cpp index 079456020b..835c2bbaf2 100644 --- a/pcbnew/netlist_reader/kicad_netlist_reader.cpp +++ b/pcbnew/netlist_reader/kicad_netlist_reader.cpp @@ -586,12 +586,14 @@ void KICAD_NETLIST_PARSER::parseGroup() { /* Parses a section like * (groups - * (group (name "") (uuid "7b1488be-4c43-4004-94fc-e4a26dda8f5b") - * (member (uuid "dfef752d-e203-4feb-91de-483b44bc4062")) + * (group (name "") (lib_id "DesignBlock:Block") (uuid "7b1488be-4c43-4004-94fc-e4a26dda8f5b") + * (members + * (member (uuid "dfef752d-e203-4feb-91de-483b44bc4062")) */ wxString name; KIID uuid; + wxString libId; // Design block library link std::vector members; // The token net was read, so the next data is (code ) @@ -616,6 +618,12 @@ void KICAD_NETLIST_PARSER::parseGroup() NeedRIGHT(); break; + case T_lib_id: + NeedSYMBOLorNUMBER(); + libId = FromUTF8(); + NeedRIGHT(); + break; + case T_members: while( ( token = NextTok() ) != T_RIGHT ) { @@ -659,7 +667,18 @@ void KICAD_NETLIST_PARSER::parseGroup() } } - NETLIST_GROUP* group = new NETLIST_GROUP { name, uuid, members }; + LIB_ID groupLibId; + + if( !libId.IsEmpty() && groupLibId.Parse( libId, true ) >= 0 ) + { + wxString error; + error.Printf( _( "Invalid lib_id ID in\nfile: '%s'\nline: %d\nofff: %d" ), CurSource(), CurLineNumber(), + CurOffset() ); + + THROW_IO_ERROR( error ); + } + + NETLIST_GROUP* group = new NETLIST_GROUP{ name, uuid, groupLibId, members }; m_netlist->AddGroup( group ); } diff --git a/pcbnew/netlist_reader/pcb_netlist.h b/pcbnew/netlist_reader/pcb_netlist.h index 62ca56146b..5654c0fe50 100644 --- a/pcbnew/netlist_reader/pcb_netlist.h +++ b/pcbnew/netlist_reader/pcb_netlist.h @@ -86,6 +86,7 @@ struct NETLIST_GROUP { wxString name; KIID uuid; + LIB_ID libId; std::vector members; }; diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp index 3b668bf3e0..d7fa12d133 100644 --- a/pcbnew/pcb_edit_frame.cpp +++ b/pcbnew/pcb_edit_frame.cpp @@ -817,6 +817,20 @@ void PCB_EDIT_FRAME::setupUIConditions() return GetUndoCommandCount() > 0; }; + auto groupWithDesignBlockLink = + [] ( const SELECTION& aSel ) + { + if( aSel.Size() != 1 ) + return false; + + if( aSel[0]->Type() != PCB_GROUP_T ) + return false; + + PCB_GROUP* group = static_cast( aSel.GetItem( 0 ) ); + + return group->HasDesignBlockLink(); + }; + wxASSERT( mgr ); #define ENABLE( x ) ACTION_CONDITIONS().Enable( x ) @@ -844,6 +858,8 @@ void PCB_EDIT_FRAME::setupUIConditions() mgr->SetConditions( ACTIONS::doDelete, ENABLE( cond.HasItems() ) ); mgr->SetConditions( ACTIONS::duplicate, ENABLE( cond.HasItems() ) ); + mgr->SetConditions( PCB_ACTIONS::placeLinkedDesignBlock, ENABLE( groupWithDesignBlockLink) ); + static const std::vector groupTypes = { PCB_GROUP_T, PCB_GENERATOR_T }; mgr->SetConditions( ACTIONS::group, ENABLE( SELECTION_CONDITIONS::NotEmpty ) ); diff --git a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.cpp b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.cpp index 3adf6681fd..88f53200dd 100644 --- a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.cpp +++ b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.cpp @@ -2223,6 +2223,9 @@ void PCB_IO_KICAD_SEXPR::format( const PCB_GROUP* aGroup ) const if( aGroup->IsLocked() ) KICAD_FORMAT::FormatBool( m_out, "locked", true ); + if( aGroup->HasDesignBlockLink() ) + m_out->Print( "(lib_id \"%s\")", aGroup->GetDesignBlockLibId().Format().c_str() ); + wxArrayString memberIds; for( EDA_ITEM* member : aGroup->GetItems() ) diff --git a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h index 1c159fa563..95f6991ea4 100644 --- a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h +++ b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h @@ -182,7 +182,8 @@ class PCB_IO_KICAD_SEXPR; // forward decl //#define SEXPR_BOARD_FILE_VERSION 20250302 // Zone Hatching Offsets //#define SEXPR_BOARD_FILE_VERSION 20250309 // Component class dynamic assignment rules //#define SEXPR_BOARD_FILE_VERSION 20250324 // Jumper pads -#define SEXPR_BOARD_FILE_VERSION 20250401 // Time domain length tuning +//#define SEXPR_BOARD_FILE_VERSION 20250401 // Time domain length tuning +#define SEXPR_BOARD_FILE_VERSION 20250513 // Groups can have design block lib_id #define BOARD_FILE_HOST_VERSION 20200825 ///< Earlier files than this include the host tag #define LEGACY_ARC_FORMATTING 20210925 ///< These were the last to use old arc formatting diff --git a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp index f5021af84f..a12860ed2e 100644 --- a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp +++ b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp @@ -1312,6 +1312,9 @@ void PCB_IO_KICAD_SEXPR_PARSER::resolveGroups( BOARD_ITEM* aParent ) const_cast( group->m_Uuid ) = groupInfo->uuid; + if( groupInfo->libId.IsValid() ) + group->SetDesignBlockLibId( groupInfo->libId ); + if( groupInfo->locked ) group->SetLocked( true ); @@ -6149,6 +6152,38 @@ void PCB_IO_KICAD_SEXPR_PARSER::parseGROUP( BOARD_ITEM* aParent ) NeedRIGHT(); break; + case T_lib_id: + { + token = NextTok(); + + if( !IsSymbol( token ) && token != T_NUMBER ) + Expecting( "symbol|number" ); + + wxString name = FromUTF8(); + // Some symbol LIB_IDs have the '/' character escaped which can break + // symbol links. The '/' character is no longer an illegal LIB_ID character so + // it doesn't need to be escaped. + name.Replace( "{slash}", "/" ); + + int bad_pos = groupInfo.libId.Parse( name ); + + if( bad_pos >= 0 ) + { + if( static_cast( name.size() ) > bad_pos ) + { + wxString msg = wxString::Format( _( "Group library link %s contains invalid character '%c'" ), name, + name[bad_pos] ); + + THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() ); + } + + THROW_PARSE_ERROR( _( "Invalid library ID" ), CurSource(), CurLine(), CurLineNumber(), CurOffset() ); + } + + NeedRIGHT(); + break; + } + case T_locked: groupInfo.locked = parseBool(); NeedRIGHT(); @@ -6161,7 +6196,7 @@ void PCB_IO_KICAD_SEXPR_PARSER::parseGROUP( BOARD_ITEM* aParent ) } default: - Expecting( "uuid, locked, or members" ); + Expecting( "uuid, locked, lib_id, or members" ); } } } diff --git a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.h b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.h index 0400dc18be..138c3fdc0a 100644 --- a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.h +++ b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.h @@ -32,6 +32,7 @@ #include #include +#include #include // PCB_LAYER_ID #include #include @@ -141,6 +142,7 @@ private: wxString name; bool locked; KIID uuid; + LIB_ID libId; std::vector memberUuids; }; diff --git a/pcbnew/tools/pcb_actions.cpp b/pcbnew/tools/pcb_actions.cpp index 58a2a39155..274f40e1d2 100644 --- a/pcbnew/tools/pcb_actions.cpp +++ b/pcbnew/tools/pcb_actions.cpp @@ -455,6 +455,15 @@ TOOL_ACTION PCB_ACTIONS::placeDesignBlock( TOOL_ACTION_ARGS() .Flags( AF_ACTIVATE ) .Parameter( nullptr ) ); +TOOL_ACTION PCB_ACTIONS::placeLinkedDesignBlock( TOOL_ACTION_ARGS() + .Name( "pcbnew.InteractiveDrawing.placeLinkedDesignBlock" ) + .Scope( AS_GLOBAL ) + .FriendlyName( _( "Place Linked Design Block" ) ) + .Tooltip( _( "Place design block linked to selected group" ) ) + .Icon( BITMAPS::add_component ) + .Flags( AF_ACTIVATE ) ); + + TOOL_ACTION PCB_ACTIONS::showDesignBlockPanel( TOOL_ACTION_ARGS() .Name( "pcbnew.PcbDesignBlockControl.showDesignBlockPanel" ) .Scope( AS_GLOBAL ) diff --git a/pcbnew/tools/pcb_actions.h b/pcbnew/tools/pcb_actions.h index a07099ea1e..a3915c097e 100644 --- a/pcbnew/tools/pcb_actions.h +++ b/pcbnew/tools/pcb_actions.h @@ -437,6 +437,7 @@ public: // Design Block management static TOOL_ACTION placeDesignBlock; + static TOOL_ACTION placeLinkedDesignBlock; static TOOL_ACTION showDesignBlockPanel; static TOOL_ACTION saveBoardAsDesignBlock; static TOOL_ACTION saveSelectionAsDesignBlock; diff --git a/pcbnew/tools/pcb_control.cpp b/pcbnew/tools/pcb_control.cpp index c9b7528588..8ae3c90cb9 100644 --- a/pcbnew/tools/pcb_control.cpp +++ b/pcbnew/tools/pcb_control.cpp @@ -1353,6 +1353,59 @@ int PCB_CONTROL::AppendDesignBlock( const TOOL_EVENT& aEvent ) } +int PCB_CONTROL::PlaceLinkedDesignBlock( const TOOL_EVENT& aEvent ) +{ + PCB_EDIT_FRAME* editFrame = dynamic_cast( m_frame ); + + if( !editFrame ) + return 1; + + // Need to have a group selected and it needs to have a linked design block + PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool(); + PCB_SELECTION selection = selTool->GetSelection(); + + if( selection.Size() != 1 || selection[0]->Type() != PCB_GROUP_T ) + return 1; + + PCB_GROUP* group = static_cast( selection[0] ); + + if( !group->HasDesignBlockLink() ) + return 1; + + // Get the associated design block + DESIGN_BLOCK* designBlock = + editFrame->GetDesignBlockPane()->GetDesignBlock( group->GetDesignBlockLibId(), true, true ); + + if( !designBlock ) + { + wxString msg; + msg.Printf( _( "Could not find design block %s." ), group->GetDesignBlockLibId().GetUniStringLibId() ); + m_frame->GetInfoBar()->ShowMessageFor( msg, 5000, wxICON_WARNING ); + return 1; + } + + if( designBlock->GetBoardFile().IsEmpty() ) + { + wxString msg; + msg.Printf( _( "Design block %s does not have a board file." ), + group->GetDesignBlockLibId().GetUniStringLibId() ); + m_frame->GetInfoBar()->ShowMessageFor( msg, 5000, wxICON_WARNING ); + return 1; + } + + + PCB_IO_MGR::PCB_FILE_T pluginType = PCB_IO_MGR::KICAD_SEXP; + IO_RELEASER pi( PCB_IO_MGR::PluginFind( pluginType ) ); + + if( !pi ) + return 1; + + int ret = AppendBoard( *pi, designBlock->GetBoardFile() ); + + return ret; +} + + template static void moveUnflaggedItems( const std::deque& aList, std::vector& aTarget, bool aIsNew ) @@ -1525,7 +1578,7 @@ bool PCB_CONTROL::placeBoardItems( BOARD_COMMIT* aCommit, std::vector( m_frame ); @@ -1646,6 +1699,16 @@ int PCB_CONTROL::AppendBoard( PCB_IO& pi, const wxString& fileName ) BOARD_COMMIT grpCommit( m_toolMgr ); PCB_GROUP* group = new PCB_GROUP( brd ); + if( aDesignBlock ) + { + group->SetName( aDesignBlock->GetLibId().GetLibItemName() ); + group->SetDesignBlockLibId( aDesignBlock->GetLibId() ); + } + else + { + group->SetName( wxFileName( fileName ).GetName() ); + } + // Get the selection tool selection PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool(); PCB_SELECTION selection = selTool->GetSelection(); @@ -2512,6 +2575,7 @@ void PCB_CONTROL::setTransitions() // Append control Go( &PCB_CONTROL::AppendDesignBlock, PCB_ACTIONS::placeDesignBlock.MakeEvent() ); + Go( &PCB_CONTROL::PlaceLinkedDesignBlock, PCB_ACTIONS::placeLinkedDesignBlock.MakeEvent() ); Go( &PCB_CONTROL::AppendBoardFromFile, PCB_ACTIONS::appendBoard.MakeEvent() ); Go( &PCB_CONTROL::DdAppendBoard, PCB_ACTIONS::ddAppendBoard.MakeEvent() ); Go( &PCB_CONTROL::PlaceCharacteristics, PCB_ACTIONS::placeCharacteristics.MakeEvent() ); diff --git a/pcbnew/tools/pcb_control.h b/pcbnew/tools/pcb_control.h index 0a7c1f0c37..8ee700fa09 100644 --- a/pcbnew/tools/pcb_control.h +++ b/pcbnew/tools/pcb_control.h @@ -107,7 +107,8 @@ public: int Paste( const TOOL_EVENT& aEvent ); int AppendBoardFromFile( const TOOL_EVENT& aEvent ); int AppendDesignBlock( const TOOL_EVENT& aEvent ); - int AppendBoard( PCB_IO& pi, const wxString& fileName ); + int PlaceLinkedDesignBlock( const TOOL_EVENT& aEvent ); + int AppendBoard( PCB_IO& pi, const wxString& fileName, DESIGN_BLOCK* aDesignBlock = nullptr ); int UpdateMessagePanel( const TOOL_EVENT& aEvent ); int PlaceCharacteristics( const TOOL_EVENT& aEvent ); int PlaceStackup( const TOOL_EVENT& aEvent ); diff --git a/pcbnew/tools/pcb_selection_tool.cpp b/pcbnew/tools/pcb_selection_tool.cpp index c8f00178ef..1cc880f277 100644 --- a/pcbnew/tools/pcb_selection_tool.cpp +++ b/pcbnew/tools/pcb_selection_tool.cpp @@ -212,6 +212,7 @@ bool PCB_SELECTION_TOOL::Init() menu.AddItem( ACTIONS::cancelInteractive, activeToolCondition, 1 ); menu.AddItem( ACTIONS::groupEnter, groupEnterCondition, 1 ); menu.AddItem( ACTIONS::groupLeave, inGroupCondition, 1 ); + menu.AddItem( PCB_ACTIONS::placeLinkedDesignBlock, groupEnterCondition, 1 ); menu.AddItem( PCB_ACTIONS::clearHighlight, haveHighlight, 1 ); menu.AddSeparator( haveHighlight, 1 );