/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2014 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 1992-2023 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LAYER_LIST_COLUMN_CHECK 0 #define LAYER_LIST_COLUMN_ICON 1 #define LAYER_LIST_COLUMN_NAME 2 #define LAYER_LIST_ROW_ALL_INNER_LAYERS 1 class DIALOG_RULE_AREA_PROPERTIES : public DIALOG_RULE_AREA_PROPERTIES_BASE { public: DIALOG_RULE_AREA_PROPERTIES( PCB_BASE_FRAME* aParent, ZONE_SETTINGS* aSettings, CONVERT_SETTINGS* aConvertSettings, BOARD* aBoard ); ~DIALOG_RULE_AREA_PROPERTIES(); private: bool TransferDataToWindow() override; bool TransferDataFromWindow() override; void PopulatePlacementSourceList( RULE_AREA_PLACEMENT_SOURCE_TYPE placementType ); void OnLayerSelection( wxDataViewEvent& event ) override; void OnSheetNameRbClicked( wxCommandEvent& event ); void OnComponentClassRbClicked( wxCommandEvent& event ); private: BOARD* m_board; UNIT_BINDER m_outlineHatchPitch; PCB_BASE_FRAME* m_parent; ZONE_SETTINGS m_zonesettings; ///< the working copy of zone settings ZONE_SETTINGS* m_ptr; ///< the pointer to the zone settings of the zone to edit bool m_isFpEditor; CONVERT_SETTINGS* m_convertSettings; wxRadioButton* m_rbCenterline; wxRadioButton* m_rbBoundingHull; wxStaticText* m_gapLabel; wxTextCtrl* m_gapCtrl; wxStaticText* m_gapUnits; UNIT_BINDER* m_gap; wxCheckBox* m_cbDeleteOriginals; // The name of a rule area source that is not now found on the board (e.g. after a netlist // update). This is used to re-populate the zone settings if the selection is not changed. wxString m_notFoundPlacementSourceName; PANEL_RULE_AREA_PROPERTIES_KEEPOUT_BASE* m_keepoutProperties; PANEL_RULE_AREA_PROPERTIES_PLACEMENT_BASE* m_placementProperties; RULE_AREA_PLACEMENT_SOURCE_TYPE m_currentKeepoutSource; std::set m_sheetNames; std::set m_componentClasses; }; int InvokeRuleAreaEditor( PCB_BASE_FRAME* aCaller, ZONE_SETTINGS* aZoneSettings, BOARD* aBoard, CONVERT_SETTINGS* aConvertSettings ) { DIALOG_RULE_AREA_PROPERTIES dlg( aCaller, aZoneSettings, aConvertSettings, aBoard ); return dlg.ShowModal(); } DIALOG_RULE_AREA_PROPERTIES::DIALOG_RULE_AREA_PROPERTIES( PCB_BASE_FRAME* aParent, ZONE_SETTINGS* aSettings, CONVERT_SETTINGS* aConvertSettings, BOARD* aBoard ) : DIALOG_RULE_AREA_PROPERTIES_BASE( aParent ), m_board( aBoard ), m_outlineHatchPitch( aParent, m_stBorderHatchPitchText, m_outlineHatchPitchCtrl, m_outlineHatchUnits ), m_convertSettings( aConvertSettings ), m_rbCenterline( nullptr ), m_rbBoundingHull( nullptr ), m_cbDeleteOriginals( nullptr ) { m_parent = aParent; m_ptr = aSettings; m_zonesettings = *aSettings; m_keepoutProperties = new PANEL_RULE_AREA_PROPERTIES_KEEPOUT_BASE( m_areaPropertiesNb ); m_areaPropertiesNb->AddPage( m_keepoutProperties, _( "Keepout" ), true ); if( ADVANCED_CFG::GetCfg().m_EnableMultichannelTool ) { m_placementProperties = new PANEL_RULE_AREA_PROPERTIES_PLACEMENT_BASE( m_areaPropertiesNb ); m_areaPropertiesNb->AddPage( m_placementProperties, _( "Placement" ) ); m_placementProperties->m_sourceSheetnameRb->Connect( wxEVT_RADIOBUTTON, wxCommandEventHandler( DIALOG_RULE_AREA_PROPERTIES::OnSheetNameRbClicked ), NULL, this ); m_placementProperties->m_sourceComponentClassRb->Connect( wxEVT_RADIOBUTTON, wxCommandEventHandler( DIALOG_RULE_AREA_PROPERTIES::OnComponentClassRbClicked ), NULL, this ); } if( aConvertSettings ) { wxStaticBox* bConvertBox = new wxStaticBox( this, wxID_ANY, _( "Conversion Settings" ) ); wxStaticBoxSizer* bConvertSizer = new wxStaticBoxSizer( bConvertBox, wxVERTICAL ); m_rbCenterline = new wxRadioButton( this, wxID_ANY, _( "Use centerlines" ) ); bConvertSizer->Add( m_rbCenterline, 0, wxLEFT|wxRIGHT, 5 ); bConvertSizer->AddSpacer( 2 ); m_rbBoundingHull = new wxRadioButton( this, wxID_ANY, _( "Create bounding hull" ) ); bConvertSizer->Add( m_rbBoundingHull, 0, wxLEFT|wxRIGHT, 5 ); m_gapLabel = new wxStaticText( this, wxID_ANY, _( "Gap:" ) ); m_gapCtrl = new wxTextCtrl( this, wxID_ANY ); m_gapUnits = new wxStaticText( this, wxID_ANY, _( "mm" ) ); m_gap = new UNIT_BINDER( aParent, m_gapLabel, m_gapCtrl, m_gapUnits ); wxBoxSizer* hullParamsSizer = new wxBoxSizer( wxHORIZONTAL ); hullParamsSizer->Add( m_gapLabel, 0, wxALIGN_CENTRE_VERTICAL, 5 ); hullParamsSizer->Add( m_gapCtrl, 1, wxALIGN_CENTRE_VERTICAL|wxLEFT|wxRIGHT, 3 ); hullParamsSizer->Add( m_gapUnits, 0, wxALIGN_CENTRE_VERTICAL, 5 ); bConvertSizer->AddSpacer( 2 ); bConvertSizer->Add( hullParamsSizer, 0, wxLEFT, 26 ); bConvertSizer->AddSpacer( 6 ); m_cbDeleteOriginals = new wxCheckBox( this, wxID_ANY, _( "Delete source objects after conversion" ) ); bConvertSizer->Add( m_cbDeleteOriginals, 0, wxALL, 5 ); GetSizer()->Insert( 0, bConvertSizer, 0, wxALL|wxEXPAND, 10 ); wxStaticLine* line = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); GetSizer()->Insert( 1, line, 0, wxLEFT|wxRIGHT|wxEXPAND, 10 ); SetTitle( _( "Convert to Rule Area" ) ); } m_isFpEditor = m_parent->IsType( FRAME_FOOTPRINT_EDITOR ); BOARD* board = m_parent->GetBoard(); LSET layers = LSET::AllNonCuMask() | LSET::AllCuMask( board->GetCopperLayerCount() ); m_zonesettings.SetupLayersList( m_layers, m_parent, layers, m_isFpEditor ); SetupStandardButtons(); finishDialogSettings(); } DIALOG_RULE_AREA_PROPERTIES::~DIALOG_RULE_AREA_PROPERTIES() { if( ADVANCED_CFG::GetCfg().m_EnableMultichannelTool ) { m_placementProperties->m_sourceSheetnameRb->Disconnect( wxEVT_RADIOBUTTON, wxCommandEventHandler( DIALOG_RULE_AREA_PROPERTIES::OnSheetNameRbClicked ), NULL, this ); m_placementProperties->m_sourceComponentClassRb->Disconnect( wxEVT_RADIOBUTTON, wxCommandEventHandler( DIALOG_RULE_AREA_PROPERTIES::OnComponentClassRbClicked ), NULL, this ); } } void DIALOG_RULE_AREA_PROPERTIES::OnSheetNameRbClicked( wxCommandEvent& event ) { if( m_currentKeepoutSource == RULE_AREA_PLACEMENT_SOURCE_TYPE::SHEETNAME ) return; m_zonesettings.SetRuleAreaPlacementSource( wxEmptyString ); m_zonesettings.SetRuleAreaPlacementSourceType( RULE_AREA_PLACEMENT_SOURCE_TYPE::SHEETNAME ); PopulatePlacementSourceList( RULE_AREA_PLACEMENT_SOURCE_TYPE::SHEETNAME ); m_currentKeepoutSource = RULE_AREA_PLACEMENT_SOURCE_TYPE::SHEETNAME; } void DIALOG_RULE_AREA_PROPERTIES::OnComponentClassRbClicked( wxCommandEvent& event ) { if( m_currentKeepoutSource == RULE_AREA_PLACEMENT_SOURCE_TYPE::COMPONENT_CLASS ) return; m_zonesettings.SetRuleAreaPlacementSource( wxEmptyString ); m_zonesettings.SetRuleAreaPlacementSourceType( RULE_AREA_PLACEMENT_SOURCE_TYPE::COMPONENT_CLASS ); PopulatePlacementSourceList( RULE_AREA_PLACEMENT_SOURCE_TYPE::COMPONENT_CLASS ); m_currentKeepoutSource = RULE_AREA_PLACEMENT_SOURCE_TYPE::COMPONENT_CLASS; } void DIALOG_RULE_AREA_PROPERTIES::PopulatePlacementSourceList( RULE_AREA_PLACEMENT_SOURCE_TYPE placementType ) { m_placementProperties->m_sourceObjectNameCb->Clear(); std::set* sourceStrings; switch( placementType ) { case RULE_AREA_PLACEMENT_SOURCE_TYPE::SHEETNAME: sourceStrings = &m_sheetNames; break; case RULE_AREA_PLACEMENT_SOURCE_TYPE::COMPONENT_CLASS: sourceStrings = &m_componentClasses; break; } int curSourceIdx = -1; const wxString& curSourceName = m_zonesettings.GetRuleAreaPlacementSource(); for( const wxString& sourceName : *sourceStrings ) { if( curSourceName == sourceName ) curSourceIdx = m_placementProperties->m_sourceObjectNameCb->GetCount(); m_placementProperties->m_sourceObjectNameCb->Append( sourceName ); } if( curSourceIdx != -1 ) { m_placementProperties->m_sourceObjectNameCb->Select( curSourceIdx ); } else if( curSourceName != wxEmptyString ) { m_notFoundPlacementSourceName = curSourceName; wxString notFoundDisplayName = _( "Not found on board: " ) + curSourceName; m_placementProperties->m_sourceObjectNameCb->Insert( notFoundDisplayName, 0 ); m_placementProperties->m_sourceObjectNameCb->Select( 0 ); } } bool DIALOG_RULE_AREA_PROPERTIES::TransferDataToWindow() { if( m_convertSettings ) { if( m_convertSettings->m_Strategy == BOUNDING_HULL ) m_rbBoundingHull->SetValue( true ); else m_rbCenterline->SetValue( true ); m_cbDeleteOriginals->SetValue( m_convertSettings->m_DeleteOriginals ); } // Init keepout parameters: m_keepoutProperties->m_cbTracksCtrl->SetValue( m_zonesettings.GetDoNotAllowTracks() ); m_keepoutProperties->m_cbViasCtrl->SetValue( m_zonesettings.GetDoNotAllowVias() ); m_keepoutProperties->m_cbPadsCtrl->SetValue( m_zonesettings.GetDoNotAllowPads() ); m_keepoutProperties->m_cbFootprintsCtrl->SetValue( m_zonesettings.GetDoNotAllowFootprints() ); m_keepoutProperties->m_cbCopperPourCtrl->SetValue( m_zonesettings.GetDoNotAllowCopperPour() ); // Init placement parameters: if( ADVANCED_CFG::GetCfg().m_EnableMultichannelTool ) { m_placementProperties->m_enabled->SetValue( m_zonesettings.GetRuleAreaPlacementEnabled() ); // Load schematic sheet and component class lists if( m_board ) { // Fetch component classes const std::unordered_map>& classes = m_board->GetComponentClassManager().GetClasses(); for( const auto& [k, v] : classes ) m_componentClasses.insert( k ); // Fetch sheet names for( FOOTPRINT* fp : m_board->Footprints() ) m_sheetNames.insert( fp->GetSheetname() ); if( m_zonesettings.GetRuleAreaPlacementSourceType() == RULE_AREA_PLACEMENT_SOURCE_TYPE::SHEETNAME ) { m_placementProperties->m_sourceComponentClassRb->SetValue( false ); m_placementProperties->m_sourceSheetnameRb->SetValue( true ); PopulatePlacementSourceList( RULE_AREA_PLACEMENT_SOURCE_TYPE::SHEETNAME ); m_currentKeepoutSource = RULE_AREA_PLACEMENT_SOURCE_TYPE::SHEETNAME; } else { m_placementProperties->m_sourceSheetnameRb->SetValue( false ); m_placementProperties->m_sourceComponentClassRb->SetValue( true ); PopulatePlacementSourceList( RULE_AREA_PLACEMENT_SOURCE_TYPE::COMPONENT_CLASS ); m_currentKeepoutSource = RULE_AREA_PLACEMENT_SOURCE_TYPE::COMPONENT_CLASS; } } // Handle most-useful notebook page selection m_areaPropertiesNb->SetSelection( 0 ); if( !m_zonesettings.HasKeepoutParametersSet() && m_zonesettings.GetRuleAreaPlacementEnabled() ) { m_areaPropertiesNb->SetSelection( 1 ); } } m_cbLocked->SetValue( m_zonesettings.m_Locked ); m_tcName->SetValue( m_zonesettings.m_Name ); switch( m_zonesettings.m_ZoneBorderDisplayStyle ) { case ZONE_BORDER_DISPLAY_STYLE::INVISIBLE_BORDER: // Not used for standard zones. Here use NO_HATCH case ZONE_BORDER_DISPLAY_STYLE::NO_HATCH: m_OutlineDisplayCtrl->SetSelection( 0 ); break; case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE: m_OutlineDisplayCtrl->SetSelection( 1 ); break; case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL: m_OutlineDisplayCtrl->SetSelection( 2 ); break; } m_outlineHatchPitch.SetValue( m_zonesettings.m_BorderHatchPitch ); SetInitialFocus( m_OutlineDisplayCtrl ); Layout(); return true; } void DIALOG_RULE_AREA_PROPERTIES::OnLayerSelection( wxDataViewEvent& event ) { if( event.GetColumn() != 0 ) return; int row = m_layers->ItemToRow( event.GetItem() ); wxVariant layerID; m_layers->GetValue( layerID, row, LAYER_LIST_COLUMN_NAME ); bool selected = m_layers->GetToggleValue( row, LAYER_LIST_COLUMN_CHECK ); // In footprint editor, we have only 3 possible layer selection: C_Cu, inner layers, B_Cu. // So row LAYER_LIST_ROW_ALL_INNER_LAYERS selection is fp editor specific. // in board editor, this row is a normal selection if( m_isFpEditor && row == LAYER_LIST_ROW_ALL_INNER_LAYERS ) { if( selected ) m_zonesettings.m_Layers |= LSET::InternalCuMask(); else m_zonesettings.m_Layers &= ~LSET::InternalCuMask(); } else { m_zonesettings.m_Layers.set( ToLAYER_ID( layerID.GetInteger() ), selected ); } } bool DIALOG_RULE_AREA_PROPERTIES::TransferDataFromWindow() { if( m_convertSettings ) { if( m_rbBoundingHull->GetValue() ) { m_convertSettings->m_Strategy = BOUNDING_HULL; m_convertSettings->m_Gap = m_gap->GetIntValue(); } else { m_convertSettings->m_Strategy = CENTERLINE; } m_convertSettings->m_DeleteOriginals = m_cbDeleteOriginals->GetValue(); } // Init keepout parameters: m_zonesettings.SetIsRuleArea( true ); m_zonesettings.SetDoNotAllowTracks( m_keepoutProperties->m_cbTracksCtrl->GetValue() ); m_zonesettings.SetDoNotAllowVias( m_keepoutProperties->m_cbViasCtrl->GetValue() ); m_zonesettings.SetDoNotAllowCopperPour( m_keepoutProperties->m_cbCopperPourCtrl->GetValue() ); m_zonesettings.SetDoNotAllowPads( m_keepoutProperties->m_cbPadsCtrl->GetValue() ); m_zonesettings.SetDoNotAllowFootprints( m_keepoutProperties->m_cbFootprintsCtrl->GetValue() ); // Init placement parameters if( ADVANCED_CFG::GetCfg().m_EnableMultichannelTool ) { m_zonesettings.SetRuleAreaPlacementEnabled( m_placementProperties->m_enabled->GetValue() ); if( m_placementProperties->m_sourceSheetnameRb->GetValue() ) { m_zonesettings.SetRuleAreaPlacementSourceType( RULE_AREA_PLACEMENT_SOURCE_TYPE::SHEETNAME ); } else { m_zonesettings.SetRuleAreaPlacementSourceType( RULE_AREA_PLACEMENT_SOURCE_TYPE::COMPONENT_CLASS ); } int selectedSourceIdx = m_placementProperties->m_sourceObjectNameCb->GetSelection(); if( selectedSourceIdx != wxNOT_FOUND ) { if( selectedSourceIdx == 0 && m_notFoundPlacementSourceName != wxEmptyString ) { m_zonesettings.SetRuleAreaPlacementSource( m_notFoundPlacementSourceName ); } else { m_zonesettings.SetRuleAreaPlacementSource( m_placementProperties->m_sourceObjectNameCb->GetString( selectedSourceIdx ) ); } } else { m_zonesettings.SetRuleAreaPlacementSource( wxEmptyString ); } } if( m_zonesettings.m_Layers.count() == 0 ) { DisplayError( this, _( "No layers selected." ) ); return false; } switch( m_OutlineDisplayCtrl->GetSelection() ) { case 0: m_zonesettings.m_ZoneBorderDisplayStyle = ZONE_BORDER_DISPLAY_STYLE::NO_HATCH; break; case 1: m_zonesettings.m_ZoneBorderDisplayStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE; break; case 2: m_zonesettings.m_ZoneBorderDisplayStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL; break; } if( !m_outlineHatchPitch.Validate( pcbIUScale.mmToIU( ZONE_BORDER_HATCH_MINDIST_MM ), pcbIUScale.mmToIU( ZONE_BORDER_HATCH_MAXDIST_MM ) ) ) return false; m_zonesettings.m_BorderHatchPitch = m_outlineHatchPitch.GetIntValue(); m_zonesettings.m_Locked = m_cbLocked->GetValue(); m_zonesettings.m_ZonePriority = 0; // for a keepout, this param is not used. m_zonesettings.m_Name = m_tcName->GetValue(); *m_ptr = m_zonesettings; return true; }