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