KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_exchange_footprints.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) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
6 * Copyright (C) 2013 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
23#include <bitmaps.h>
24#include <board.h>
25#include <footprint.h>
26#include <pad.h>
28#include <string_utils.h>
29#include <kiway.h>
30#include <kiway_mail.h>
31#include <macros.h>
32#include <pcb_edit_frame.h>
35#include <tool/tool_manager.h>
37
38#include <ranges>
39
40
41#define ID_MATCH_FP_ALL 4200
42#define ID_MATCH_FP_SELECTED 4201
43#define ID_MATCH_FP_REF 4202
44#define ID_MATCH_FP_VAL 4203
45#define ID_MATCH_FP_ID 4204
46
51
52
54 bool updateMode, bool selectedMode ) :
56 m_commit( aParent ),
57 m_parent( aParent ),
58 m_currentFootprint( aFootprint ),
59 m_updateMode( updateMode )
60{
61 if( !updateMode )
62 {
63 SetTitle( _( "Change Footprints" ) );
64 m_matchAll->SetLabel( _( "Change all footprints on board" ) );
65 m_matchSelected->SetLabel( _( "Change selected footprint(s)" ) );
66 m_matchSpecifiedRef->SetLabel( _( "Change footprints matching reference designator:" ) );
67 m_matchSpecifiedValue->SetLabel( _( "Change footprints matching value:" ) );
68 m_matchSpecifiedID->SetLabel( _( "Change footprints with library id:" ) );
69 m_resetTextItemLayers->SetLabel( _( "Update text layers and visibilities" ) );
70 m_resetTextItemEffects->SetLabel( _( "Update text sizes and styles" ) );
71 m_resetTextItemPositions->SetLabel( _( "Update text positions" ) );
72 m_resetTextItemContent->SetLabel( _( "Update text content" ) );
73 m_resetFabricationAttrs->SetLabel( _( "Update fabrication attributes" ) );
74 m_resetClearanceOverrides->SetLabel( _( "Update clearance overrides" ) );
75 m_reset3DModels->SetLabel( _( "Update 3D models" ) );
76 }
77
78#if 0 // translator hint
79 wxString x = _( "Update/reset strings: there are two cases these descriptions need to cover: "
80 "the user made overrides to a footprint on the PCB and wants to remove them, "
81 "or the user made changes to the library footprint and wants to propagate "
82 "them back to the PCB." );
83#endif
84
85 if( m_updateMode )
86 m_changeSizer->Show( false );
87 else
88 m_upperSizer->FindItem( m_matchAll )->Show( false );
89
91 m_upperSizer->FindItem( m_matchSelected )->Show( false );
92
95
96 m_upperSizer->SetEmptyCellSize( wxSize( 0, 0 ) );
97 // The upper sizer has its content modified: re-layout it:
98 m_upperSizer->Layout();
99
100 // initialize controls based on update mode in case there is no saved state yet
101 m_removeExtraBox->SetValue( false );
102 m_resetTextItemLayers->SetValue( m_updateMode ? false : true );
103 m_resetTextItemEffects->SetValue( m_updateMode ? false : true );
104 m_resetTextItemPositions->SetValue( m_updateMode ? false : true );
105 m_resetTextItemContent->SetValue( m_updateMode ? false : true );
106 m_resetFabricationAttrs->SetValue( m_updateMode ? false : true );
107 m_resetClearanceOverrides->SetValue( true );
108 m_reset3DModels->SetValue( true );
109 m_resetTransform->SetValue( m_updateMode ? false : true );
110 m_matchPadPositions->SetValue( true );
111
112 // initialize match-mode
113 if( m_updateMode )
115 else
117
118 m_MessageWindow->SetLazyUpdate( true );
119 m_MessageWindow->SetFileName( Prj().GetProjectPath() + wxT( "report.txt" ) );
120
121 // DIALOG_SHIM needs a unique hash_key because classname is not sufficient
122 // because the update and change versions of this dialog have different controls.
123 m_hash_key = TO_UTF8( GetTitle() );
124
125 wxString okLabel = m_updateMode ? _( "Update" ) : _( "Change" );
126
127 SetupStandardButtons( { { wxID_OK, okLabel },
128 { wxID_CANCEL, _( "Close" ) } } );
129
130 // Now all widgets have the size fixed, call FinishDialogSettings
132}
133
134
136{
138 {
139 if( m_updateMode )
140 m_newID->ChangeValue( From_UTF8( m_currentFootprint->GetFPID().Format().c_str() ) );
141
142 // Use ChangeValue() instead of SetValue() so we don't generate events.
143 m_specifiedRef->ChangeValue( m_currentFootprint->GetReference() );
144 m_specifiedValue->ChangeValue( m_currentFootprint->GetValue() );
145 m_specifiedID->ChangeValue( From_UTF8( m_currentFootprint->GetFPID().Format().c_str() ) );
146 }
147
148 wxCommandEvent event;
149 event.SetEventObject( this );
150
151 switch( *m_matchMode )
152 {
153 case ID_MATCH_FP_ALL: OnMatchAllClicked( event ); break;
154 case ID_MATCH_FP_SELECTED: OnMatchSelectedClicked( event ); break;
155 case ID_MATCH_FP_REF: OnMatchRefClicked( event ); break;
156 case ID_MATCH_FP_VAL: OnMatchValueClicked( event ); break;
157 case ID_MATCH_FP_ID: OnMatchIDClicked( event ); break;
158 default: break;
159 }
160
161 return true;
162}
163
164
166{
167 LIB_ID specifiedID;
168
169 switch( *m_matchMode )
170 {
171 case ID_MATCH_FP_ALL:
172 return true;
174 return aFootprint == m_currentFootprint || aFootprint->IsSelected();
175 case ID_MATCH_FP_REF:
176 return WildCompareString( m_specifiedRef->GetValue(), aFootprint->GetReference(), false );
177 case ID_MATCH_FP_VAL:
178 return WildCompareString( m_specifiedValue->GetValue(), aFootprint->GetValue(), false );
179 case ID_MATCH_FP_ID:
180 specifiedID.Parse( m_specifiedID->GetValue() );
181 return aFootprint->GetFPID() == specifiedID;
182 default:
183 return false; // just to quiet compiler warnings....
184 }
185}
186
187
189{
190 switch( *m_matchMode )
191 {
192 case ID_MATCH_FP_ALL: return m_matchAll;
197 default: return nullptr;
198 }
199}
200
201
203{
204 wxRadioButton* rb_button = getRadioButtonForMode();
205
206 wxRadioButton* rb_butt_list[] =
207 {
213 nullptr // end of list
214 };
215
216 // Ensure the button state is ok. Only one button can be checked
217 // Change button state only if its state is incorrect, otherwise
218 // we have issues depending on the platform.
219 for( int ii = 0; rb_butt_list[ii]; ++ii )
220 {
221 bool state = rb_butt_list[ii] == rb_button;
222
223 if( rb_butt_list[ii]->GetValue() != state )
224 rb_butt_list[ii]->SetValue( state );
225 }
226}
227
228
230{
232
233 if( aEvent.GetEventObject() == this )
235 else
236 m_matchAll->SetFocus();
237}
238
239
241{
243
244 if( aEvent.GetEventObject() == this )
246 else
247 m_matchSelected->SetFocus();
248}
249
250
252{
254
255 if( aEvent.GetEventObject() == this )
257 else if( aEvent.GetEventObject() != m_specifiedRef )
258 m_specifiedRef->SetFocus();
259}
260
261
263{
265
266 if( aEvent.GetEventObject() == this )
268 else if( aEvent.GetEventObject() != m_specifiedValue )
269 m_specifiedValue->SetFocus();
270}
271
272
274{
276
277 if( aEvent.GetEventObject() == this )
279 else if( aEvent.GetEventObject() != m_specifiedID )
280 m_specifiedID->SetFocus();
281}
282
283
285{
286 m_removeExtraBox->SetValue( aCheck );
287 m_resetTextItemLayers->SetValue( aCheck );
288 m_resetTextItemEffects->SetValue( aCheck );
289 m_resetTextItemPositions->SetValue( aCheck );
290 m_resetTextItemContent->SetValue( aCheck );
291 m_resetFabricationAttrs->SetValue( aCheck );
292 m_resetClearanceOverrides->SetValue( aCheck );
293 m_reset3DModels->SetValue( aCheck );
294 m_resetTransform->SetValue( aCheck );
295 m_matchPadPositions->SetValue( aCheck );
296}
297
298
299void DIALOG_EXCHANGE_FOOTPRINTS::OnOKClicked( wxCommandEvent& aEvent )
300{
301 PCB_SELECTION_TOOL* selTool = m_parent->GetToolManager()->GetTool<PCB_SELECTION_TOOL>();
302 wxBusyCursor dummy;
303
304 m_MessageWindow->Clear();
305 m_MessageWindow->Flush( false );
306
307 m_newFootprints.clear();
309 m_commit.Push( m_updateMode ? _( "Update Footprint" ) : _( "Change Footprint" ) );
310 selTool->AddItemsToSel( &m_newFootprints );
311
312 m_MessageWindow->Flush( false );
313
314 WINDOW_THAWER thawer( m_parent );
315 m_parent->GetCanvas()->Refresh();
316}
317
318
320{
321 LIB_ID newFPID;
322
323 if( m_parent->GetBoard()->Footprints().empty() )
324 return;
325
326 if( !m_updateMode )
327 {
328 newFPID.Parse( m_newID->GetValue() );
329
330 if( !newFPID.IsValid() )
331 return;
332 }
333
334 /*
335 * NB: the change is done from the last footprint because processFootprint() modifies the
336 * last item in the list.
337 */
338 for( FOOTPRINT* footprint : std::ranges::reverse_view( m_parent->GetBoard()->Footprints() ) )
339 {
340 if( !isMatch( footprint ) )
341 continue;
342
343 if( m_updateMode )
344 processFootprint( footprint, footprint->GetFPID() );
345 else
346 processFootprint( footprint, newFPID );
347 }
348}
349
350
352{
353 LIB_ID oldFPID = aFootprint->GetFPID();
354 wxString msg;
355
356 // Load new footprint.
357 if( m_updateMode )
358 {
359 msg.Printf( _( "Updated footprint %s (%s)" ) + wxS( ": " ),
360 aFootprint->GetReference(),
361 oldFPID.Format().c_str() );
362 }
363 else
364 {
365 msg.Printf( _( "Changed footprint %s from '%s' to '%s'" ) + wxS( ": " ),
366 aFootprint->GetReference(),
367 oldFPID.Format().c_str(),
368 aNewFPID.Format().c_str() );
369 }
370
371 FOOTPRINT* newFootprint = m_parent->LoadFootprint( aNewFPID );
372
373 if( !newFootprint )
374 {
375 msg += _( "*** library footprint not found ***" );
376 m_MessageWindow->Report( msg, RPT_SEVERITY_ERROR );
377 return;
378 }
379
380 bool updated = !m_updateMode || aFootprint->FootprintNeedsUpdate( newFootprint );
381 bool shifted = false;
382
383 m_parent->GetBoard()->ExchangeFootprint( aFootprint, newFootprint, m_commit,
384 m_matchPadPositions->GetValue(),
385 m_removeExtraBox->GetValue(),
386 m_resetTextItemLayers->GetValue(),
387 m_resetTextItemEffects->GetValue(),
388 m_resetTextItemPositions->GetValue(),
389 m_resetTextItemContent->GetValue(),
390 m_resetFabricationAttrs->GetValue(),
391 m_resetClearanceOverrides->GetValue(),
392 m_reset3DModels->GetValue(),
393 m_resetTransform->GetValue(),
394 &updated, &shifted );
395
396 if( aFootprint == m_currentFootprint )
397 m_currentFootprint = newFootprint;
398
399 if( newFootprint )
400 m_newFootprints.push_back( newFootprint );
401
402 if( m_updateMode && !updated )
403 {
404 if( shifted )
405 msg += _( ": (shifted/rotated to match pad positions)" );
406 else
407 msg += _( ": (no changes)" );
408
409 m_MessageWindow->Report( msg, RPT_SEVERITY_INFO );
410 }
411 else
412 {
413 msg += _( ": OK" );
414 m_MessageWindow->Report( msg, RPT_SEVERITY_ACTION );
415 }
416}
417
418
420{
421 wxString newname = m_newID->GetValue();
422
423 if( KIWAY_PLAYER* frame = Kiway().Player( FRAME_FOOTPRINT_CHOOSER, true, this ) )
424 {
426 {
427 /*
428 * Symbol netlist format:
429 * pinNumber pinName <tab> pinNumber pinName...
430 * fpFilter fpFilter...
431 */
432 wxString netlist;
433
434 wxArrayString pins;
435
436 for( const wxString& pad : m_currentFootprint->GetUniquePadNumbers() )
437 pins.push_back( pad + ' ' + wxEmptyString /* leave pinName empty */ );
438
439 if( !pins.IsEmpty() )
440 netlist << EscapeString( wxJoin( pins, '\t' ), CTX_LINE );
441
442 netlist << wxS( "\r" );
443
444 netlist << EscapeString( m_currentFootprint->GetFilters(), CTX_LINE ) << wxS( "\r" );
445
446 std::string payload( netlist.ToStdString() );
448 frame->KiwayMailIn( mail );
449 }
450
451 if( frame->ShowModal( &newname, this ) )
452 {
453 if( event.GetEventObject() == m_newIDBrowseButton )
454 m_newID->SetValue( newname );
455 else
456 m_specifiedID->SetValue( newname );
457 }
458
459 frame->Destroy();
460 }
461}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
DIALOG_EXCHANGE_FOOTPRINTS_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Update Footprints from Library"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
void OnMatchIDClicked(wxCommandEvent &aEvent) override
void updateMatchModeRadioButtons(wxUpdateUIEvent &aEvent) override
void processFootprint(FOOTPRINT *aFootprint, const LIB_ID &aNewFPID)
void OnMatchValueClicked(wxCommandEvent &aEvent) override
void OnOKClicked(wxCommandEvent &aEvent) override
void OnMatchSelectedClicked(wxCommandEvent &aEvent) override
void OnMatchRefClicked(wxCommandEvent &aEvent) override
void ViewAndSelectFootprint(wxCommandEvent &aEvent) override
DIALOG_EXCHANGE_FOOTPRINTS(PCB_EDIT_FRAME *aParent, FOOTPRINT *aFootprint, bool updateMode, bool selectedMode)
void OnMatchAllClicked(wxCommandEvent &aEvent) override
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={})
std::string m_hash_key
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
bool IsSelected() const
Definition eda_item.h:134
bool FootprintNeedsUpdate(const FOOTPRINT *aLibFP, int aCompareFlags=0, REPORTER *aReporter=nullptr)
Return true if a board footprint differs from the library version.
const LIB_ID & GetFPID() const
Definition footprint.h:474
const wxString & GetValue() const
Definition footprint.h:933
const wxString & GetReference() const
Definition footprint.h:909
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
Carry a payload from one KIWAY_PLAYER to another within a PROJECT.
Definition kiway_mail.h:34
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition lib_id.cpp:65
bool IsValid() const
Check if this LID_ID is valid.
Definition lib_id.h:168
UTF8 Format() const
Definition lib_id.cpp:132
The main frame for Pcbnew.
The selection tool: currently supports:
int AddItemsToSel(const TOOL_EVENT &aEvent)
const char * c_str() const
Definition utf8.h:104
#define ID_MATCH_FP_ALL
#define ID_MATCH_FP_VAL
#define ID_MATCH_FP_SELECTED
#define ID_MATCH_FP_REF
int g_matchModeForExchange
int g_matchModeForUpdateSelected
#define ID_MATCH_FP_ID
int g_matchModeForExchangeSelected
#define _(s)
@ FRAME_FOOTPRINT_CHOOSER
Definition frame_type.h:40
static wxString From_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition idf_helpers.h:44
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition idf_helpers.h:37
This file contains miscellaneous commonly used macros and functions.
@ MAIL_SYMBOL_NETLIST
Definition mail_type.h:41
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION
std::vector< FAB_LAYER_COLOR > dummy
bool WildCompareString(const wxString &pattern, const wxString &string_to_tst, bool case_sensitive)
Compare a string against wild card (* and ?) pattern using the usual rules.
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
std::string netlist