KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_footprint_properties_fp_editor.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) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2015 Dick Hollenbeck, [email protected]
6 * Copyright (C) 2008 Wayne Stambaugh <[email protected]>
7 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
24
25#include <wx/debug.h>
26#include <wx/tokenzr.h>
27
30#include <bitmaps.h>
31#include <board.h>
32#include <board_commit.h>
34#include <confirm.h>
35#include <core/kicad_algo.h>
36
39#include <embedded_files.h>
40#include <filename_resolver.h>
41#include <footprint.h>
43#include <pad.h>
46#include <layer_utils.h>
47#include <kiplatform/ui.h>
50#include <pgm_base.h>
52#include <tool/tool_manager.h>
53#include <tools/pcb_actions.h>
55#include <validators.h>
59#include <widgets/wx_grid.h>
60#include <zone.h>
61
63#include <project_pcb.h>
64#include <kidialog.h>
65
66
67class LAYERS_GRID_TABLE : public WX_GRID_TABLE_BASE, public std::vector<PCB_LAYER_ID>
68{
69public:
70 LAYERS_GRID_TABLE( PCB_BASE_FRAME* aFrame, const LSET& aForbiddenLayers );
72
73 int GetNumberRows() override { return (int) size(); }
74 int GetNumberCols() override { return 1; }
75
76 bool CanGetValueAs( int aRow, int aCol, const wxString& aTypeName ) override;
77 bool CanSetValueAs( int aRow, int aCol, const wxString& aTypeName ) override;
78 wxGridCellAttr* GetAttr( int aRow, int aCol, wxGridCellAttr::wxAttrKind aKind ) override;
79
80 wxString GetValue( int aRow, int aCol ) override;
81 long GetValueAsLong( int aRow, int aCol ) override;
82
83 void SetValue( int aRow, int aCol, const wxString& aValue ) override;
84 void SetValueAsLong( int aRow, int aCol, long aValue ) override;
85
86private:
88 wxGridCellAttr* m_layerColAttr;
89};
90
91
92LAYERS_GRID_TABLE::LAYERS_GRID_TABLE( PCB_BASE_FRAME* aFrame, const LSET& aForbiddenLayers ) :
93 m_frame( aFrame )
94{
95 m_layerColAttr = new wxGridCellAttr;
96 m_layerColAttr->SetRenderer( new GRID_CELL_LAYER_RENDERER( m_frame ) );
97
98 m_layerColAttr->SetEditor( new GRID_CELL_LAYER_SELECTOR( m_frame, aForbiddenLayers, true ) );
99}
100
101
106
107
108bool LAYERS_GRID_TABLE::CanGetValueAs( int aRow, int aCol, const wxString& aTypeName )
109{
110 return aTypeName == wxGRID_VALUE_NUMBER;
111}
112
113
114bool LAYERS_GRID_TABLE::CanSetValueAs( int aRow, int aCol, const wxString& aTypeName )
115{
116 return aTypeName == wxGRID_VALUE_NUMBER;
117}
118
119
120wxGridCellAttr* LAYERS_GRID_TABLE::GetAttr( int aRow, int aCol, wxGridCellAttr::wxAttrKind aKind )
121{
122 m_layerColAttr->IncRef();
123 return enhanceAttr( m_layerColAttr, aRow, aCol, aKind );
124}
125
126
127wxString LAYERS_GRID_TABLE::GetValue( int aRow, int aCol )
128{
129 return m_frame->GetBoard()->GetLayerName( this->at( (size_t) aRow ) );
130}
131
132
133long LAYERS_GRID_TABLE::GetValueAsLong( int aRow, int aCol )
134{
135 return this->at( (size_t) aRow );
136}
137
138
139void LAYERS_GRID_TABLE::SetValue( int aRow, int aCol, const wxString& aValue )
140{
141 wxFAIL_MSG( wxString::Format( wxT( "column %d doesn't hold a string value" ), aCol ) );
142}
143
144
145void LAYERS_GRID_TABLE::SetValueAsLong( int aRow, int aCol, long aValue )
146{
147 this->at( (size_t) aRow ) = ToLAYER_ID( (int) aValue );
148}
149
150
151// Remember the last open page during session.
152
154
155
157 FOOTPRINT* aFootprint ) :
159 m_frame( aParent ),
160 m_footprint( aFootprint ),
161 m_initialized( false ),
165{
166 SetEvtHandlerEnabled( false );
167
168 // Create the extra panels.
171
172 m_NoteBook->AddPage( m_3dPanel, _("3D Models"), false );
173 m_NoteBook->AddPage( m_embeddedFiles, _( "Embedded Files" ) );
174
175 m_fields = new PCB_FIELDS_GRID_TABLE( m_frame, this, { m_embeddedFiles->GetLocalFiles() } );
176
177 {
178 LSET forbiddenLayers = LSET::AllCuMask() | LSET::AllTechMask();
179 forbiddenLayers.set( Edge_Cuts );
180 forbiddenLayers.set( Margin );
181
182 m_privateLayers = new LAYERS_GRID_TABLE( m_frame, forbiddenLayers );
183 }
184
185 {
186 LSET forbiddenLayers = LSET::AllLayersMask() & ~LSET::UserDefinedLayersMask();
187 m_customUserLayers = new LAYERS_GRID_TABLE( m_frame, forbiddenLayers );
188 }
189
190 m_delayedErrorMessage = wxEmptyString;
191 m_delayedFocusCtrl = nullptr;
192 m_delayedFocusGrid = nullptr;
196
197 // Give an icon
198 wxIcon icon;
199 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_modedit ) );
200 SetIcon( icon );
201
202 m_itemsGrid->SetTable( m_fields );
203 m_itemsGrid->OverrideMinSize( 1.0, 1.0 );
206
207 m_itemsGrid->PushEventHandler( new GRID_TRICKS( m_itemsGrid ) );
209 [this]( wxCommandEvent& aEvent )
210 {
211 OnAddPrivateLayer( aEvent );
212 } ) );
213 m_nettieGroupsGrid->PushEventHandler( new GRID_TRICKS( m_nettieGroupsGrid,
214 [this]( wxCommandEvent& aEvent )
215 {
216 OnAddNettieGroup( aEvent );
217 } ) );
218 m_jumperGroupsGrid->PushEventHandler( new GRID_TRICKS( m_jumperGroupsGrid,
219 [this]( wxCommandEvent& aEvent )
220 {
221 OnAddJumperGroup( aEvent );
222 } ) );
224 [this]( wxCommandEvent& aEvent )
225 {
226 OnAddCustomLayer( aEvent );
227 } ) );
228
229 m_itemsGrid->SetupColumnAutosizer( PFC_VALUE );
230 m_privateLayersGrid->SetupColumnAutosizer( 0 );
231 m_nettieGroupsGrid->SetupColumnAutosizer( 0 );
232 m_jumperGroupsGrid->SetupColumnAutosizer( 0 );
233 m_customUserLayersGrid->SetupColumnAutosizer( 0 );
234
235 m_itemsGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
236 m_privateLayersGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
237 m_nettieGroupsGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
238 m_jumperGroupsGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
239 m_customUserLayersGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
240
241 m_itemsGrid->ShowHideColumns( "0 1 2 3 4 5 7" );
242
244
245 // Set font sizes
246 wxFont infoFont = KIUI::GetInfoFont( this ).Italic();
247 m_staticTextInfoCopper->SetFont( infoFont );
248 m_staticTextInfoPaste->SetFont( infoFont );
249
250 if( static_cast<int>( m_page ) >= 0 )
251 m_NoteBook->SetSelection( (unsigned) m_page );
252
254 {
259 }
261 {
263 }
264
265 // Update label text and tooltip for combined offset + ratio field
266 m_SolderPasteMarginLabel->SetLabel( _( "Solder paste clearance:" ) );
267 m_SolderPasteMarginLabel->SetToolTip( _( "Local solder paste clearance for this footprint.\n"
268 "Enter an absolute value (e.g., -0.1mm), a percentage "
269 "(e.g., -5%), or both (e.g., -0.1mm - 5%).\n"
270 "If blank, the global value is used." ) );
271
272 // Hide the old ratio controls - they're no longer needed
273 m_PasteMarginRatioLabel->Show( false );
274 m_PasteMarginRatioCtrl->Show( false );
275 m_PasteMarginRatioUnits->Show( false );
276
277 // Configure button logos
288
290
292 SetEvtHandlerEnabled( true );
293}
294
295
297{
298 // Prevents crash bug in wxGrid's d'tor
299 m_itemsGrid->DestroyTable( m_fields );
300 m_privateLayersGrid->DestroyTable( m_privateLayers );
302
303 // Delete the GRID_TRICKS.
304 m_itemsGrid->PopEventHandler( true );
305 m_privateLayersGrid->PopEventHandler( true );
306 m_nettieGroupsGrid->PopEventHandler( true );
307 m_jumperGroupsGrid->PopEventHandler( true );
308 m_customUserLayersGrid->PopEventHandler( true );
309
310 m_page = static_cast<NOTEBOOK_PAGES>( m_NoteBook->GetSelection() );
311
312 // the GL canvas on the 3D models page has to be visible before it is destroyed
313 m_NoteBook->SetSelection( static_cast<int>( NOTEBOOK_PAGES::PAGE_3D_MODELS ) );
314}
315
316
318{
319 LIB_ID fpID = m_footprint->GetFPID();
320 wxString footprintName = fpID.GetLibItemName();
321
322 m_FootprintNameCtrl->ChangeValue( footprintName );
323
324 m_DocCtrl->SetValue( EscapeString( m_footprint->GetLibDescription(), CTX_LINE ) );
325 m_KeywordCtrl->SetValue( m_footprint->GetKeywords() );
326
327 if( !wxDialog::TransferDataToWindow() )
328 return false;
329
330 if( !m_PanelGeneral->TransferDataToWindow() )
331 return false;
332
333 // Add the models to the panel
334 if( !m_3dPanel->TransferDataToWindow() )
335 return false;
336
337 if( !m_embeddedFiles->TransferDataToWindow() )
338 return false;
339
340 // Footprint Fields
341 for( PCB_FIELD* field : m_footprint->GetFields() )
342 {
343 wxCHECK2( field, continue );
344
345 m_fields->push_back( *field );
346 }
347
348 // Notify the grid
349 wxGridTableMessage tmsg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_fields->GetNumberRows() );
350 m_itemsGrid->ProcessTableMessage( tmsg );
351
352 if( m_footprint->GetAttributes() & FP_THROUGH_HOLE )
353 m_componentType->SetSelection( 0 );
354 else if( m_footprint->GetAttributes() & FP_SMD )
355 m_componentType->SetSelection( 1 );
356 else
357 m_componentType->SetSelection( 2 );
358
359 // Private layers
360 for( PCB_LAYER_ID privateLayer : m_footprint->GetPrivateLayers().UIOrder() )
361 m_privateLayers->push_back( privateLayer );
362
363 // Notify the grid
364 wxGridTableMessage gridTableMessagesg( m_privateLayers, wxGRIDTABLE_NOTIFY_ROWS_APPENDED,
365 m_privateLayers->GetNumberRows() );
366 m_privateLayersGrid->ProcessTableMessage( gridTableMessagesg );
367
368 switch( m_footprint->GetStackupMode() )
369 {
371 {
372 m_cbCustomLayers->SetValue( false );
373
374 m_copperLayerCount->SetSelection( 0 );
375 break;
376 }
378 {
379 m_cbCustomLayers->SetValue( true );
380
381 const LSET& customFpLayers = m_footprint->GetStackupLayers();
382 const LSET customUserLayers = customFpLayers & LSET::UserDefinedLayersMask();
383
384 for( PCB_LAYER_ID customUserLayer : customUserLayers )
385 {
386 m_customUserLayers->push_back( customUserLayer );
387 }
388
389 // Set the number of copper layers
390 m_copperLayerCount->SetSelection( ( customFpLayers & LSET::AllCuMask() ).count() / 2 - 1 );
391 break;
392 }
393 }
395
396 // Notify the grid
397 {
398 wxGridTableMessage gridTableMessagesCustom( m_customUserLayers, wxGRIDTABLE_NOTIFY_ROWS_APPENDED,
399 m_customUserLayers->GetNumberRows() );
400 m_customUserLayersGrid->ProcessTableMessage( gridTableMessagesCustom );
401 }
402
403 m_boardOnly->SetValue( m_footprint->GetAttributes() & FP_BOARD_ONLY );
404 m_cbExcludeFromSim->SetValue( m_footprint->GetAttributes() & FP_EXCLUDE_FROM_SIM );
405 m_excludeFromPosFiles->SetValue( m_footprint->GetAttributes() & FP_EXCLUDE_FROM_POS_FILES );
406 m_excludeFromBOM->SetValue( m_footprint->GetAttributes() & FP_EXCLUDE_FROM_BOM );
407 m_cbDNP->SetValue( m_footprint->GetAttributes() & FP_DNP );
408
409 // Local Clearances
410
411 if( m_footprint->GetLocalClearance().has_value() )
412 m_netClearance.SetValue( m_footprint->GetLocalClearance().value() );
413 else
414 m_netClearance.SetValue( wxEmptyString );
415
416 if( m_footprint->GetLocalSolderMaskMargin().has_value() )
417 m_solderMask.SetValue( m_footprint->GetLocalSolderMaskMargin().value() );
418 else
419 m_solderMask.SetValue( wxEmptyString );
420
421 m_solderPaste.SetOffsetValue( m_footprint->GetLocalSolderPasteMargin() );
422 m_solderPaste.SetRatioValue( m_footprint->GetLocalSolderPasteMarginRatio() );
423
424 m_noCourtyards->SetValue( m_footprint->AllowMissingCourtyard() );
425 m_allowBridges->SetValue( m_footprint->AllowSolderMaskBridges() );
426
427 switch( m_footprint->GetLocalZoneConnection() )
428 {
429 default:
430 case ZONE_CONNECTION::INHERITED: m_ZoneConnectionChoice->SetSelection( 0 ); break;
431 case ZONE_CONNECTION::FULL: m_ZoneConnectionChoice->SetSelection( 1 ); break;
432 case ZONE_CONNECTION::THERMAL: m_ZoneConnectionChoice->SetSelection( 2 ); break;
433 case ZONE_CONNECTION::NONE: m_ZoneConnectionChoice->SetSelection( 3 ); break;
434 }
435
436 for( const wxString& group : m_footprint->GetNetTiePadGroups() )
437 {
438 if( !group.IsEmpty() )
439 {
440 m_nettieGroupsGrid->AppendRows( 1 );
441 m_nettieGroupsGrid->SetCellValue( m_nettieGroupsGrid->GetNumberRows() - 1, 0, group );
442 }
443 }
444
445 m_cbDuplicatePadsAreJumpers->SetValue( m_footprint->GetDuplicatePadNumbersAreJumpers() );
446
447 for( const std::set<wxString>& group : m_footprint->JumperPadGroups() )
448 {
449 wxString groupTxt;
450
451 for( const wxString& pinNumber : group )
452 {
453 if( !groupTxt.IsEmpty() )
454 groupTxt << ", ";
455
456 groupTxt << pinNumber;
457 }
458
459 m_jumperGroupsGrid->AppendRows( 1 );
460 m_jumperGroupsGrid->SetCellValue( m_jumperGroupsGrid->GetNumberRows() - 1, 0, groupTxt );
461 }
462
463 // Items grid
464 for( int col = 0; col < m_itemsGrid->GetNumberCols(); col++ )
465 {
466 // Adjust min size to the column label size
467 m_itemsGrid->SetColMinimalWidth( col, m_itemsGrid->GetVisibleWidth( col, true, false ) );
468 // Adjust the column size.
469 int col_size = m_itemsGrid->GetVisibleWidth( col );
470
471 if( col == PFC_LAYER ) // This one's a drop-down. Check all possible values.
472 {
473 BOARD* board = m_footprint->GetBoard();
474
475 for( PCB_LAYER_ID layer : board->GetEnabledLayers() )
476 col_size = std::max( col_size, GetTextExtent( board->GetLayerName( layer ) ).x );
477
478 // Swatch and gaps:
479 col_size += KiROUND( 14 * GetDPIScaleFactor() ) + 12;
480 }
481
482 if( m_itemsGrid->IsColShown( col ) )
483 m_itemsGrid->SetColSize( col, col_size );
484 }
485
486 m_itemsGrid->SetRowLabelSize( 0 );
487
488 Layout();
489 m_itemsGrid->SetMinVisibleRows( this, 4 );
490 m_initialized = true;
491
492 return true;
493}
494
495
497 LIB_ID* doOverwrite )
498{
499 if( aFootprintName.IsEmpty() )
500 {
501 m_delayedErrorMessage = _( "Footprint must have a name." );
502 return false;
503 }
504 else if( !FOOTPRINT::IsLibNameValid( aFootprintName ) )
505 {
506 m_delayedErrorMessage.Printf( _( "Footprint name may not contain '%s'." ),
508 return false;
509 }
510
511 LIB_ID fpID = m_footprint->GetFPID();
512 wxString libraryName = fpID.GetLibNickname();
513 wxString originalFPName = fpID.GetLibItemName();
514
516
517 if( aFootprintName != originalFPName && adapter->FootprintExists( libraryName, aFootprintName ) )
518 {
519 wxString msg = wxString::Format( _( "Footprint '%s' already exists in library '%s'." ),
520 aFootprintName, libraryName );
521
522 KIDIALOG errorDlg( m_frame, msg, _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
523 errorDlg.SetOKLabel( _( "Overwrite" ) );
524
525 if( errorDlg.ShowModal() == wxID_OK )
526 {
527 doOverwrite->SetLibNickname( libraryName );
528 doOverwrite->SetLibItemName( aFootprintName );
529 return true;
530 }
531 }
532
533 return true;
534}
535
536
538{
539 LSET userLayers;
540 if( m_cbCustomLayers->GetValue() )
541 {
542 userLayers |= LSET::AllCuMask( ( m_copperLayerCount->GetSelection() + 1 ) * 2 );
543
544 for( PCB_LAYER_ID layer : *m_customUserLayers )
545 {
546 userLayers.set( layer );
547 }
548 }
549 else
550 {
551 // The default stackup restricts nothing, so every standard layer is available.
552 userLayers |= LSET{ F_Cu, B_Cu };
553 userLayers |= LSET::InternalCuMask();
554 userLayers |= LSET::UserDefinedLayersMask();
555 }
556
557 return userLayers;
558}
559
560
562{
563 if( !m_itemsGrid->CommitPendingChanges() )
564 return false;
565
566 if( !DIALOG_SHIM::Validate() )
567 return false;
568
569 // First, test for invalid chars in footprint name
570 wxString footprintName = m_FootprintNameCtrl->GetValue();
571 LIB_ID overwrite;
572
573 if( !checkFootprintName( footprintName, &overwrite ) )
574 {
575 if( m_NoteBook->GetSelection() != 0 )
576 m_NoteBook->SetSelection( 0 );
577
580
581 return false;
582 }
583
584 // Check for valid field text properties
585 for( int i = 0; i < (int) m_fields->size(); ++i )
586 {
587 PCB_FIELD& field = m_fields->at( i );
588
589 // Check for missing field names.
590 if( field.GetName( false ).IsEmpty() )
591 {
593 m_delayedErrorMessage = wxString::Format( _( "Fields must have a name." ) );
596
597 return false;
598 }
599
600 int minSize = pcbIUScale.mmToIU( TEXT_MIN_SIZE_MM );
601 int maxSize = pcbIUScale.mmToIU( TEXT_MAX_SIZE_MM );
602
603 if( field.GetTextWidth() < minSize || field.GetTextWidth() > maxSize )
604 {
606 m_delayedErrorMessage = wxString::Format( _( "The text width must be between %s and %s." ),
607 m_frame->StringFromValue( minSize, true ),
608 m_frame->StringFromValue( maxSize, true ) );
611
612 return false;
613 }
614
615 if( field.GetTextHeight() < minSize || field.GetTextHeight() > maxSize )
616 {
618 m_delayedErrorMessage = wxString::Format( _( "The text height must be between %s and %s." ),
619 m_frame->StringFromValue( minSize, true ),
620 m_frame->StringFromValue( maxSize, true ) );
623
624 return false;
625 }
626
627 // Test for acceptable values for thickness and size and clamp if fails
628 int maxPenWidth = ClampTextPenSize( field.GetTextThickness(), field.GetTextSize() );
629
630 if( field.GetTextThickness() > maxPenWidth )
631 {
632 m_itemsGrid->SetCellValue( i, PFC_THICKNESS, m_frame->StringFromValue( maxPenWidth, true ) );
633
635 m_delayedErrorMessage = _( "The text thickness is too large for the text size.\n"
636 "It will be clamped." );
639
640 return false;
641 }
642 }
643
644 if( !m_netClearance.Validate( 0, INT_MAX ) )
645 return false;
646
647 if( overwrite.IsValid() )
648 {
649 if( m_frame->DeleteFootprintFromLibrary( overwrite, false /* already confirmed */ ) )
650 m_frame->SyncLibraryTree( true );
651 }
652
653 // A custom layer set restricts which layers the footprint may use, so check that the user
654 // isn't removing a layer that is still used by the footprint. The default stackup imposes no
655 // such restriction, so nothing can be orphaned in that case.
656 if( m_cbCustomLayers->GetValue() )
657 {
660
661 if( orphanLayers.any() )
662 {
664 wxString::Format( _( "You are trying to remove layers that are used by the footprint: %s.\n"
665 "Please remove the objects that use these layers first." ),
666 LAYER_UTILS::AccumulateNames( orphanLayers, m_frame->GetBoard() ) );
671 return false;
672 }
673 }
674
675 return true;
676}
677
678
680{
681 if( !m_itemsGrid->CommitPendingChanges()
682 || !m_privateLayersGrid->CommitPendingChanges()
683 || !m_nettieGroupsGrid->CommitPendingChanges()
684 || !m_jumperGroupsGrid->CommitPendingChanges()
685 || !m_customUserLayersGrid->CommitPendingChanges() )
686 {
687 return false;
688 }
689
690 KIGFX::PCB_VIEW* view = m_frame->GetCanvas()->GetView();
691 PCB_SELECTION_TOOL* selectionTool = m_frame->GetToolManager()->GetTool<PCB_SELECTION_TOOL>();
692 BOARD_COMMIT commit( m_frame );
693 commit.Modify( m_footprint );
694
695 // Must be done inside the commit to capture the undo state
696 // This will call TransferDataToWindow() on the 3D panel and
697 // the embedded files panel.
698 if( !DIALOG_SHIM::TransferDataFromWindow() )
699 return false;
700
701 // Clear out embedded files that are no longer in use
702 std::set<wxString> files;
703 std::set<wxString> files_to_delete;
704
705 // Get the new files from the footprint fields
706 for( const PCB_FIELD& field : *m_fields )
707 {
708 if( field.GetText().StartsWith( FILEEXT::KiCadUriPrefix ) )
709 files.insert( field.GetText() );
710 }
711
712 // Find any files referenced in the old fields that are not in the new fields
713 for( PCB_FIELD* field : m_footprint->GetFields() )
714 {
715 wxCHECK2( field, continue );
716
717 if( field->GetText().StartsWith( FILEEXT::KiCadUriPrefix ) )
718 {
719 if( files.find( field->GetText() ) == files.end() )
720 files_to_delete.insert( field->GetText() );
721 }
722 }
723
724 for( const wxString& file : files_to_delete )
725 {
726 wxString name = file.Mid( FILEEXT::KiCadUriPrefix.size() + 3 ); // Skip "kicad-embed://"
727 m_footprint->RemoveFile( name );
728 }
729
730 LIB_ID fpID = m_footprint->GetFPID();
731 fpID.SetLibItemName( m_FootprintNameCtrl->GetValue() );
732 m_footprint->SetFPID( fpID );
733
734 m_footprint->SetLibDescription( UnescapeString( m_DocCtrl->GetValue() ) );
735 m_footprint->SetKeywords( m_KeywordCtrl->GetValue() );
736
737 // Update fields
738 m_frame->GetToolManager()->RunAction( ACTIONS::selectionClear );
739
740 std::vector<PCB_FIELD*> addedFields;
741 std::vector<PCB_FIELD*> detachedFields;
742
743 m_footprint->UpdateFields( *m_fields, addedFields, detachedFields );
744
745 for( PCB_FIELD* field : detachedFields )
746 {
747 view->Remove( field );
748 delete field;
749 }
750
751 for( PCB_FIELD* field : m_footprint->GetFields() )
752 {
753 // Reused fields are already known to the view
754 if( alg::contains( addedFields, field ) )
755 view->Add( field );
756 else
757 view->Update( field );
758
759 if( field->IsSelected() )
760 {
761 // The old copy was in the selection list, but this one is not. Remove the
762 // out-of-sync selection flag so we can re-add the field to the selection.
763 field->ClearSelected();
764 selectionTool->AddItemToSel( field, true );
765 }
766 }
767
768 LSET privateLayers;
769
770 for( PCB_LAYER_ID layer : *m_privateLayers )
771 privateLayers.set( layer );
772
773 m_footprint->SetPrivateLayers( privateLayers );
774
775 if( m_cbCustomLayers->GetValue() )
776 {
777 const LSET customLayers = getCustomLayersFromControls();
778
780 m_footprint->SetStackupLayers( std::move( customLayers ) );
781 }
782 else
783 {
784 // Just use the default stackup mode
786 }
787
788 int attributes = 0;
789
790 switch( m_componentType->GetSelection() )
791 {
792 case 0: attributes |= FP_THROUGH_HOLE; break;
793 case 1: attributes |= FP_SMD; break;
794 default: break;
795 }
796
797 if( m_boardOnly->GetValue() )
798 attributes |= FP_BOARD_ONLY;
799
800 if( m_cbExcludeFromSim->GetValue() )
801 attributes |= FP_EXCLUDE_FROM_SIM;
802
803 if( m_excludeFromPosFiles->GetValue() )
804 attributes |= FP_EXCLUDE_FROM_POS_FILES;
805
806 if( m_excludeFromBOM->GetValue() )
807 attributes |= FP_EXCLUDE_FROM_BOM;
808
809 if( m_cbDNP->GetValue() )
810 attributes |= FP_DNP;
811
812 m_footprint->SetAttributes( attributes );
813
814 m_footprint->SetAllowMissingCourtyard( m_noCourtyards->GetValue() );
815 m_footprint->SetAllowSolderMaskBridges( m_allowBridges->GetValue() );
816
817 // Initialize mask clearances
818 if( m_netClearance.IsNull() )
819 m_footprint->SetLocalClearance( {} );
820 else
821 m_footprint->SetLocalClearance( m_netClearance.GetValue() );
822
823 if( m_solderMask.IsNull() )
824 m_footprint->SetLocalSolderMaskMargin( {} );
825 else
826 m_footprint->SetLocalSolderMaskMargin( m_solderMask.GetValue() );
827
828 m_footprint->SetLocalSolderPasteMargin( m_solderPaste.GetOffsetValue() );
829 m_footprint->SetLocalSolderPasteMarginRatio( m_solderPaste.GetRatioValue() );
830
831 switch( m_ZoneConnectionChoice->GetSelection() )
832 {
833 default:
834 case 0: m_footprint->SetLocalZoneConnection( ZONE_CONNECTION::INHERITED ); break;
835 case 1: m_footprint->SetLocalZoneConnection( ZONE_CONNECTION::FULL ); break;
836 case 2: m_footprint->SetLocalZoneConnection( ZONE_CONNECTION::THERMAL ); break;
837 case 3: m_footprint->SetLocalZoneConnection( ZONE_CONNECTION::NONE ); break;
838 }
839
840 m_footprint->ClearNetTiePadGroups();
841
842 for( int ii = 0; ii < m_nettieGroupsGrid->GetNumberRows(); ++ii )
843 {
844 wxString group = m_nettieGroupsGrid->GetCellValue( ii, 0 );
845
846 if( !group.IsEmpty() )
847 m_footprint->AddNetTiePadGroup( group );
848 }
849
850 m_footprint->SetDuplicatePadNumbersAreJumpers( m_cbDuplicatePadsAreJumpers->GetValue() );
851
852 std::set<wxString> availablePads;
853
854 for( const PAD* pad : m_footprint->Pads() )
855 availablePads.insert( pad->GetNumber() );
856
857 std::vector<std::set<wxString>> newJumpers;
858
859 for( int ii = 0; ii < m_jumperGroupsGrid->GetNumberRows(); ++ii )
860 {
861 wxStringTokenizer tokenizer( m_jumperGroupsGrid->GetCellValue( ii, 0 ), ", \t\r\n", wxTOKEN_STRTOK );
862 std::set<wxString>& group = newJumpers.emplace_back();
863
864 while( tokenizer.HasMoreTokens() )
865 {
866 wxString token = tokenizer.GetNextToken();
867
868 if( token.IsEmpty() )
869 continue;
870
871 if( !availablePads.count( token ) )
872 {
873 wxString msg;
874 msg.Printf( _( "Pad '%s' in jumper pad group %d does not exist in this footprint." ),
875 token, ii + 1 );
876 DisplayErrorMessage( this, msg );
877 return false;
878 }
879
880 group.insert( token );
881 }
882 }
883
884 m_footprint->JumperPadGroups() = std::move( newJumpers );
885
886 // Copy the models from the panel to the footprint
887 std::vector<FP_3DMODEL>& panelList = m_3dPanel->GetModelList();
888 std::vector<FP_3DMODEL>* fpList = &m_footprint->Models();
889 fpList->clear();
890 fpList->insert( fpList->end(), panelList.begin(), panelList.end() );
891
892 commit.Push( _( "Edit Footprint Properties" ) );
893
894 return true;
895}
896
897
899{
900 m_itemsGrid->OnAddRow(
901 [&]() -> std::pair<int, int>
902 {
903 const BOARD_DESIGN_SETTINGS& dsnSettings = m_frame->GetDesignSettings();
904
906
907 // Set active layer if legal; otherwise copy layer from previous text item
908 if( LSET::AllTechMask().test( m_frame->GetActiveLayer() ) )
909 newField.SetLayer( m_frame->GetActiveLayer() );
910 else
911 newField.SetLayer( m_fields->at( m_fields->size() - 1 ).GetLayer() );
912
913 newField.SetTextSize( dsnSettings.GetTextSize( newField.GetLayer() ) );
914 newField.SetTextThickness( dsnSettings.GetTextThickness( newField.GetLayer() ) );
915 newField.SetItalic( dsnSettings.GetTextItalic( newField.GetLayer() ) );
916
917 m_fields->push_back( newField );
918
919 // notify the grid
920 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
921 m_itemsGrid->ProcessTableMessage( msg );
922 OnModify();
923
924 return { m_fields->size() - 1, PFC_NAME };
925 } );
926}
927
928
930{
931 m_itemsGrid->OnDeleteRows(
932 [&]( int row )
933 {
934 if( row < m_fields->GetMandatoryRowCount() )
935 {
936 DisplayError( this, wxString::Format( _( "The first %d fields are mandatory." ),
937 m_fields->GetMandatoryRowCount() ) );
938 return false;
939 }
940
941 return true;
942 },
943 [&]( int row )
944 {
945 m_fields->erase( m_fields->begin() + row );
946
947 // notify the grid
948 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, row, 1 );
949 m_itemsGrid->ProcessTableMessage( msg );
950 } );
951
952 OnModify();
953}
954
955
957 int aRow )
958{
959 aLayerTable.erase( aLayerTable.begin() + aRow );
960
961 // notify the grid
962 wxGridTableMessage msg( &aLayerTable, wxGRIDTABLE_NOTIFY_ROWS_DELETED, aRow, 1 );
963 aGrid.ProcessTableMessage( msg );
964
965 OnModify();
966}
967
968
970 LAYERS_GRID_TABLE& aGridTable )
971{
972 PCB_LAYER_ID nextLayer = User_1;
973
974 while( alg::contains( aGridTable, nextLayer ) && nextLayer < User_45 )
975 nextLayer = ToLAYER_ID( nextLayer + 2 );
976
977 aGridTable.push_back( nextLayer );
978
979 // notify the grid
980 wxGridTableMessage msg( &aGridTable, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
981 aGrid.ProcessTableMessage( msg );
982 OnModify();
983
984 return { aGridTable.size() - 1, -1 };
985}
986
987
989{
990 m_privateLayersGrid->OnAddRow(
991 [&]()
992 {
994 } );
995}
996
997
999{
1000 m_privateLayersGrid->OnDeleteRows(
1001 [&]( int row )
1002 {
1004 } );
1005}
1006
1007
1012
1013
1015{
1016 m_customUserLayersGrid->OnAddRow(
1017 [&]()
1018 {
1020 } );
1021}
1022
1023
1025{
1026 m_customUserLayersGrid->OnDeleteRows(
1027 [&]( int row )
1028 {
1030 } );
1031}
1032
1033
1038
1039
1044
1045
1050
1051
1056
1057
1059{
1060 aGrid->OnAddRow(
1061 [&]() -> std::pair<int, int>
1062 {
1063 aGrid->AppendRows( 1 );
1064 OnModify();
1065
1066 return { aGrid->GetNumberRows() - 1, 0 };
1067 } );
1068}
1069
1070
1072{
1073 aGrid->OnDeleteRows(
1074 [&]( int row )
1075 {
1076 aGrid->DeleteRows( row, 1 );
1077 } );
1078
1079 OnModify();
1080}
1081
1082
1084{
1085 // Handle a delayed focus. The delay allows us to:
1086 // a) change focus when the error was triggered from within a killFocus handler
1087 // b) show the correct notebook page in the background before the error dialog comes up
1088 // when triggered from an OK or a notebook page change
1089
1090 if( static_cast<int>( m_delayedFocusPage ) >= 0 )
1091 {
1092 if( m_NoteBook->GetSelection() != static_cast<int>( m_delayedFocusPage ) )
1093 m_NoteBook->ChangeSelection( static_cast<int>( m_delayedFocusPage ) );
1094
1096 }
1097
1098 if( !m_delayedErrorMessage.IsEmpty() )
1099 {
1100 // We will re-enter this routine when the error dialog is displayed, so make
1101 // sure we don't keep putting up more dialogs.
1102 wxString msg = m_delayedErrorMessage;
1103 m_delayedErrorMessage = wxEmptyString;
1104
1105 // Do not use DisplayErrorMessage(); it screws up window order on Mac
1106 DisplayError( nullptr, msg );
1107 }
1108
1109 if( m_delayedFocusCtrl )
1110 {
1111 m_delayedFocusCtrl->SetFocus();
1112
1113 if( wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_delayedFocusCtrl ) )
1114 textEntry->SelectAll();
1115
1116 m_delayedFocusCtrl = nullptr;
1117 }
1118 else if( m_delayedFocusGrid )
1119 {
1120 m_delayedFocusGrid->SetFocus();
1123
1124 if( !( m_delayedFocusColumn == 0 && m_delayedFocusRow < m_fields->GetMandatoryRowCount() ) )
1125 m_delayedFocusGrid->EnableCellEditControl( true );
1126
1127 m_delayedFocusGrid->ShowCellEditControl();
1128
1129 m_delayedFocusGrid = nullptr;
1130 m_delayedFocusRow = -1;
1132 }
1133}
1134
1135
1137{
1138 bool enableCustomCtrls = m_cbCustomLayers->GetValue();
1139
1140 m_copperLayerCount->Enable( enableCustomCtrls );
1141 m_customUserLayersGrid->Enable( enableCustomCtrls );
1142 m_bpAddCustomLayer->Enable( enableCustomCtrls );
1143 m_bpDeleteCustomLayer->Enable( enableCustomCtrls );
1144}
1145
1146
1148{
1149 if( !m_itemsGrid->CommitPendingChanges() )
1150 aEvent.Veto();
1151
1152 if( !m_privateLayersGrid->CommitPendingChanges() )
1153 aEvent.Veto();
1154
1155 if( !m_customUserLayersGrid->CommitPendingChanges() )
1156 aEvent.Veto();
1157}
1158
1159
1161{
1162 if( m_initialized )
1163 OnModify();
1164}
1165
1166
1168{
1169 if( m_initialized )
1170 OnModify();
1171}
1172
1173
1175{
1176 if( m_initialized )
1177 OnModify();
1178}
const char * name
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition bitmap.cpp:100
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:995
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
BASE_SET & set(size_t pos)
Definition base_set.h:126
Container for design settings for a BOARD object.
int GetTextThickness(PCB_LAYER_ID aLayer) const
Return the default text thickness from the layer class for the given layer.
bool GetTextItalic(PCB_LAYER_ID aLayer) const
VECTOR2I GetTextSize(PCB_LAYER_ID aLayer) const
Return the default text size from the layer class for the given layer.
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:374
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:409
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition board.cpp:936
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:1183
DIALOG_FOOTPRINT_PROPERTIES_FP_EDITOR_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Footprint Properties"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
DIALOG_FOOTPRINT_PROPERTIES_FP_EDITOR(FOOTPRINT_EDIT_FRAME *aParent, FOOTPRINT *aFootprint)
LSET getCustomLayersFromControls() const
Get the layers for the footprint from the controls that can be affected by the stackup.
bool checkFootprintName(const wxString &aFootprintName, LIB_ID *doOverwrite)
void onLayerGridRowDelete(WX_GRID &aGrid, LAYERS_GRID_TABLE &aLayerTable, int aRow)
std::pair< int, int > onLayerGridRowAddUserLayer(WX_GRID &aGrid, LAYERS_GRID_TABLE &aLayerTable)
void SetInitialFocus(wxWindow *aWindow)
Sets the window (usually a wxTextCtrl) that should be focused when the dialog is shown.
Definition dialog_shim.h:94
void SetupStandardButtons(std::map< int, wxString > aLabels={})
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
virtual int GetTextHeight() const
Definition eda_text.h:307
virtual int GetTextWidth() const
Definition eda_text.h:304
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:285
An interface to the global shared library manager that is schematic-specific and linked to one projec...
bool FootprintExists(const wxString &aNickname, const wxString &aName)
Provide a custom wxValidator object for limiting the allowable characters when defining footprint nam...
Definition validators.h:49
static bool IsLibNameValid(const wxString &aName)
Test for validity of a name of a footprint to be used in a footprint library ( no spaces,...
static const wxChar * StringLibNameInvalidChars(bool aUserReadable)
Test for validity of the name in a library of the footprint ( no spaces, dir separators ....
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition grid_tricks.h:57
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition kidialog.h:38
int ShowModal() override
Definition kidialog.cpp:89
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const override
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition pcb_view.cpp:87
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1) override
Add a VIEW_ITEM to the view.
Definition pcb_view.cpp:53
virtual void Remove(VIEW_ITEM *aItem) override
Remove a VIEW_ITEM from the view.
Definition pcb_view.cpp:70
LAYERS_GRID_TABLE(PCB_BASE_FRAME *aFrame, const LSET &aForbiddenLayers)
wxString GetValue(int aRow, int aCol) override
bool CanGetValueAs(int aRow, int aCol, const wxString &aTypeName) override
void SetValueAsLong(int aRow, int aCol, long aValue) override
long GetValueAsLong(int aRow, int aCol) override
bool CanSetValueAs(int aRow, int aCol, const wxString &aTypeName) override
void SetValue(int aRow, int aCol, const wxString &aValue) override
wxGridCellAttr * GetAttr(int aRow, int aCol, wxGridCellAttr::wxAttrKind aKind) override
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
int SetLibItemName(const UTF8 &aLibItemName)
Override the library item name portion of the LIB_ID to aLibItemName.
Definition lib_id.cpp:124
bool IsValid() const
Check if this LID_ID is valid.
Definition lib_id.h:168
int SetLibNickname(const UTF8 &aLibNickname)
Override the logical library name portion of the LIB_ID to aLibNickname.
Definition lib_id.cpp:113
const UTF8 & GetLibItemName() const
Definition lib_id.h:98
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:83
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:604
static const LSET & AllTechMask()
Return a mask holding all technical layers (no CU layer) on both side.
Definition lset.cpp:672
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:595
static const LSET & AllLayersMask()
Definition lset.cpp:637
static LSET UserDefinedLayersMask(int aUserDefinedLayerCount=MAX_USER_DEFINED_LAYERS)
Return a mask with the requested number of user defined layers.
Definition lset.cpp:700
static const LSET & InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
Definition lset.cpp:573
Definition pad.h:61
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
wxString GetName(bool aUseDefaultName=true) const
Return the field name (not translated).
The selection tool: currently supports:
void SetTextThickness(int aWidth) override
The TextThickness is that set by the user.
Definition pcb_text.cpp:512
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true) override
Definition pcb_text.cpp:484
int GetTextThickness() const override
Definition pcb_text.cpp:497
VECTOR2I GetTextSize() const override
Definition pcb_text.cpp:470
static FOOTPRINT_LIBRARY_ADAPTER * FootprintLibAdapter(PROJECT *aProject)
int AddItemToSel(const TOOL_EVENT &aEvent)
wxGridCellAttr * enhanceAttr(wxGridCellAttr *aInputAttr, int aRow, int aCol, wxGridCellAttr::wxAttrKind aKind)
Definition wx_grid.cpp:43
void OnDeleteRows(const std::function< void(int row)> &aDeleter)
Handles a row deletion event.
Definition wx_grid.cpp:806
void OnAddRow(const std::function< std::pair< int, int >()> &aAdder)
Definition wx_grid.cpp:786
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:192
This file is part of the common library.
const int minSize
Push and Shove router track width and via size dialog.
#define _(s)
Declaration of the eda_3d_viewer class.
#define TEXT_MIN_SIZE_MM
Minimum text size (1 micron).
Definition eda_text.h:61
#define TEXT_MAX_SIZE_MM
Maximum text size in mm (~10 inches)
Definition eda_text.h:62
@ FP_SMD
Definition footprint.h:86
@ FP_DNP
Definition footprint.h:91
@ FP_EXCLUDE_FROM_POS_FILES
Definition footprint.h:87
@ FP_BOARD_ONLY
Definition footprint.h:89
@ FP_EXCLUDE_FROM_BOM
Definition footprint.h:88
@ FP_EXCLUDE_FROM_SIM
Definition footprint.h:92
@ FP_THROUGH_HOLE
Definition footprint.h:85
@ EXPAND_INNER_LAYERS
The 'normal' stackup handling, where there is a single inner layer (In1) and rule areas using it expa...
Definition footprint.h:160
@ CUSTOM_LAYERS
Stackup handling where the footprint can have any number of copper layers, and objects on those layer...
Definition footprint.h:165
int ClampTextPenSize(int aPenSize, int aSize, bool aStrict)
Pen width should not allow characters to become cluttered up in their own fatness.
Definition gr_text.cpp:69
static const std::string KiCadUriPrefix
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ User_45
Definition layer_ids.h:164
@ Edge_Cuts
Definition layer_ids.h:108
@ B_Cu
Definition layer_ids.h:61
@ Margin
Definition layer_ids.h:109
@ User_1
Definition layer_ids.h:120
@ F_Cu
Definition layer_ids.h:60
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition lset.cpp:750
KICOMMON_API wxFont GetInfoFont(wxWindow *aWindow)
wxString AccumulateNames(const LSEQ &aLayers, const BOARD *aBoard)
Accumulate layer names from a layer set into a comma separated string.
LSET GetOrphanedFootprintLayers(const FOOTPRINT &aFootprint, const LSET &aCustomUserLayers)
Compute the set of footprint-used layers that would be orphaned if the footprint's allowed layer set ...
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:96
@ PFC_THICKNESS
see class PGM_BASE
wxString UnescapeString(const wxString &aSource)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_LINE
wxString GetUserFieldName(int aFieldNdx, TRANSLATION aTranslation)
@ USER
The field ID hasn't been set yet; field is invalid.
@ TRANSLATED
Custom text control validator definitions.
@ THERMAL
Use thermal relief for pads.
Definition zones.h:46
@ NONE
Pads are not covered.
Definition zones.h:45
@ FULL
pads are covered by copper
Definition zones.h:47