KiCad PCB EDA Suite
Loading...
Searching...
No Matches
panel_zone_properties.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2023 Ethan Chien <[email protected]>
5 * Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
6 * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
7 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
27
28#include <widgets/wx_infobar.h>
30#include <grid_tricks.h>
31#include <wx/radiobut.h>
32#include <kiface_base.h>
33#include <confirm.h>
34#include <pcb_edit_frame.h>
35#include <dialog_shim.h>
36#include <pcbnew_settings.h>
37#include <zone_settings_bag.h>
38#include <wx/string.h>
39#include <widgets/unit_binder.h>
41#include <widgets/wx_grid.h>
42#include <pad.h>
44
45
46wxDEFINE_EVENT( EVT_ZONE_NAME_UPDATE, wxCommandEvent );
47wxDEFINE_EVENT( EVT_ZONE_NET_UPDATE, wxCommandEvent );
48
50 ZONE_SETTINGS_BAG& aZonesSettingsBag, bool allowNetSpec ) :
52 m_frame( aFrame ),
53 m_zonesSettingsBag( aZonesSettingsBag ),
54 m_zone( nullptr ),
65{
66 m_netSelector->SetNetInfo( &m_frame->GetBoard()->GetNetInfo() );
67
68 if( !allowNetSpec )
69 {
70 m_netLabel->Hide();
71 m_netSelector->Hide();
72 }
73
75 [&]() -> LSET
76 {
77 return m_settings->m_Layers;
78 } );
79
82 m_layerSpecificOverrides->SetSelectionMode( wxGrid::wxGridSelectRows );
83
84 wxGridCellAttr* attr = new wxGridCellAttr;
85 attr->SetRenderer( new GRID_CELL_LAYER_RENDERER( nullptr ) );
86 LSET forbiddenLayers = LSET::AllNonCuMask();
87
88 for( PCB_LAYER_ID copper : LSET::AllCuMask() )
89 {
90 if( !m_frame->GetBoard()->IsLayerEnabled( copper ) )
91 forbiddenLayers.set( copper );
92 }
93
94 attr->SetEditor( new GRID_CELL_LAYER_SELECTOR( nullptr, forbiddenLayers ) );
95 m_layerSpecificOverrides->SetColAttr( 0, attr );
96 m_layerSpecificOverrides->SetupColumnAutosizer( 0 );
97
100
101 m_netSelector->Bind( FILTERED_ITEM_SELECTED, &PANEL_ZONE_PROPERTIES::onNetSelector, this );
102}
103
104
106{
107 // we passed ownership of table to grid
108 // delete m_layerPropsTable;
109
110 m_layerSpecificOverrides->PopEventHandler( true );
111
112 m_netSelector->Unbind( FILTERED_ITEM_SELECTED, &PANEL_ZONE_PROPERTIES::onNetSelector, this );
113}
114
115
117{
118 if( m_settings )
120
121 m_zone = aZone;
122 m_settings = m_zonesSettingsBag.GetZoneSettings( aZone );
124
126}
127
128
130{
131 if( !m_settings )
132 return false;
133
134 m_tcZoneName->ChangeValue( m_settings->m_Name );
135
136 if( m_netSelector->IsShown() )
137 m_netSelector->SetSelectedNetcode( std::max( 0, m_settings->m_Netcode ) );
138
139 m_cbLocked->SetValue( m_settings->m_Locked );
140 m_cornerSmoothingChoice->SetSelection( m_settings->GetCornerSmoothingType() );
141 m_cornerRadius.SetValue( m_settings->GetCornerRadius() );
142
143 if( m_isTeardrop ) // outlines are never smoothed: they have already the right shape
144 {
145 m_cornerSmoothingChoice->SetSelection( 0 );
146 m_cornerSmoothingChoice->Enable( false );
147 m_cornerRadius.Show( false );
148 }
149
150 switch( m_settings->m_ZoneBorderDisplayStyle )
151 {
152 case ZONE_BORDER_DISPLAY_STYLE::NO_HATCH: m_OutlineDisplayCtrl->SetSelection( 0 ); break;
156 }
157
158 m_outlineHatchPitch.SetValue( m_settings->m_BorderHatchPitch );
159
160 m_clearance.SetValue( m_settings->m_ZoneClearance );
161 m_minWidth.SetValue( m_settings->m_ZoneMinThickness );
162
163 switch( m_settings->GetPadConnection() )
164 {
165 default:
166 case ZONE_CONNECTION::THERMAL: m_PadInZoneOpt->SetSelection( 1 ); break;
167 case ZONE_CONNECTION::THT_THERMAL: m_PadInZoneOpt->SetSelection( 2 ); break;
168 case ZONE_CONNECTION::NONE: m_PadInZoneOpt->SetSelection( 3 ); break;
169 case ZONE_CONNECTION::FULL: m_PadInZoneOpt->SetSelection( 0 ); break;
170 }
171
172 if( m_isTeardrop )
173 {
174 m_PadInZoneOpt->SetSelection( 0 );
175 m_PadInZoneOpt->Enable( false );
176 m_antipadClearance.Enable( false );
177 m_spokeWidth.Enable( false );
178 }
179
180 // Do not enable/disable antipad clearance and spoke width. They might be needed if
181 // a footprint or pad overrides the zone to specify a thermal connection.
182 m_antipadClearance.SetValue( m_settings->m_ThermalReliefGap );
183 m_spokeWidth.SetValue( m_settings->m_ThermalReliefSpokeWidth );
184
186 m_islandThreshold.SetDoubleValue( static_cast<double>( m_settings->GetMinIslandArea() ) );
187
188 m_cbRemoveIslands->SetSelection( static_cast<int>( m_settings->GetIslandRemovalMode() ) );
189
191
193 m_gridStyleRotation.SetAngleValue( m_settings->m_HatchOrientation );
194 m_gridStyleThickness.SetValue( m_settings->m_HatchThickness );
195 m_gridStyleGap.SetValue( m_settings->m_HatchGap );
196
197 m_spinCtrlSmoothLevel->SetValue( m_settings->m_HatchSmoothingLevel );
198 m_spinCtrlSmoothValue->SetValue( m_settings->m_HatchSmoothingValue );
199
200 m_layerPropsTable->Clear();
201
202 for( PCB_LAYER_ID layer : LSET::AllCuMask().UIOrder() )
203 {
204 if( m_settings->m_LayerProperties.contains( layer ) )
205 {
206 ZONE_LAYER_PROPERTIES& props = m_settings->m_LayerProperties[layer];
207
208 if( props.hatching_offset.has_value() )
209 m_layerPropsTable->AddItem( layer, props );
210 }
211 }
212
213 // Enable/Disable some widgets
214 wxCommandEvent aEvent;
215 onNetSelector( aEvent );
217 OnRemoveIslandsSelection( aEvent );
218 onHatched( aEvent );
219
220 return true;
221}
222
223
225{
226 m_islandThreshold.Show( m_cbRemoveIslands->GetSelection() == 2 );
227
228 if( DIALOG_SHIM* dlg = dynamic_cast<DIALOG_SHIM*>( wxGetTopLevelParent( this ) ) )
229 dlg->Layout();
230}
231
232
234{
235 int selection = m_cornerSmoothingChoice->GetSelection();
236
237 if( selection == ZONE_SETTINGS::SMOOTHING_CHAMFER
238 || selection == ZONE_SETTINGS::SMOOTHING_FILLET )
239 {
240 m_cornerRadius.Show( true );
241 }
242 else
243 {
244 m_cornerRadius.Show( false );
245 }
246
247 if( DIALOG_SHIM* dlg = dynamic_cast<DIALOG_SHIM*>( wxGetTopLevelParent( this ) ) )
248 dlg->Layout();
249}
250
251
252void PANEL_ZONE_PROPERTIES::OnZoneNameChanged( wxCommandEvent& aEvent )
253{
254 // Propagate all the way out so that the MODEL_ZONES_OVERVIEW can pick it up
255 m_settings->m_Name = m_tcZoneName->GetValue();
256 m_zonesSettingsBag.GetZoneSettings( m_zone )->m_Name = m_settings->m_Name;
257
258 if( m_zone )
259 m_zone->SetZoneName( m_settings->m_Name );
260
261 wxCommandEvent* evt = new wxCommandEvent( EVT_ZONE_NAME_UPDATE );
262 wxQueueEvent( m_parent, evt );
263}
264
265
266void PANEL_ZONE_PROPERTIES::onNetSelector( wxCommandEvent& aEvent )
267{
268 // There is nothing to do if there is no zone selected which can happen when there are no zones on the board.
269 if( !m_netSelector || !m_netSelector->IsShown() )
270 return;
271
273
274 // Zones with no net never have islands removed
275 if( m_netSelector->GetSelectedNetcode() == INVALID_NET_CODE )
276 {
277 if( m_cbRemoveIslands && m_cbRemoveIslands->IsEnabled() && m_settings )
278 m_settings->SetIslandRemovalMode( (ISLAND_REMOVAL_MODE) m_cbRemoveIslands->GetSelection() );
279
280 m_cbRemoveIslands->SetSelection( 1 );
281 m_removeIslandsLabel->Enable( false );
282 m_cbRemoveIslands->Enable( false );
283 }
284 else if( !m_cbRemoveIslands->IsEnabled() )
285 {
287 m_cbRemoveIslands->SetSelection( static_cast<int>( m_settings->GetIslandRemovalMode() ) );
288
289 m_removeIslandsLabel->Enable( true );
290 m_cbRemoveIslands->Enable( true );
291 }
292
293 // Propagate all the way out so that the MODEL_ZONES_OVERVIEW can pick it up
294 if( m_settings )
295 m_settings->m_Netcode = m_netSelector->GetSelectedNetcode();
296
297 if( m_settings && m_zone )
298 {
299 m_zonesSettingsBag.GetZoneSettings( m_zone )->m_Netcode = m_settings->m_Netcode;
300 m_zone->SetNetCode( m_settings->m_Netcode, true );
301 }
302
303 wxCommandEvent* evt = new wxCommandEvent( EVT_ZONE_NET_UPDATE );
304 wxQueueEvent( m_parent, evt );
305}
306
307
309{
310 if( !CommitPendingChanges() )
311 return false;
312
313 if( !m_settings )
314 return false;
315
316 if( !AcceptOptions() )
317 return false;
318
319 return true;
320}
321
322
324{
325 return m_layerSpecificOverrides->CommitPendingChanges();
326}
327
328
329bool PANEL_ZONE_PROPERTIES::AcceptOptions( bool aUseExportableSetupOnly )
330{
331 if( !m_clearance.Validate( 0, pcbIUScale.mmToIU( ZONE_CLEARANCE_MAX_VALUE_MM ) ) )
332 return false;
333
334 if( !m_minWidth.Validate( pcbIUScale.mmToIU( ZONE_THICKNESS_MIN_VALUE_MM ), INT_MAX ) )
335 return false;
336
337 if( !m_cornerRadius.Validate( 0, INT_MAX ) )
338 return false;
339
340 if( !m_spokeWidth.Validate( 0, INT_MAX ) )
341 return false;
342
344
345 if( m_settings->m_FillMode == ZONE_FILL_MODE::HATCH_PATTERN )
346 {
347 int minThickness = m_minWidth.GetIntValue();
348
349 if( !m_gridStyleThickness.Validate( minThickness, INT_MAX ) )
350 return false;
351
352 if( !m_gridStyleGap.Validate( minThickness, INT_MAX ) )
353 return false;
354 }
355
356 switch( m_PadInZoneOpt->GetSelection() )
357 {
358 case 3: m_settings->SetPadConnection( ZONE_CONNECTION::NONE ); break;
359 case 2: m_settings->SetPadConnection( ZONE_CONNECTION::THT_THERMAL ); break;
360 case 1: m_settings->SetPadConnection( ZONE_CONNECTION::THERMAL ); break;
361 case 0: m_settings->SetPadConnection( ZONE_CONNECTION::FULL ); break;
362 }
363
364 switch( m_OutlineDisplayCtrl->GetSelection() )
365 {
366 case 0: m_settings->m_ZoneBorderDisplayStyle = ZONE_BORDER_DISPLAY_STYLE::NO_HATCH; break;
367 case 1: m_settings->m_ZoneBorderDisplayStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE; break;
368 case 2: m_settings->m_ZoneBorderDisplayStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL; break;
369 }
370
373 {
374 return false;
375 }
376
377 m_settings->m_BorderHatchPitch = m_outlineHatchPitch.GetIntValue();
378
379 m_settings->m_ZoneClearance = m_clearance.GetIntValue();
380 m_settings->m_ZoneMinThickness = m_minWidth.GetIntValue();
381
382 m_settings->SetCornerSmoothingType( m_cornerSmoothingChoice->GetSelection() );
383
384 if( m_settings->GetCornerSmoothingType() == ZONE_SETTINGS::SMOOTHING_NONE )
385 m_settings->SetCornerRadius( 0 );
386 else
387 m_settings->SetCornerRadius( m_cornerRadius.GetIntValue() );
388
389 m_settings->m_Locked = m_cbLocked->GetValue();
390
391 m_settings->m_ThermalReliefGap = m_antipadClearance.GetValue();
392 m_settings->m_ThermalReliefSpokeWidth = m_spokeWidth.GetValue();
393
394 if( m_settings->m_ThermalReliefSpokeWidth < m_settings->m_ZoneMinThickness )
395 {
396 DisplayErrorMessage( this, _( "Thermal spoke width cannot be smaller than the minimum width." ) );
397 return false;
398 }
399
400 m_settings->SetIslandRemovalMode( (ISLAND_REMOVAL_MODE) m_cbRemoveIslands->GetSelection() );
401 m_settings->SetMinIslandArea( m_islandThreshold.GetValue() );
402
403 // If we use only exportable to others zones parameters, exit here:
404 if( aUseExportableSetupOnly )
405 return true;
406
407 if( m_netSelector->IsShown() )
408 m_settings->m_Netcode = m_netSelector->GetSelectedNetcode();
409
410 m_settings->m_Name = m_tcZoneName->GetValue();
411
413 m_settings->m_HatchOrientation = m_gridStyleRotation.GetAngleValue();
414 m_settings->m_HatchThickness = m_gridStyleThickness.GetIntValue();
415 m_settings->m_HatchGap = m_gridStyleGap.GetIntValue();
416 m_settings->m_HatchSmoothingLevel = m_spinCtrlSmoothLevel->GetValue();
417 m_settings->m_HatchSmoothingValue = m_spinCtrlSmoothValue->GetValue();
418
419 for( auto& [layer, props] : m_settings->m_LayerProperties )
420 props.hatching_offset = std::nullopt;
421
422 for( const auto& [layer, props] : m_layerPropsTable->GetItems() )
423 m_settings->m_LayerProperties[layer] = props;
424
425 return true;
426}
427
428
429void PANEL_ZONE_PROPERTIES::onHatched( wxCommandEvent& event )
430{
431 bool enable = m_cbHatched->GetValue();
432 m_gridStyleThickness.Enable( enable );
433 m_gridStyleGap.Enable( enable );
434 m_gridStyleRotation.Enable( enable );
435 m_staticTextGridSmoothingLevel->Enable( enable );
436 m_spinCtrlSmoothLevel->Enable( enable );
437 m_staticTextGridSmootingVal->Enable( enable );
438 m_spinCtrlSmoothValue->Enable( enable );
439 m_offsetOverridesLabel->Enable( enable );
440 m_layerSpecificOverrides->Enable( enable );
441 m_bpAddCustomLayer->Enable( enable );
442 m_bpDeleteCustomLayer->Enable( enable );
443}
444
445
446void PANEL_ZONE_PROPERTIES::OnAddLayerItem( wxCommandEvent& event )
447{
448 m_layerSpecificOverrides->OnAddRow(
449 [&]() -> std::pair<int, int>
450 {
451 if( !m_layerSpecificOverrides->GetTable()->AppendRows( 1 ) )
452 {
453 m_copperZoneInfoBar->ShowMessageFor( _( "All zone layers already overridden." ), 8000,
454 wxICON_WARNING );
455 }
456
457 return { m_layerSpecificOverrides->GetNumberRows() - 1, -1 };
458 } );
459}
460
461
462void PANEL_ZONE_PROPERTIES::OnDeleteLayerItem( wxCommandEvent& event )
463{
464 m_layerSpecificOverrides->OnDeleteRows(
465 [&]( int row )
466 {
467 m_layerSpecificOverrides->GetTable()->DeleteRows( row, 1 );
468 } );
469}
470
471
473{
474 if( m_netSelector->GetSelectedNetcode() <= INVALID_NET_CODE && !m_copperZoneInfoBar->IsShown() )
475 {
476 m_copperZoneInfoBar->ShowMessage( _( "<no net> will result in an isolated copper island." ), wxICON_WARNING );
477 }
478 else if( m_copperZoneInfoBar->IsShown() )
479 {
480 m_copperZoneInfoBar->Dismiss();
481 }
482}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:125
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:110
BASE_SET & set(size_t pos)
Definition base_set.h:116
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition dialog_shim.h:68
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition grid_tricks.h:61
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static LSET AllNonCuMask()
Return a mask holding all layer minus CU layers.
Definition lset.cpp:627
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:599
PANEL_ZONE_PROPERTIES_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
void OnAddLayerItem(wxCommandEvent &event) override
static constexpr int INVALID_NET_CODE
void OnZoneNameChanged(wxCommandEvent &event) override
void onNetSelector(wxCommandEvent &aEvent)
bool AcceptOptions(bool aUseExportableSetupOnly=false)
PANEL_ZONE_PROPERTIES(wxWindow *aParent, PCB_BASE_FRAME *aFrame, ZONE_SETTINGS_BAG &aZonesSettingsBag, bool allowNetSpec=true)
void onHatched(wxCommandEvent &event) override
void OnRemoveIslandsSelection(wxCommandEvent &event) override
ZONE_SETTINGS_BAG & m_zonesSettingsBag
void OnDeleteLayerItem(wxCommandEvent &event) override
std::shared_ptr< ZONE_SETTINGS > m_settings
void OnCornerSmoothingSelection(wxCommandEvent &event) override
LAYER_PROPERTIES_GRID_TABLE * m_layerPropsTable
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
Handle a list of polygons defining a copper zone.
Definition zone.h:74
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:221
This file is part of the common library.
#define _(s)
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
wxDEFINE_EVENT(EVT_ZONE_NAME_UPDATE, wxCommandEvent)
std::optional< VECTOR2I > hatching_offset
T NormalizeAngle180(T Angle)
Normalize angle to be in the -180.0 .
Definition trigo.h:196
ISLAND_REMOVAL_MODE
Whether or not to remove isolated islands from a zone.
#define ZONE_CLEARANCE_MAX_VALUE_MM
Definition zones.h:37
@ THERMAL
Use thermal relief for pads.
Definition zones.h:50
@ THT_THERMAL
Thermal relief only for THT pads.
Definition zones.h:52
@ NONE
Pads are not covered.
Definition zones.h:49
@ FULL
pads are covered by copper
Definition zones.h:51
#define ZONE_BORDER_HATCH_MINDIST_MM
Definition zones.h:39
#define ZONE_THICKNESS_MIN_VALUE_MM
Definition zones.h:35
#define ZONE_BORDER_HATCH_MAXDIST_MM
Definition zones.h:40