KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_footprint_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) 2016 Mario Luzeiro <[email protected]>
5 * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
6 * Copyright (C) 2015 Dick Hollenbeck, [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#include <bitmaps.h>
25#include <board_commit.h>
27#include <board.h>
28#include <footprint.h>
29#include <eda_group.h>
30#include <confirm.h>
31#include <core/kicad_algo.h>
33#include <filename_resolver.h>
34#include <pcb_edit_frame.h>
35#include <pcbnew_settings.h>
37#include <pgm_base.h>
38#include <project_pcb.h>
39#include <kiplatform/ui.h>
44#include <template_fieldnames.h>
49#include <tool/tool_manager.h>
51
52
53int DIALOG_FOOTPRINT_PROPERTIES::m_page = 0; // remember the last open page during session
54
55
58 m_frame( aParent ),
59 m_footprint( aFootprint ),
67 m_initialized( false )
68{
69 // Create the extra panels. Embedded files is referenced by the 3D model panel.
72
73 m_NoteBook->AddPage( m_3dPanel, _("3D Models"), false );
74 m_NoteBook->AddPage( m_embeddedFiles, _( "Embedded Files" ) );
75
76 // Configure display origin transforms
79
80 std::vector<EMBEDDED_FILES*> embeddedFilesStack;
81 embeddedFilesStack.push_back( m_embeddedFiles->GetLocalFiles() );
82 embeddedFilesStack.push_back( m_frame->GetBoard()->GetEmbeddedFiles() );
83
84 m_fields = new PCB_FIELDS_GRID_TABLE( m_frame, this, std::move( embeddedFilesStack ) );
85
86 m_delayedErrorMessage = wxEmptyString;
87 m_delayedFocusGrid = nullptr;
90 m_initialFocus = false;
91
92 // Give an icon
93 wxIcon icon;
94 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_modedit ) );
95 SetIcon( icon );
96
97 m_itemsGrid->SetTable( m_fields );
98 m_itemsGrid->OverrideMinSize( 1.0, 1.0 );
99 m_itemsGrid->PushEventHandler( new GRID_TRICKS( m_itemsGrid ) );
100 m_itemsGrid->Bind( wxEVT_GRID_CELL_CHANGING, &DIALOG_FOOTPRINT_PROPERTIES::OnGridCellChanging, this );
101 m_itemsGrid->SetupColumnAutosizer( PFC_VALUE );
102 m_itemsGrid->ShowHideColumns( "0 1 2 3 4 5 7" );
103
105 m_orientation.SetPrecision( 3 );
106
107 // Set predefined rotations in combo dropdown, according to the locale floating point
108 // separator notation
109 double rot_list[] = { 0.0, 90.0, -90.0, 180.0 };
110
111 for( size_t ii = 0; ii < m_orientationCtrl->GetCount() && ii < 4; ++ii )
112 m_orientationCtrl->SetString( ii, wxString::Format( "%.1f", rot_list[ii] ) );
113
114 // Set font size for items showing long strings:
115 wxFont infoFont = KIUI::GetSmallInfoFont( this );
116 m_libraryIDLabel->SetFont( infoFont );
117 m_tcLibraryID->SetFont( infoFont );
118
119 infoFont = KIUI::GetInfoFont( this ).Italic();
120 m_staticTextInfoCopper->SetFont( infoFont );
121 m_staticTextInfoPaste->SetFont( infoFont );
122
123 m_NoteBook->SetSelection( m_page );
124
125 if( m_page == 0 )
126 {
130 }
131 else if( m_page == 1 )
132 {
134 }
135
136 // Update label text and tooltip for combined offset + ratio field
137 m_SolderPasteMarginLabel->SetLabel( _( "Solder paste clearance:" ) );
138 m_SolderPasteMarginLabel->SetToolTip( _( "Local solder paste clearance for this footprint.\n"
139 "Enter an absolute value (e.g., -0.1mm), a percentage "
140 "(e.g., -5%), or both (e.g., -0.1mm - 5%).\n"
141 "If blank, the global value is used." ) );
142
143 // Configure button logos
146
147 m_tcLibraryID->SetBackgroundColour( KIPLATFORM::UI::GetDialogBGColour() );
148
149 // We can't set the tab order through wxWidgets due to shortcomings in their mnemonics
150 // implementation on MSW
151 m_tabOrder = {
161 m_cbDNP,
169 };
170
172
173 // The 3D model tab was added after the base dtor. The final dialog size needs to be set
174 // accordingly.
175 SetSizer( m_GeneralBoxSizer );
176 Layout();
177 m_GeneralBoxSizer->Fit( this );
178
180}
181
182
184{
185 m_itemsGrid->Unbind( wxEVT_GRID_CELL_CHANGING, &DIALOG_FOOTPRINT_PROPERTIES::OnGridCellChanging, this );
186
187 // Prevents crash bug in wxGrid's d'tor
188 m_itemsGrid->DestroyTable( m_fields );
189
190 // Delete the GRID_TRICKS.
191 m_itemsGrid->PopEventHandler( true );
192
193 // free the memory used by all models, otherwise models which were
194 // browsed but not used would consume memory
196
197 // the GL canvas has to be visible before it is destroyed
198 m_page = m_NoteBook->GetSelection();
199 m_NoteBook->SetSelection( 1 );
200}
201
202
204{
206 {
208 Close();
209 }
210}
211
212
214{
216 {
218 Close();
219 }
220}
221
222
224{
226 {
228 Close();
229 }
230}
231
232
234{
236 {
238 Close();
239 }
240}
241
242
244{
245 if( !wxDialog::TransferDataToWindow() )
246 return false;
247
248 if( !m_PanelGeneral->TransferDataToWindow() )
249 return false;
250
251 // Add the models to the panel
252 if( !m_3dPanel->TransferDataToWindow() )
253 return false;
254
255 if( !m_embeddedFiles->TransferDataToWindow() )
256 return false;
257
258 wxString variantName;
259
260 if( m_footprint->GetBoard() )
261 variantName = m_footprint->GetBoard()->GetCurrentVariant();
262
263 // Footprint Fields
264 for( PCB_FIELD* srcField : m_footprint->GetFields() )
265 {
266 wxCHECK2( srcField, continue );
267
268 PCB_FIELD field( *srcField );
269 wxString text = m_footprint->GetFieldValueForVariant( variantName, field.GetName() );
270 field.SetText( m_footprint->GetBoard()->ConvertKIIDsToCrossReferences( text ) );
271
272 m_fields->push_back( field );
273 }
274
275 // notify the grid
276 wxGridTableMessage tmsg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_fields->GetNumberRows() );
277 m_itemsGrid->ProcessTableMessage( tmsg );
278
279 // Footprint Properties
280
281 m_posX.SetValue( m_footprint->GetPosition().x );
282 m_posY.SetValue( m_footprint->GetPosition().y );
283
284 m_BoardSideCtrl->SetSelection( (m_footprint->GetLayer() == B_Cu) ? 1 : 0 );
285
286 EDA_ANGLE orientation = m_footprint->GetOrientation();
287 m_orientation.SetAngleValue( orientation.Normalize180() );
288
289 m_cbLocked->SetValue( m_footprint->IsLocked() );
290 m_cbLocked->SetToolTip( _( "Locked footprints cannot be freely moved or oriented on the canvas and can only be "
291 "selected when the 'Locked items' checkbox is checked in the selection filter." ) );
292
293 if( m_footprint->GetAttributes() & FP_THROUGH_HOLE )
294 m_componentType->SetSelection( 0 );
295 else if( m_footprint->GetAttributes() & FP_SMD )
296 m_componentType->SetSelection( 1 );
297 else
298 m_componentType->SetSelection( 2 );
299
300 m_boardOnly->SetValue( m_footprint->GetAttributes() & FP_BOARD_ONLY );
301
302 m_cbExcludeFromSim->SetValue( m_footprint->GetExcludedFromSimForVariant( variantName ) );
303 m_excludeFromPosFiles->SetValue( m_footprint->GetExcludedFromPosFilesForVariant( variantName ) );
304 m_excludeFromBOM->SetValue( m_footprint->GetExcludedFromBOMForVariant( variantName ) );
305 m_cbDNP->SetValue( m_footprint->GetDNPForVariant( variantName ) );
306
307 // Local Clearances
308
309 if( m_footprint->GetLocalClearance().has_value() )
310 m_netClearance.SetValue( m_footprint->GetLocalClearance().value() );
311 else
312 m_netClearance.SetValue( wxEmptyString );
313
314 if( m_footprint->GetLocalSolderMaskMargin().has_value() )
315 m_solderMask.SetValue( m_footprint->GetLocalSolderMaskMargin().value() );
316 else
317 m_solderMask.SetValue( wxEmptyString );
318
319 m_solderPaste.SetOffsetValue( m_footprint->GetLocalSolderPasteMargin() );
320 m_solderPaste.SetRatioValue( m_footprint->GetLocalSolderPasteMarginRatio() );
321
322 m_allowSolderMaskBridges->SetValue( m_footprint->AllowSolderMaskBridges() );
323
324 switch( m_footprint->GetLocalZoneConnection() )
325 {
326 default:
327 case ZONE_CONNECTION::INHERITED: m_ZoneConnectionChoice->SetSelection( 0 ); break;
328 case ZONE_CONNECTION::FULL: m_ZoneConnectionChoice->SetSelection( 1 ); break;
329 case ZONE_CONNECTION::THERMAL: m_ZoneConnectionChoice->SetSelection( 2 ); break;
330 case ZONE_CONNECTION::NONE: m_ZoneConnectionChoice->SetSelection( 3 ); break;
331 }
332
333 wxString jumperGroups;
334
335 if( m_footprint->GetDuplicatePadNumbersAreJumpers() )
336 jumperGroups = _( "all pads with duplicate numbers" );
337
338 for( const JUMPER_GROUP& group : m_footprint->JumperPadGroups().GetAll() )
339 {
340 wxString groupTxt;
341
342 for( const wxString& pinNumber : group.GetNames() )
343 {
344 if( !groupTxt.IsEmpty() )
345 groupTxt << ", ";
346
347 groupTxt << pinNumber;
348 }
349
350 if( !jumperGroups.IsEmpty() )
351 jumperGroups << "; ";
352
353 jumperGroups << "(" << groupTxt << ")";
354 }
355
356 if( jumperGroups.IsEmpty() )
357 {
358 m_jumperGroupsText->SetLabel( _( "none" ) );
360 }
361 else
362 {
363 m_jumperGroupsText->SetLabel( jumperGroups );
364 }
365
366 // Show the footprint's FPID.
367 m_tcLibraryID->SetValue( m_footprint->GetFPID().Format() );
368
369 for( int col = 0; col < m_itemsGrid->GetNumberCols(); col++ )
370 {
371 m_itemsGrid->SetColMinimalWidth( col, m_itemsGrid->GetVisibleWidth( col, true, false ) );
372
373 // Adjust the column size.
374 int col_size = m_itemsGrid->GetVisibleWidth( col );
375
376 if( col == PFC_LAYER ) // This one's a drop-down. Check all possible values.
377 {
378 BOARD* board = m_footprint->GetBoard();
379
380 for( PCB_LAYER_ID layer : board->GetEnabledLayers() )
381 col_size = std::max( col_size, GetTextExtent( board->GetLayerName( layer ) ).x );
382
383 // Swatch and gaps:
384 col_size += KiROUND( 14 * GetDPIScaleFactor() ) + 12;
385 }
386
387 if( m_itemsGrid->IsColShown( col ) )
388 m_itemsGrid->SetColSize( col, col_size );
389 }
390
391 m_itemsGrid->SetRowLabelSize( 0 );
392
393 Layout();
394 m_itemsGrid->SetMinVisibleRows( this, 4 );
395 m_initialized = true;
396
397 return true;
398}
399
400
402{
403 if( !m_itemsGrid->CommitPendingChanges() )
404 return false;
405
406 if( !DIALOG_SHIM::Validate() )
407 return false;
408
409 // Validate texts.
410 for( size_t i = 0; i < m_fields->size(); ++i )
411 {
412 PCB_FIELD& field = m_fields->at( i );
413
414 // Check for missing field names.
415 if( field.GetName( false ).IsEmpty() )
416 {
418 m_delayedErrorMessage = wxString::Format( _( "Fields must have a name." ) );
421
422 return false;
423 }
424
425 int minSize = pcbIUScale.mmToIU( TEXT_MIN_SIZE_MM );
426 int maxSize = pcbIUScale.mmToIU( TEXT_MAX_SIZE_MM );
427 int width = m_frame->ValueFromString( m_itemsGrid->GetCellValue( i, PFC_WIDTH ) );
428 int height = m_frame->ValueFromString( m_itemsGrid->GetCellValue( i, PFC_HEIGHT ) );
429
430 if( width < minSize )
431 {
432 wxString min = m_frame->StringFromValue( minSize, true );
433
434 m_itemsGrid->SetCellValue( i, PFC_WIDTH, min );
435
437 m_delayedErrorMessage = wxString::Format( _( "Text width must be at least %s." ), min );
440
441 return false;
442 }
443 else if( width > maxSize )
444 {
445 wxString max = m_frame->StringFromValue( maxSize, true );
446
447 m_itemsGrid->SetCellValue( i, PFC_WIDTH, max );
448
450 m_delayedErrorMessage = wxString::Format( _( "Text width must be at most %s." ), max );
453
454 return false;
455 }
456
457 if( height < minSize )
458 {
459 wxString min = m_frame->StringFromValue( minSize, true );
460
461 m_itemsGrid->SetCellValue( i, PFC_HEIGHT, min );
462
464 m_delayedErrorMessage = wxString::Format( _( "Text height must be at least %s." ), min );
467
468 return false;
469 }
470 else if( height > maxSize )
471 {
472 wxString max = m_frame->StringFromValue( maxSize, true );
473
474 m_itemsGrid->SetCellValue( i, PFC_HEIGHT, max );
475
477 m_delayedErrorMessage = wxString::Format( _( "Text height must be at most %s." ), max );
480
481 return false;
482 }
483
484 // Test for acceptable values for thickness and size and clamp if fails
485 int maxPenWidth = ClampTextPenSize( field.GetTextThickness(), field.GetTextSize() );
486
487 if( field.GetTextThickness() > maxPenWidth )
488 {
489 wxString clamped = m_frame->StringFromValue( maxPenWidth, true );
490
491 m_itemsGrid->SetCellValue( i, PFC_THICKNESS, clamped );
492
494 m_delayedErrorMessage = wxString::Format( _( "Text thickness is too large for the text size.\n"
495 "It will be clamped at %s." ),
496 clamped );
499
500 return false;
501 }
502 }
503
504 if( !m_netClearance.Validate( 0, INT_MAX ) )
505 return false;
506
507 return true;
508}
509
510
512{
513 if( !Validate() )
514 return false;
515
516 if( !m_itemsGrid->CommitPendingChanges()
517 || !m_embeddedFiles->CommitPendingChanges() )
518 {
519 return false;
520 }
521
522 if( !m_3dPanel->Validate() )
523 return false;
524
525 KIGFX::PCB_VIEW* view = m_frame->GetCanvas()->GetView();
526 PCB_SELECTION_TOOL* selectionTool = m_frame->GetToolManager()->GetTool<PCB_SELECTION_TOOL>();
527 BOARD_COMMIT commit( m_frame );
528 commit.Modify( m_footprint );
529
530 // Make sure this happens inside a commit to capture any changed files
531 (void) m_3dPanel->TransferDataFromWindow();
532
533 (void) m_embeddedFiles->TransferDataFromWindow();
534
535 // Clear out embedded files that are no longer in use
536 std::set<wxString> files;
537 std::set<wxString> files_to_delete;
538
539 // Get the new files from the footprint fields
540 for( PCB_FIELD& field : *m_fields )
541 {
542 if( field.GetText().StartsWith( FILEEXT::KiCadUriPrefix ) )
543 files.insert( field.GetText() );
544 }
545
546 // Find any files referenced in the old fields that are not in the new fields
547 for( PCB_FIELD* field : m_footprint->GetFields() )
548 {
549 wxCHECK2( field, continue );
550
551 if( field->GetText().StartsWith( FILEEXT::KiCadUriPrefix ) )
552 {
553 if( files.find( field->GetText() ) == files.end() )
554 files_to_delete.insert( field->GetText() );
555 }
556 }
557
558 for( const wxString& file : files_to_delete )
559 {
560 wxString name = file.Mid( FILEEXT::KiCadUriPrefix.size() + 3 ); // Skip "kicad-embed://"
561 m_footprint->RemoveFile( name );
562 }
563
564 // Update fields
565 BOARD* board = m_footprint->GetBoard();
566
567 wxString variantName;
568
569 if( board )
570 variantName = board->GetCurrentVariant();
571
572 // Save base field values before the update so we can detect variant changes
573 std::map<wxString, wxString> baseFieldValues;
574
575 for( PCB_FIELD* existing : m_footprint->GetFields() )
576 baseFieldValues[existing->GetName()] = existing->GetText();
577
578 std::vector<PCB_FIELD> newFields;
579 int ordinal = 42; // Arbitrarily larger than any mandatory FIELD_T ids.
580
581 // The vector holds the fields by value, so it must not reallocate while newField is bound
582 newFields.reserve( m_fields->size() );
583
584 for( PCB_FIELD& field : *m_fields )
585 {
586 PCB_FIELD& newField = newFields.emplace_back( field );
587 wxString newText = commit.GetBoard()->ConvertCrossReferencesToKIIDs( field.GetText() );
588
589 if( !variantName.IsEmpty() )
590 {
591 auto it = baseFieldValues.find( field.GetName() );
592 wxString baseText = ( it != baseFieldValues.end() ) ? it->second : wxString();
593
594 FOOTPRINT_VARIANT* variant = m_footprint->GetVariant( variantName );
595
596 if( !variant )
597 variant = m_footprint->AddVariant( variantName );
598
599 if( variant )
600 variant->SetFieldValue( field.GetName(), newText );
601
602 newField.SetText( baseText );
603 }
604 else
605 {
606 newField.SetText( newText );
607 }
608
609 if( !field.IsMandatory() )
610 newField.SetOrdinal( ordinal++, FIELD_T::USER );
611 }
612
613 std::vector<PCB_FIELD*> addedFields;
614 std::vector<PCB_FIELD*> detachedFields;
615
616 m_footprint->UpdateFields( newFields, addedFields, detachedFields );
617
618 for( PCB_FIELD* field : detachedFields )
619 {
620 if( EDA_GROUP* parentGroup = field->GetParentGroup() )
621 parentGroup->RemoveItem( field );
622
623 delete field;
624 }
625
626 for( PCB_FIELD* field : m_footprint->GetFields() )
627 {
628 // Reused fields are already known to the view and to their group
629 if( alg::contains( addedFields, field ) )
630 {
631 view->Add( field );
632
633 if( EDA_GROUP* parentGroup = field->GetParentGroup() )
634 parentGroup->AddItem( field );
635 }
636 else
637 {
638 view->Update( field );
639 }
640
641 if( field->IsSelected() )
642 {
643 // The old copy was in the selection list, but this one is not. Remove the
644 // out-of-sync selection flag so we can re-add the field to the selection.
645 field->ClearSelected();
646 selectionTool->AddItemToSel( field, true );
647 }
648 }
649
650 // Initialize masks clearances
651 if( m_netClearance.IsNull() )
652 m_footprint->SetLocalClearance( {} );
653 else
654 m_footprint->SetLocalClearance( m_netClearance.GetValue() );
655
656 if( m_solderMask.IsNull() )
657 m_footprint->SetLocalSolderMaskMargin( {} );
658 else
659 m_footprint->SetLocalSolderMaskMargin( m_solderMask.GetValue() );
660
661 m_footprint->SetLocalSolderPasteMargin( m_solderPaste.GetOffsetValue() );
662 m_footprint->SetLocalSolderPasteMarginRatio( m_solderPaste.GetRatioValue() );
663
664 switch( m_ZoneConnectionChoice->GetSelection() )
665 {
666 default:
667 case 0: m_footprint->SetLocalZoneConnection( ZONE_CONNECTION::INHERITED ); break;
668 case 1: m_footprint->SetLocalZoneConnection( ZONE_CONNECTION::FULL ); break;
669 case 2: m_footprint->SetLocalZoneConnection( ZONE_CONNECTION::THERMAL ); break;
670 case 3: m_footprint->SetLocalZoneConnection( ZONE_CONNECTION::NONE ); break;
671 }
672
673 // Set Footprint Position
674 VECTOR2I pos( m_posX.GetValue(), m_posY.GetValue() );
675 m_footprint->SetPosition( pos );
676 m_footprint->SetLocked( m_cbLocked->GetValue() );
677
678 int attributes = 0;
679
680 switch( m_componentType->GetSelection() )
681 {
682 case 0: attributes |= FP_THROUGH_HOLE; break;
683 case 1: attributes |= FP_SMD; break;
684 default: break;
685 }
686
687 if( m_boardOnly->GetValue() )
688 attributes |= FP_BOARD_ONLY;
689
690 if( !variantName.IsEmpty() )
691 {
692 FOOTPRINT_VARIANT* variant = m_footprint->GetVariant( variantName );
693
694 if( !variant )
695 variant = m_footprint->AddVariant( variantName );
696
697 if( variant )
698 {
699 variant->SetExcludedFromSim( m_cbExcludeFromSim->GetValue() );
700 variant->SetExcludedFromPosFiles( m_excludeFromPosFiles->GetValue() );
701 variant->SetExcludedFromBOM( m_excludeFromBOM->GetValue() );
702 variant->SetDNP( m_cbDNP->GetValue() );
703 }
704
705 // Preserve base attribute flags for these four properties
706 if( m_footprint->GetAttributes() & FP_EXCLUDE_FROM_SIM )
707 attributes |= FP_EXCLUDE_FROM_SIM;
708
709 if( m_footprint->GetAttributes() & FP_EXCLUDE_FROM_POS_FILES )
710 attributes |= FP_EXCLUDE_FROM_POS_FILES;
711
712 if( m_footprint->GetAttributes() & FP_EXCLUDE_FROM_BOM )
713 attributes |= FP_EXCLUDE_FROM_BOM;
714
715 if( m_footprint->GetAttributes() & FP_DNP )
716 attributes |= FP_DNP;
717 }
718 else
719 {
720 if( m_cbExcludeFromSim->GetValue() )
721 attributes |= FP_EXCLUDE_FROM_SIM;
722
723 if( m_excludeFromPosFiles->GetValue() )
724 attributes |= FP_EXCLUDE_FROM_POS_FILES;
725
726 if( m_excludeFromBOM->GetValue() )
727 attributes |= FP_EXCLUDE_FROM_BOM;
728
729 if( m_cbDNP->GetValue() )
730 attributes |= FP_DNP;
731 }
732
733 m_footprint->SetAttributes( attributes );
734
735 m_footprint->SetAllowSolderMaskBridges( m_allowSolderMaskBridges->GetValue() );
736
737 EDA_ANGLE orient = m_orientation.GetAngleValue().Normalize();
738
739 if( m_footprint->GetOrientation() != orient )
740 m_footprint->Rotate( m_footprint->GetPosition(), orient - m_footprint->GetOrientation() );
741
742 // Set component side, that also have effect on the fields positions on board
743 bool change_layer = false;
744
745 if( m_BoardSideCtrl->GetSelection() == 0 ) // layer req = COMPONENT
746 {
747 if( m_footprint->GetLayer() == B_Cu )
748 change_layer = true;
749 }
750 else if( m_footprint->GetLayer() == F_Cu )
751 {
752 change_layer = true;
753 }
754
755 if( change_layer )
756 m_footprint->Flip( m_footprint->GetPosition(), m_frame->GetPcbNewSettings()->m_FlipDirection );
757
758 // Copy the models from the panel to the footprint
759 std::vector<FP_3DMODEL>& panelList = m_3dPanel->GetModelList();
760 std::vector<FP_3DMODEL>* fpList = &m_footprint->Models();
761 fpList->clear();
762 fpList->insert( fpList->end(), panelList.begin(), panelList.end() );
763
764 // This is a simple edit, we must create an undo entry
765 if( m_footprint->GetEditFlags() == 0 ) // i.e. not edited, or moved
766 commit.Push( _( "Edit Footprint Properties" ) );
767
769 return true;
770}
771
772
774{
775 m_itemsGrid->OnAddRow(
776 [&]() -> std::pair<int, int>
777 {
779
780 newField.SetVisible( false );
781 newField.SetLayer( m_footprint->GetLayer() == F_Cu ? F_Fab : B_Fab );
782 newField.SetFPRelativePosition( { 0, 0 } );
783 newField.StyleFromSettings( m_frame->GetDesignSettings(), true );
784
785 m_fields->push_back( newField );
786
787 // notify the grid
788 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
789 m_itemsGrid->ProcessTableMessage( msg );
790 OnModify();
791
792 return { m_fields->size() - 1, PFC_NAME };
793 } );
794}
795
796
798{
799 m_itemsGrid->OnDeleteRows(
800 [&]( int row )
801 {
802 if( row < m_fields->GetMandatoryRowCount() )
803 {
804 DisplayError( this, wxString::Format( _( "The first %d fields are mandatory." ),
805 m_fields->GetMandatoryRowCount() ) );
806 return false;
807 }
808
809 return true;
810 },
811 [&]( int row )
812 {
813 m_fields->DeleteRows( row );
814 } );
815
816 OnModify();
817}
818
819
821{
822 wxGridCellEditor* editor = m_itemsGrid->GetCellEditor( aEvent.GetRow(), aEvent.GetCol() );
823 wxControl* control = editor->GetControl();
824
825 if( control && control->GetValidator() && !control->GetValidator()->Validate( control ) )
826 {
827 aEvent.Veto();
829 m_delayedFocusRow = aEvent.GetRow();
830 m_delayedFocusColumn = aEvent.GetCol();
831 }
832 else if( aEvent.GetCol() == PFC_NAME )
833 {
834 wxString newName = aEvent.GetString();
835
836 for( int row = 0; row < m_itemsGrid->GetNumberRows(); ++row )
837 {
838 if( row == aEvent.GetRow() )
839 continue;
840
841 if( FieldNamesAreDuplicates( newName, m_itemsGrid->GetCellValue( row, PFC_NAME ) ) )
842 {
843 aEvent.Veto();
845 m_delayedFocusRow = aEvent.GetRow();
846 m_delayedFocusColumn = aEvent.GetCol();
847 m_delayedErrorMessage = wxString::Format( _( "Field name '%s' already in use." ), newName );
848 break;
849 }
850 }
851 }
852
853 editor->DecRef();
854}
855
856
858{
859 if( !m_initialized )
860 return;
861
862 // Handle a grid error. This is delayed to OnUpdateUI so that we can change focus
863 // even when the original validation was triggered from a killFocus event, and so
864 // that the corresponding notebook page can be shown in the background when triggered
865 // from an OK.
866 if( m_delayedFocusRow >= 0 )
867 {
868 // We will re-enter this routine if an error dialog is displayed, so make sure we
869 // zero out our member variables first.
870 wxGrid* grid = m_delayedFocusGrid;
871 int row = m_delayedFocusRow;
872 int col = m_delayedFocusColumn;
873 wxString msg = m_delayedErrorMessage;
874
875 m_delayedFocusGrid = nullptr;
878 m_delayedErrorMessage = wxEmptyString;
879
880 if( !msg.IsEmpty() )
881 {
882 // Do not use DisplayErrorMessage(); it screws up window order on Mac
883 DisplayError( nullptr, msg );
884 }
885
886 grid->SetFocus();
887 grid->MakeCellVisible( row, col );
888
889 // Selecting the first grid item only makes sense for the
890 // items grid
891 if( !m_initialFocus || grid == m_itemsGrid )
892 {
893 grid->SetGridCursor( row, col );
894
895 if( !( col == 0 && row < m_fields->GetMandatoryRowCount() ) )
896 grid->EnableCellEditControl( true );
897
898 grid->ShowCellEditControl();
899
900 if( grid == m_itemsGrid && row == 0 && col == 0 )
901 {
902 wxGridCellEditor* referenceEditor = grid->GetCellEditor( 0, 0 );
903
904 if( wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( referenceEditor->GetControl() ) )
905 KIUI::SelectReferenceNumber( textEntry );
906
907 referenceEditor->DecRef();
908 }
909 }
910
911 m_initialFocus = false;
912 }
913}
914
915
916void DIALOG_FOOTPRINT_PROPERTIES::OnPageChanging( wxNotebookEvent& aEvent )
917{
918 if( !m_itemsGrid->CommitPendingChanges() )
919 aEvent.Veto();
920}
921
922
923void DIALOG_FOOTPRINT_PROPERTIES::OnCheckBox( wxCommandEvent& event )
924{
925 if( m_initialized )
926 OnModify();
927}
928
929
930void DIALOG_FOOTPRINT_PROPERTIES::OnCombobox( wxCommandEvent& event )
931{
932 if( m_initialized )
933 OnModify();
934}
935
936
937void DIALOG_FOOTPRINT_PROPERTIES::OnText( wxCommandEvent& event )
938{
939 if( m_initialized )
940 OnModify();
941}
942
943
944void DIALOG_FOOTPRINT_PROPERTIES::OnChoice( wxCommandEvent& event )
945{
946 if( m_initialized )
947 OnModify();
948}
const char * name
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
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:982
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:374
void SetFPRelativePosition(const VECTOR2I &aPos)
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition board.cpp:943
wxString GetCurrentVariant() const
Definition board.h:529
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:1185
DIALOG_FOOTPRINT_PROPERTIES_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)
void OnDeleteField(wxCommandEvent &) override
void OnUpdateUI(wxUpdateUIEvent &) override
void OnAddField(wxCommandEvent &) override
void OnText(wxCommandEvent &event) override
void OnCombobox(wxCommandEvent &event) override
PANEL_FP_PROPERTIES_3D_MODEL * m_3dPanel
void OnGridCellChanging(wxGridEvent &aEvent)
void OnPageChanging(wxNotebookEvent &event) override
void EditLibraryFootprint(wxCommandEvent &) override
DIALOG_FOOTPRINT_PROPERTIES(PCB_EDIT_FRAME *aParent, FOOTPRINT *aFootprint)
void OnChoice(wxCommandEvent &event) override
void EditFootprint(wxCommandEvent &) override
void OnCheckBox(wxCommandEvent &event) override
void UpdateFootprint(wxCommandEvent &) override
void ChangeFootprint(wxCommandEvent &) override
std::vector< wxWindow * > m_tabOrder
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...
EDA_ANGLE Normalize180()
Definition eda_angle.h:273
A set of EDA_ITEMs (i.e., without duplicates).
Definition eda_group.h:43
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:236
Variant information for a footprint.
Definition footprint.h:228
void SetExcludedFromPosFiles(bool aExclude)
Definition footprint.h:252
void SetExcludedFromSim(bool aExclude)
Definition footprint.h:249
void SetDNP(bool aDNP)
Definition footprint.h:243
void SetFieldValue(const wxString &aFieldName, const wxString &aValue)
Set a field value override for this variant.
Definition footprint.h:274
void SetExcludedFromBOM(bool aExclude)
Definition footprint.h:246
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition grid_tricks.h:57
Represents a group of jumper pins or pads, keyed by name.
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
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
The main frame for Pcbnew.
void SetOrdinal(int aOrdinal, FIELD_T aType)
Definition pcb_field.h:129
wxString GetName(bool aUseDefaultName=true) const
Return the field name (not translated).
The selection tool: currently supports:
void StyleFromSettings(const BOARD_DESIGN_SETTINGS &settings, bool aCheckSide) override
Definition pcb_text.cpp:371
int GetTextThickness() const override
Definition pcb_text.cpp:497
VECTOR2I GetTextSize() const override
Definition pcb_text.cpp:470
static S3D_CACHE * Get3DCacheManager(PROJECT *aProject, bool updateProjDir=false)
Return a pointer to an instance of the 3D cache manager.
void FlushCache(bool closePlugins=true)
Free all data in the cache and by default closes all plugins.
Definition 3d_cache.cpp:560
int AddItemToSel(const TOOL_EVENT &aEvent)
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:87
@ FP_DNP
Definition footprint.h:92
@ FP_EXCLUDE_FROM_POS_FILES
Definition footprint.h:88
@ FP_BOARD_ONLY
Definition footprint.h:90
@ FP_EXCLUDE_FROM_BOM
Definition footprint.h:89
@ FP_EXCLUDE_FROM_SIM
Definition footprint.h:93
@ FP_THROUGH_HOLE
Definition footprint.h:86
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
@ B_Cu
Definition layer_ids.h:61
@ F_Fab
Definition layer_ids.h:115
@ F_Cu
Definition layer_ids.h:60
@ B_Fab
Definition layer_ids.h:114
wxColour GetDialogBGColour()
Definition wxgtk/ui.cpp:64
KICOMMON_API wxFont GetInfoFont(wxWindow *aWindow)
KICOMMON_API wxFont GetSmallInfoFont(wxWindow *aWindow)
KICOMMON_API wxFont GetControlFont(wxWindow *aWindow)
KICOMMON_API void SelectReferenceNumber(wxTextEntry *aTextEntry)
Select the number (or "?") in a reference for ease of editing.
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
bool FieldNamesAreDuplicates(const wxString &aLhs, const wxString &aRhs, std::initializer_list< FIELD_T > aMandatoryFields)
Test whether two field names should be treated as duplicates for the purposes of field name uniquenes...
wxString GetUserFieldName(int aFieldNdx, TRANSLATION aTranslation)
@ USER
The field ID hasn't been set yet; field is invalid.
@ TRANSLATED
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
@ 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