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) 2011 Wayne Stambaugh <stambaughw@gmail.com>
6 * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26 #include <bitmaps.h>
27 #include <confirm.h>
28 #include <eda_dde.h>
29 #include <fp_lib_table.h>
30 #include <kiface_base.h>
31 #include <kiplatform/app.h>
32 #include <kiway_express.h>
33 #include <macros.h>
34 #include <netlist_reader/netlist_reader.h>
35 #include <numeric>
36 #include <tool/action_manager.h>
37 #include <tool/action_toolbar.h>
38 #include <tool/common_control.h>
39 #include <tool/editor_conditions.h>
40 #include <tool/tool_dispatcher.h>
41 #include <tool/tool_manager.h>
42 #include <widgets/wx_progress_reporters.h>
43 #include <wx/statline.h>
44 #include <wx/stattext.h>
45
46 #include <cvpcb_association.h>
47 #include <cvpcb_id.h>
48 #include <cvpcb_mainframe.h>
49 #include <cvpcb_settings.h>
50 #include <display_footprints_frame.h>
51 #include <kiplatform/ui.h>
52 #include <listboxes.h>
53 #include <tools/cvpcb_actions.h>
54 #include <tools/cvpcb_association_tool.h>
55 #include <tools/cvpcb_control.h>
56 #include <wx/button.h>
57 #include <wx/settings.h>
58
59
60 #define CVPCB_MAINFRAME_NAME wxT( "CvpcbFrame" )
61
CVPCB_MAINFRAME(KIWAY * aKiway,wxWindow * aParent)62 CVPCB_MAINFRAME::CVPCB_MAINFRAME( KIWAY* aKiway, wxWindow* aParent ) :
63 KIWAY_PLAYER( aKiway, aParent, FRAME_CVPCB, _( "Assign Footprints" ), wxDefaultPosition,
64 wxDefaultSize, KICAD_DEFAULT_DRAWFRAME_STYLE, CVPCB_MAINFRAME_NAME )
65 {
66 m_symbolsListBox = nullptr;
67 m_footprintListBox = nullptr;
68 m_librariesListBox = nullptr;
69 m_mainToolBar = nullptr;
70 m_modified = false;
71 m_cannotClose = false;
72 m_skipComponentSelect = false;
73 m_filteringOptions = FOOTPRINTS_LISTBOX::UNFILTERED_FP_LIST;
74 m_tcFilterString = nullptr;
75 m_FootprintsList = FOOTPRINT_LIST::GetInstance( Kiway() );
76 m_initialized = false;
77 m_aboutTitle = "CvPcb";
78
79 // Give an icon
80 wxIcon icon;
81 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_cvpcb ) );
82 SetIcon( icon );
83
84 SetAutoLayout( true );
85
86 LoadSettings( config() );
87
88 setupTools();
89 setupUIConditions();
90 ReCreateMenuBar();
91 ReCreateHToolbar();
92
93 // Create list of available footprints and symbols of the schematic
94 BuildSymbolsListBox();
95 BuildFootprintsListBox();
96 BuildLibrariesListBox();
97
98 m_auimgr.SetManagedWindow( this );
99
100 m_auimgr.AddPane( m_mainToolBar, EDA_PANE().HToolbar().Name( "MainToolbar" ).Top().Layer(6) );
101
102 m_auimgr.AddPane( m_librariesListBox, EDA_PANE().Palette().Name( "Libraries" ).Left().Layer(1)
103 .Caption( _( "Footprint Libraries" ) )
104 .BestSize((int) ( m_frameSize.x * 0.20 ), m_frameSize.y ) );
105
106 m_auimgr.AddPane( m_symbolsListBox, EDA_PANE().Palette().Name( "Symbols" ).Center().Layer(0)
107 .Caption( _( "Symbol : Footprint Assignments" ) ) );
108
109 m_auimgr.AddPane( m_footprintListBox, EDA_PANE().Palette().Name( "Footprints" ).Right().Layer(1)
110 .Caption( _( "Filtered Footprints" ) )
111 .BestSize((int) ( m_frameSize.x * 0.30 ), m_frameSize.y ) );
112
113 // Build the bottom panel, to display 2 status texts and the buttons:
114 auto bottomPanel = new wxPanel( this );
115 auto panelSizer = new wxBoxSizer( wxVERTICAL );
116
117 wxFlexGridSizer* fgSizerStatus = new wxFlexGridSizer( 3, 1, 0, 0 );
118 fgSizerStatus->SetFlexibleDirection( wxBOTH );
119 fgSizerStatus->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
120
121 m_statusLine1 = new wxStaticText( bottomPanel, wxID_ANY, wxEmptyString );
122 fgSizerStatus->Add( m_statusLine1, 0, 0, 5 );
123
124 m_statusLine2 = new wxStaticText( bottomPanel, wxID_ANY, wxEmptyString );
125 fgSizerStatus->Add( m_statusLine2, 0, 0, 5 );
126
127 m_statusLine3 = new wxStaticText( bottomPanel, wxID_ANY, wxEmptyString );
128 fgSizerStatus->Add( m_statusLine3, 0, wxBOTTOM, 3 );
129
130 panelSizer->Add( fgSizerStatus, 1, wxEXPAND|wxLEFT, 2 );
131
132 wxStaticLine* staticline1 = new wxStaticLine( bottomPanel );
133 panelSizer->Add( staticline1, 0, wxEXPAND, 5 );
134
135 m_statusLine1->SetFont( KIUI::GetStatusFont( this ) );
136 m_statusLine2->SetFont( KIUI::GetStatusFont( this ) );
137 m_statusLine3->SetFont( KIUI::GetStatusFont( this ) );
138
139 // Add buttons:
140 auto buttonsSizer = new wxBoxSizer( wxHORIZONTAL );
141 auto sdbSizer = new wxStdDialogButtonSizer();
142
143 m_saveAndContinue = new wxButton( bottomPanel, wxID_ANY,
144 _( "Apply, Save Schematic && Continue" ) );
145 buttonsSizer->Add( m_saveAndContinue, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 20 );
146
147 auto sdbSizerOK = new wxButton( bottomPanel, wxID_OK );
148 sdbSizer->AddButton( sdbSizerOK );
149 auto sdbSizerCancel = new wxButton( bottomPanel, wxID_CANCEL );
150 sdbSizer->AddButton( sdbSizerCancel );
151 sdbSizer->Realize();
152
153 buttonsSizer->Add( sdbSizer, 0, 0, 5 );
154 panelSizer->Add( buttonsSizer, 0, wxALIGN_RIGHT|wxALL, 5 );
155
156 bottomPanel->SetSizer( panelSizer );
157 bottomPanel->Fit();
158
159 sdbSizerOK->SetDefault();
160 KIPLATFORM::UI::FixupCancelButtonCmdKeyCollision( this );
161
162 m_auimgr.AddPane( bottomPanel, EDA_PANE().HToolbar().Name( "Buttons" ).Bottom().Layer(6) );
163
164 m_auimgr.Update();
165 m_initialized = true;
166
167 if( CVPCB_SETTINGS* cfg = dynamic_cast<CVPCB_SETTINGS*>( config() ) )
168 {
169 if( cfg->m_LibrariesWidth > 0 )
170 {
171 wxAuiPaneInfo& librariesPane = m_auimgr.GetPane( "Libraries" );
172
173 // wxAUI hack: force width by setting MinSize() and then Fixed()
174 // thanks to ZenJu http://trac.wxwidgets.org/ticket/13180
175 librariesPane.MinSize( cfg->m_LibrariesWidth, -1 );
176 librariesPane.BestSize( cfg->m_LibrariesWidth, -1 );
177 librariesPane.MaxSize( cfg->m_LibrariesWidth, -1 );
178 librariesPane.Fixed();
179 m_auimgr.Update();
180
181 // now make it resizable again
182 librariesPane.MinSize( 20, -1 );
183 librariesPane.Resizable();
184 m_auimgr.Update();
185 }
186
187 if( cfg->m_FootprintsWidth > 0 )
188 {
189 wxAuiPaneInfo& footprintsPane = m_auimgr.GetPane( "Footprints" );
190
191 // wxAUI hack: force width by setting MinSize() and then Fixed()
192 // thanks to ZenJu http://trac.wxwidgets.org/ticket/13180
193 footprintsPane.MinSize( cfg->m_FootprintsWidth, -1 );
194 footprintsPane.BestSize( cfg->m_FootprintsWidth, -1 );
195 footprintsPane.MaxSize( cfg->m_FootprintsWidth, -1 );
196 footprintsPane.Fixed();
197 m_auimgr.Update();
198
199 // now make it resizable again
200 footprintsPane.MinSize( 20, -1 );
201 footprintsPane.Resizable();
202 m_auimgr.Update();
203 }
204 }
205
206 // Connect Events
207 setupEventHandlers();
208
209 // Start the main processing loop
210 m_toolManager->InvokeTool( "cvpcb.Control" );
211
212 KIPLATFORM::APP::SetShutdownBlockReason( this, _( "Symbol to footprint changes are unsaved" ) );
213 }
214
215
~CVPCB_MAINFRAME()216 CVPCB_MAINFRAME::~CVPCB_MAINFRAME()
217 {
218 // Shutdown all running tools
219 if( m_toolManager )
220 m_toolManager->ShutdownAllTools();
221
222 // Clean up the tool infrastructure
223 delete m_actions;
224 delete m_toolManager;
225 delete m_toolDispatcher;
226
227 m_auimgr.UnInit();
228 }
229
230
setupTools()231 void CVPCB_MAINFRAME::setupTools()
232 {
233 // Create the manager
234 m_actions = new CVPCB_ACTIONS();
235 m_toolManager = new TOOL_MANAGER;
236 m_toolManager->SetEnvironment( nullptr, nullptr, nullptr, config(), this );
237 m_toolDispatcher = new TOOL_DISPATCHER( m_toolManager );
238
239 // Register tools
240 m_toolManager->RegisterTool( new COMMON_CONTROL );
241 m_toolManager->RegisterTool( new CVPCB_CONTROL );
242 m_toolManager->RegisterTool( new CVPCB_ASSOCIATION_TOOL );
243 m_toolManager->InitTools();
244
245 CVPCB_CONTROL* tool = m_toolManager->GetTool<CVPCB_CONTROL>();
246
247 // Even though these menus will open with the right-click, we treat them as a normal
248 // menu instead of a context menu because we don't care about their position and want
249 // to be able to tell the difference between a menu click and a hotkey activation.
250
251 // Create the context menu for the symbols list box
252 m_symbolsContextMenu = new ACTION_MENU( false, tool );
253 m_symbolsContextMenu->Add( CVPCB_ACTIONS::showFootprintViewer );
254 m_symbolsContextMenu->AppendSeparator();
255 m_symbolsContextMenu->Add( ACTIONS::cut );
256 m_symbolsContextMenu->Add( ACTIONS::copy );
257 m_symbolsContextMenu->Add( ACTIONS::paste );
258 m_symbolsContextMenu->AppendSeparator();
259 m_symbolsContextMenu->Add( CVPCB_ACTIONS::deleteAssoc );
260
261 // Create the context menu for the footprint list box
262 m_footprintContextMenu = new ACTION_MENU( false, tool );
263 m_footprintContextMenu->Add( CVPCB_ACTIONS::showFootprintViewer );
264 }
265
266
setupUIConditions()267 void CVPCB_MAINFRAME::setupUIConditions()
268 {
269 EDA_BASE_FRAME::setupUIConditions();
270
271 ACTION_MANAGER* mgr = m_toolManager->GetActionManager();
272 EDITOR_CONDITIONS cond( this );
273
274 wxASSERT( mgr );
275
276 #define ENABLE( x ) ACTION_CONDITIONS().Enable( x )
277 #define CHECK( x ) ACTION_CONDITIONS().Check( x )
278
279 mgr->SetConditions( CVPCB_ACTIONS::saveAssociations, ENABLE( cond.ContentModified() ) );
280 mgr->SetConditions( ACTIONS::undo, ENABLE( cond.UndoAvailable() ) );
281 mgr->SetConditions( ACTIONS::redo, ENABLE( cond.RedoAvailable() ) );
282
283 auto compFilter =
284 [this] ( const SELECTION& )
285 {
286 return m_filteringOptions & FOOTPRINTS_LISTBOX::FILTERING_BY_COMPONENT_FP_FILTERS;
287 };
288
289 auto libFilter =
290 [this] ( const SELECTION& )
291 {
292 return m_filteringOptions & FOOTPRINTS_LISTBOX::FILTERING_BY_LIBRARY;
293 };
294
295 auto pinFilter =
296 [this] ( const SELECTION& )
297 {
298 return m_filteringOptions & FOOTPRINTS_LISTBOX::FILTERING_BY_PIN_COUNT;
299 };
300
301 mgr->SetConditions( CVPCB_ACTIONS::FilterFPbyFPFilters, CHECK( compFilter ) );
302 mgr->SetConditions( CVPCB_ACTIONS::FilterFPbyLibrary, CHECK( libFilter ) );
303 mgr->SetConditions( CVPCB_ACTIONS::filterFPbyPin, CHECK( pinFilter ) );
304
305 #undef CHECK
306 #undef ENABLE
307 }
308
309
setupEventHandlers()310 void CVPCB_MAINFRAME::setupEventHandlers()
311 {
312 // Connect the handlers to launch the context menus in the listboxes
313 m_footprintListBox->Bind( wxEVT_RIGHT_DOWN,
314 [this]( wxMouseEvent& )
315 {
316 PopupMenu( m_footprintContextMenu );
317 } );
318
319 m_symbolsListBox->Bind( wxEVT_RIGHT_DOWN,
320 [this]( wxMouseEvent& )
321 {
322 PopupMenu( m_symbolsContextMenu );
323 } );
324
325 // Connect the handler for the save button
326 m_saveAndContinue->Bind( wxEVT_COMMAND_BUTTON_CLICKED,
327 [this]( wxCommandEvent& )
328 {
329 // saveAssociations must be run immediately
330 GetToolManager()->RunAction( CVPCB_ACTIONS::saveAssociations, true );
331 } );
332
333 // Connect the handlers for the ok/cancel buttons
334 Bind( wxEVT_BUTTON,
335 [this]( wxCommandEvent& )
336 {
337 // saveAssociations must be run immediately, before running Close( true )
338 GetToolManager()->RunAction( CVPCB_ACTIONS::saveAssociations, true );
339 Close( true );
340 }, wxID_OK );
341 Bind( wxEVT_BUTTON,
342 [this]( wxCommandEvent& )
343 {
344 Close( false );
345 }, wxID_CANCEL );
346
347 // Connect the handlers for the close events
348 Bind( wxEVT_MENU,
349 [this]( wxCommandEvent& )
350 {
351 Close( false );
352 }, wxID_CLOSE );
353 Bind( wxEVT_MENU,
354 [this]( wxCommandEvent& )
355 {
356 Close( false );
357 }, wxID_EXIT );
358
359 // Toolbar events
360 Bind( wxEVT_TEXT, &CVPCB_MAINFRAME::OnEnterFilteringText, this, ID_CVPCB_FILTER_TEXT_EDIT );
361
362 // Just skip the resize events
363 Bind( wxEVT_SIZE,
364 []( wxSizeEvent& aEvent )
365 {
366 aEvent.Skip();
367 } );
368
369 // Attach the events to the tool dispatcher
370 Bind( wxEVT_CHAR, &TOOL_DISPATCHER::DispatchWxEvent, m_toolDispatcher );
371 Bind( wxEVT_CHAR_HOOK, &TOOL_DISPATCHER::DispatchWxEvent, m_toolDispatcher );
372 }
373
374
canCloseWindow(wxCloseEvent & aEvent)375 bool CVPCB_MAINFRAME::canCloseWindow( wxCloseEvent& aEvent )
376 {
377 if( m_modified )
378 {
379 // Shutdown blocks must be determined and vetoed as early as possible
380 if( KIPLATFORM::APP::SupportsShutdownBlockReason()
381 && aEvent.GetId() == wxEVT_QUERY_END_SESSION )
382 {
383 return false;
384 }
385
386 if( !HandleUnsavedChanges( this, _( "Symbol to Footprint links have been modified. "
387 "Save changes?" ),
388 [&]() -> bool
389 {
390 return SaveFootprintAssociation( false );
391 } ) )
392 {
393 return false;
394 }
395 }
396
397 if( m_cannotClose )
398 return false;
399
400 return true;
401 }
402
403
doCloseWindow()404 void CVPCB_MAINFRAME::doCloseWindow()
405 {
406 if( GetFootprintViewerFrame() )
407 GetFootprintViewerFrame()->Close( true );
408
409 m_modified = false;
410
411 // clear highlight symbol in schematic:
412 SendMessageToEESCHEMA( true );
413 }
414
415
OnEnterFilteringText(wxCommandEvent & aEvent)416 void CVPCB_MAINFRAME::OnEnterFilteringText( wxCommandEvent& aEvent )
417 {
418 // Called when changing the filter string in main toolbar.
419 // If the option FOOTPRINTS_LISTBOX::FILTERING_BY_TEXT_PATTERN is set, update the list
420 // of available footprints which match the filter
421
422 wxListEvent l_event;
423 OnSelectComponent( l_event );
424 }
425
426
OnSelectComponent(wxListEvent & event)427 void CVPCB_MAINFRAME::OnSelectComponent( wxListEvent& event )
428 {
429 if( m_skipComponentSelect )
430 return;
431
432 wxString libraryName;
433 COMPONENT* symbol = GetSelectedComponent();
434 libraryName = m_librariesListBox->GetSelectedLibrary();
435
436 m_footprintListBox->SetFootprints( *m_FootprintsList, libraryName, symbol,
437 m_tcFilterString->GetValue(), m_filteringOptions );
438
439 if( symbol && symbol->GetFPID().IsValid() )
440 m_footprintListBox->SetSelectedFootprint( symbol->GetFPID() );
441 else
442 m_footprintListBox->SetSelection( m_footprintListBox->GetSelection(), false );
443
444 refreshAfterSymbolSearch( symbol );
445 }
446
447
LoadSettings(APP_SETTINGS_BASE * aCfg)448 void CVPCB_MAINFRAME::LoadSettings( APP_SETTINGS_BASE* aCfg )
449 {
450 EDA_BASE_FRAME::LoadSettings( aCfg );
451
452 CVPCB_SETTINGS* cfg = static_cast<CVPCB_SETTINGS*>( aCfg );
453
454 m_filteringOptions = cfg->m_FilterFootprint;
455 }
456
457
SaveSettings(APP_SETTINGS_BASE * aCfg)458 void CVPCB_MAINFRAME::SaveSettings( APP_SETTINGS_BASE* aCfg )
459 {
460 EDA_BASE_FRAME::SaveSettings( aCfg );
461
462 CVPCB_SETTINGS* cfg = static_cast<CVPCB_SETTINGS*>( aCfg );
463 cfg->m_FilterFootprint = m_filteringOptions;
464
465 cfg->m_LibrariesWidth = m_librariesListBox->GetSize().x;
466 cfg->m_FootprintsWidth = m_footprintListBox->GetSize().x;
467 }
468
469
UndoAssociation()470 void CVPCB_MAINFRAME::UndoAssociation()
471 {
472 if( m_undoList.size() == 0 )
473 return;
474
475 CVPCB_UNDO_REDO_ENTRIES redoEntries;
476 CVPCB_UNDO_REDO_ENTRIES curEntry = m_undoList.back();
477 m_undoList.pop_back();
478
479 // Iterate over the entries to undo
480 for( const auto& assoc : curEntry )
481 {
482 AssociateFootprint( assoc, true, false );
483 redoEntries.emplace_back( assoc.Reverse() );
484 }
485
486 // Add the redo entries to the redo stack
487 m_redoList.emplace_back( redoEntries );
488 }
489
490
RedoAssociation()491 void CVPCB_MAINFRAME::RedoAssociation()
492 {
493 if( m_redoList.size() == 0 )
494 return;
495
496 CVPCB_UNDO_REDO_ENTRIES curEntry = m_redoList.back();
497 m_redoList.pop_back();
498
499 // Iterate over the entries to undo
500 bool firstAssoc = true;
501
502 for( const auto& assoc : curEntry )
503 {
504 AssociateFootprint( assoc, firstAssoc );
505 firstAssoc = false;
506 }
507 }
508
509
formatSymbolDesc(int idx,const wxString & aReference,const wxString & aValue,const wxString & aFootprint)510 wxString CVPCB_MAINFRAME::formatSymbolDesc( int idx, const wxString& aReference,
511 const wxString& aValue, const wxString& aFootprint )
512 {
513 // Work around a bug in wxString::Format with double-byte chars (and double-quote, for some
514 // reason).
515 wxString desc = wxString::Format( wxT( "%3d " ), idx );
516
517 for( int ii = aReference.Length(); ii < 8; ++ii )
518 desc += wxS( " " );
519
520 desc += aReference + wxT( " - " );
521
522 for( int ii = aValue.Length(); ii < 16; ++ii )
523 desc += wxS( " " );
524
525 desc += aValue + wxT( " : " ) + aFootprint;
526
527 return desc;
528 }
529
530
AssociateFootprint(const CVPCB_ASSOCIATION & aAssociation,bool aNewEntry,bool aAddUndoItem)531 void CVPCB_MAINFRAME::AssociateFootprint( const CVPCB_ASSOCIATION& aAssociation,
532 bool aNewEntry, bool aAddUndoItem )
533 {
534 if( m_netlist.IsEmpty() )
535 return;
536
537 COMPONENT* symbol = m_netlist.GetComponent( aAssociation.GetComponentIndex() );
538
539 if( symbol == nullptr )
540 return;
541
542 LIB_ID fpid = aAssociation.GetNewFootprint();
543 LIB_ID oldFpid = symbol->GetFPID();
544
545 // Test for validity of the requested footprint
546 if( !fpid.empty() && !fpid.IsValid() )
547 {
548 wxString msg = wxString::Format( _( "'%s' is not a valid footprint." ),
549 fpid.Format().wx_str() );
550 DisplayErrorMessage( this, msg );
551 return;
552 }
553
554 const KIID& id = symbol->GetKIIDs().front();
555
556 // Set new footprint to all instances of the selected symbol
557 for( unsigned int idx : GetComponentIndices() )
558 {
559 COMPONENT* candidate = m_netlist.GetComponent( idx );
560 const std::vector<KIID>& kiids = candidate->GetKIIDs();
561
562 if( std::find( kiids.begin(), kiids.end(), id ) != kiids.end() )
563 {
564 // Set the new footprint
565 candidate->SetFPID( fpid );
566
567 // create the new symbol description and set it
568 wxString description = formatSymbolDesc( idx + 1,
569 candidate->GetReference(),
570 candidate->GetValue(),
571 candidate->GetFPID().Format().wx_str() );
572 m_symbolsListBox->SetString( idx, description );
573 }
574 }
575
576 // Mark the data as being modified
577 m_modified = true;
578
579 // Update the statusbar and refresh the list
580 DisplayStatus();
581 m_symbolsListBox->Refresh();
582
583 if( !aAddUndoItem )
584 return;
585
586 // Update the undo list
587 if ( aNewEntry )
588 {
589 // Create a new entry for this association
590 CVPCB_UNDO_REDO_ENTRIES newEntry;
591 newEntry.emplace_back( CVPCB_ASSOCIATION( aAssociation.GetComponentIndex(), oldFpid,
592 aAssociation.GetNewFootprint() ) );
593 m_undoList.emplace_back( newEntry );
594
595 // Clear the redo list
596 m_redoList.clear();
597 }
598 else
599 {
600 m_undoList.back().emplace_back( CVPCB_ASSOCIATION( aAssociation.GetComponentIndex(),
601 oldFpid,
602 aAssociation.GetNewFootprint() ) );
603 }
604
605 }
606
607
OpenProjectFiles(const std::vector<wxString> & aFileSet,int aCtl)608 bool CVPCB_MAINFRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
609 {
610 return true;
611 }
612
613
refreshAfterSymbolSearch(COMPONENT * aSymbol)614 void CVPCB_MAINFRAME::refreshAfterSymbolSearch( COMPONENT* aSymbol )
615 {
616 // Tell AuiMgr that objects are changed !
617 if( m_auimgr.GetManagedWindow() ) // Be sure Aui Manager is initialized
618 m_auimgr.Update(); // (could be not the case when starting CvPcb)
619
620 if( aSymbol == nullptr )
621 {
622 DisplayStatus();
623 return;
624 }
625
626 // Preview of the already assigned footprint.
627 // Find the footprint that was already chosen for this aSymbol and select it,
628 // but only if the selection is made from the aSymbol list or the library list.
629 // If the selection is made from the footprint list, do not change the current
630 // selected footprint.
631 if( FindFocus() == m_symbolsListBox || FindFocus() == m_librariesListBox )
632 {
633 wxString footprintName = FROM_UTF8( aSymbol->GetFPID().Format().c_str() );
634
635 m_footprintListBox->SetSelection( m_footprintListBox->GetSelection(), false );
636
637 for( int ii = 0; ii < m_footprintListBox->GetCount(); ii++ )
638 {
639 wxString candidateName;
640 wxString msg = m_footprintListBox->OnGetItemText( ii, 0 );
641 msg.Trim( true );
642 msg.Trim( false );
643 candidateName = msg.AfterFirst( wxChar( ' ' ) );
644
645 if( footprintName.Cmp( candidateName ) == 0 )
646 {
647 m_footprintListBox->SetSelection( ii, true );
648 break;
649 }
650 }
651
652 if( GetFootprintViewerFrame() )
653 m_toolManager->RunAction( CVPCB_ACTIONS::showFootprintViewer, true );
654 }
655
656 SendMessageToEESCHEMA();
657 DisplayStatus();
658 }
659
660
SetFootprintFilter(FOOTPRINTS_LISTBOX::FP_FILTER_T aFilter,CVPCB_MAINFRAME::CVPCB_FILTER_ACTION aAction)661 void CVPCB_MAINFRAME::SetFootprintFilter( FOOTPRINTS_LISTBOX::FP_FILTER_T aFilter,
662 CVPCB_MAINFRAME::CVPCB_FILTER_ACTION aAction )
663 {
664 int option = aFilter;
665
666 // Apply the filter accordingly
667 switch( aAction )
668 {
669 case CVPCB_MAINFRAME::FILTER_DISABLE: m_filteringOptions &= ~option; break;
670 case CVPCB_MAINFRAME::FILTER_ENABLE: m_filteringOptions |= option; break;
671 case CVPCB_MAINFRAME::FILTER_TOGGLE: m_filteringOptions ^= option; break;
672 }
673
674 wxListEvent l_event;
675 OnSelectComponent( l_event );
676 }
677
678
DisplayStatus()679 void CVPCB_MAINFRAME::DisplayStatus()
680 {
681 if( !m_initialized )
682 return;
683
684 wxString filters, msg;
685 COMPONENT* symbol = GetSelectedComponent();
686
687 if( ( m_filteringOptions & FOOTPRINTS_LISTBOX::FILTERING_BY_COMPONENT_FP_FILTERS ) )
688 {
689 msg.Empty();
690
691 if( symbol )
692 {
693 for( unsigned ii = 0; ii < symbol->GetFootprintFilters().GetCount(); ii++ )
694 {
695 if( msg.IsEmpty() )
696 msg += symbol->GetFootprintFilters()[ii];
697 else
698 msg += wxT( ", " ) + symbol->GetFootprintFilters()[ii];
699 }
700 }
701
702 filters += _( "Keywords" );
703
704 if( !msg.IsEmpty() )
705 filters += wxString::Format( wxT( " (%s)" ), msg );
706 }
707
708 if( ( m_filteringOptions & FOOTPRINTS_LISTBOX::FILTERING_BY_PIN_COUNT ) )
709 {
710 msg.Empty();
711
712 if( symbol )
713 msg = wxString::Format( wxT( "%i" ), symbol->GetPinCount() );
714
715 if( !filters.IsEmpty() )
716 filters += wxT( ", " );
717
718 filters += _( "Pin Count" );
719
720 if( !msg.IsEmpty() )
721 filters += wxString::Format( wxT( " (%s)" ), msg );
722 }
723
724 if( ( m_filteringOptions & FOOTPRINTS_LISTBOX::FILTERING_BY_LIBRARY ) )
725 {
726 msg = m_librariesListBox->GetSelectedLibrary();
727
728 if( !filters.IsEmpty() )
729 filters += wxT( ", " );
730
731 filters += _( "Library" );
732
733 if( !msg.IsEmpty() )
734 filters += wxString::Format( wxT( " (%s)" ), msg );
735 }
736
737 wxString textFilter = m_tcFilterString->GetValue();
738
739 if( !textFilter.IsEmpty() )
740 {
741 if( !filters.IsEmpty() )
742 filters += wxT( ", " );
743
744 filters += _( "Search Text" ) + wxString::Format( wxT( " (%s)" ), textFilter );
745 }
746
747 if( filters.IsEmpty() )
748 msg = _( "No Filtering" );
749 else
750 msg.Printf( _( "Filtered by %s" ), filters );
751
752 msg << wxT( ": " ) << m_footprintListBox->GetCount();
753
754 SetStatusText( msg );
755
756
757 msg.Empty();
758 wxString footprintName = GetSelectedFootprint();
759
760 FOOTPRINT_INFO* fp = m_FootprintsList->GetFootprintInfo( footprintName );
761
762 if( fp ) // can be NULL if no netlist loaded
763 {
764 msg = wxString::Format( _( "Description: %s; Keywords: %s" ),
765 fp->GetDescription(),
766 fp->GetKeywords() );
767 }
768
769 SetStatusText( msg, 1 );
770
771 msg.Empty();
772 wxString lib;
773
774 // Choose the footprint to get the information on
775 if( fp )
776 {
777 // Use the footprint in the footprint viewer
778 lib = fp->GetLibNickname();
779 }
780 else if( GetFocusedControl() == CVPCB_MAINFRAME::CONTROL_COMPONENT )
781 {
782 // Use the footprint of the selected symbol
783 if( symbol )
784 lib = symbol->GetFPID().GetLibNickname();
785 }
786 else if( GetFocusedControl() == CVPCB_MAINFRAME::CONTROL_LIBRARY )
787 {
788 // Use the library that is selected
789 lib = m_librariesListBox->GetSelectedLibrary();
790 }
791
792 // Extract the library information
793 FP_LIB_TABLE* fptbl = Prj().PcbFootprintLibs();
794
795 if( fptbl->HasLibrary( lib ) )
796 msg = wxString::Format( _( "Library location: %s" ), fptbl->GetFullURI( lib ) );
797 else
798 msg = wxString::Format( _( "Library location: unknown" ) );
799
800 SetStatusText( msg, 2 );
801 }
802
803
LoadFootprintFiles()804 bool CVPCB_MAINFRAME::LoadFootprintFiles()
805 {
806 FP_LIB_TABLE* fptbl = Prj().PcbFootprintLibs();
807
808 // Check if there are footprint libraries in the footprint library table.
809 if( !fptbl || !fptbl->GetLogicalLibs().size() )
810 {
811 wxMessageBox( _( "No PCB footprint libraries are listed in the current footprint "
812 "library table." ), _( "Configuration Error" ), wxOK | wxICON_ERROR );
813 return false;
814 }
815
816 WX_PROGRESS_REPORTER progressReporter( this, _( "Loading Footprint Libraries" ), 2 );
817
818 m_FootprintsList->ReadFootprintFiles( fptbl, nullptr, &progressReporter );
819
820 if( m_FootprintsList->GetErrorCount() )
821 {
822 m_FootprintsList->DisplayErrors( this );
823 }
824
825 return true;
826 }
827
828
SendMessageToEESCHEMA(bool aClearHighligntOnly)829 void CVPCB_MAINFRAME::SendMessageToEESCHEMA( bool aClearHighligntOnly )
830 {
831 if( m_netlist.IsEmpty() )
832 return;
833
834 // clear highlight of previously selected symbols (if any):
835 // Selecting a non existing symbol clears any previously highlighted symbols
836 std::string packet = "$CLEAR: \"HIGHLIGHTED\"";
837
838 if( Kiface().IsSingle() )
839 SendCommand( MSG_TO_SCH, packet );
840 else
841 Kiway().ExpressMail( FRAME_SCH, MAIL_CROSS_PROBE, packet, this );
842
843 if( aClearHighligntOnly )
844 return;
845
846 int selection = m_symbolsListBox->GetSelection();
847
848 if ( selection < 0 ) // Nothing selected
849 return;
850
851 if( m_netlist.GetComponent( selection ) == nullptr )
852 return;
853
854 // Now highlight the selected symbol:
855 COMPONENT* symbol = m_netlist.GetComponent( selection );
856
857 packet = std::string( "$PART: \"" ) + TO_UTF8( symbol->GetReference() ) + "\"";
858
859 if( Kiface().IsSingle() )
860 SendCommand( MSG_TO_SCH, packet );
861 else
862 Kiway().ExpressMail( FRAME_SCH, MAIL_CROSS_PROBE, packet, this );
863 }
864
865
ReadSchematicNetlist(const std::string & aNetlist)866 int CVPCB_MAINFRAME::ReadSchematicNetlist( const std::string& aNetlist )
867 {
868 STRING_LINE_READER* stringReader = new STRING_LINE_READER( aNetlist, "Eeschema via Kiway" );
869 KICAD_NETLIST_READER netlistReader( stringReader, &m_netlist );
870
871 m_netlist.Clear();
872
873 try
874 {
875 netlistReader.LoadNetlist();
876 }
877 catch( const IO_ERROR& ioe )
878 {
879 wxString msg = wxString::Format( _( "Error loading schematic.\n%s" ),
880 ioe.What().GetData() );
881 wxMessageBox( msg, _( "Load Error" ), wxOK | wxICON_ERROR );
882 return 1;
883 }
884
885 // We also remove footprint name if it is "$noname" because this is a dummy name,
886 // not the actual name of the footprint.
887 for( unsigned ii = 0; ii < m_netlist.GetCount(); ii++ )
888 {
889 if( m_netlist.GetComponent( ii )->GetFPID().GetLibItemName() == std::string( "$noname" ) )
890 m_netlist.GetComponent( ii )->SetFPID( LIB_ID() );
891 }
892
893 // Sort symbols by reference:
894 m_netlist.SortByReference();
895
896 return 0;
897 }
898
899
BuildFootprintsListBox()900 void CVPCB_MAINFRAME::BuildFootprintsListBox()
901 {
902 wxFont guiFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
903
904 if( m_footprintListBox == nullptr )
905 {
906 m_footprintListBox = new FOOTPRINTS_LISTBOX( this, ID_CVPCB_FOOTPRINT_LIST );
907 m_footprintListBox->SetFont( KIUI::GetMonospacedUIFont() );
908 }
909
910 m_footprintListBox->SetFootprints( *m_FootprintsList, wxEmptyString, nullptr, wxEmptyString,
911 FOOTPRINTS_LISTBOX::UNFILTERED_FP_LIST );
912 DisplayStatus();
913 }
914
915
BuildSymbolsListBox()916 void CVPCB_MAINFRAME::BuildSymbolsListBox()
917 {
918 wxString msg;
919 COMPONENT* symbol;
920 wxFont guiFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
921
922 if( m_symbolsListBox == nullptr )
923 {
924 m_symbolsListBox = new SYMBOLS_LISTBOX( this, ID_CVPCB_COMPONENT_LIST );
925 m_symbolsListBox->SetFont( KIUI::GetMonospacedUIFont() );
926 }
927
928 m_symbolsListBox->m_SymbolList.Clear();
929
930 for( unsigned i = 0; i < m_netlist.GetCount(); i++ )
931 {
932 symbol = m_netlist.GetComponent( i );
933
934 msg = formatSymbolDesc( m_symbolsListBox->GetCount() + 1,
935 symbol->GetReference(),
936 symbol->GetValue(),
937 symbol->GetFPID().Format().wx_str() );
938 m_symbolsListBox->m_SymbolList.Add( msg );
939 }
940
941 if( m_symbolsListBox->m_SymbolList.Count() )
942 {
943 m_symbolsListBox->SetItemCount( m_symbolsListBox->m_SymbolList.Count() );
944 m_symbolsListBox->SetSelection( 0, true );
945 m_symbolsListBox->RefreshItems( 0L, m_symbolsListBox->m_SymbolList.Count() - 1 );
946 m_symbolsListBox->UpdateWidth();
947 }
948 }
949
950
BuildLibrariesListBox()951 void CVPCB_MAINFRAME::BuildLibrariesListBox()
952 {
953 wxFont guiFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
954
955 if( m_librariesListBox == nullptr )
956 {
957 m_librariesListBox = new LIBRARY_LISTBOX( this, ID_CVPCB_LIBRARY_LIST );
958 m_librariesListBox->SetFont( KIUI::GetMonospacedUIFont() );
959 }
960
961 FP_LIB_TABLE* tbl = Prj().PcbFootprintLibs();
962
963 if( tbl )
964 {
965 wxArrayString libNames;
966
967 std::vector< wxString > libNickNames = tbl->GetLogicalLibs();
968
969 for( const wxString& libNickName : libNickNames )
970 libNames.Add( libNickName );
971
972 m_librariesListBox->SetLibraryList( libNames );
973 }
974 }
975
976
GetSelectedComponent()977 COMPONENT* CVPCB_MAINFRAME::GetSelectedComponent()
978 {
979 int selection = m_symbolsListBox->GetSelection();
980
981 if( selection >= 0 && selection < (int) m_netlist.GetCount() )
982 return m_netlist.GetComponent( selection );
983
984 return nullptr;
985 }
986
987
SetSelectedComponent(int aIndex,bool aSkipUpdate)988 void CVPCB_MAINFRAME::SetSelectedComponent( int aIndex, bool aSkipUpdate )
989 {
990 m_skipComponentSelect = aSkipUpdate;
991
992 if( aIndex < 0 )
993 {
994 m_symbolsListBox->DeselectAll();
995 }
996 else if( aIndex < m_symbolsListBox->GetCount() )
997 {
998 m_symbolsListBox->DeselectAll();
999 m_symbolsListBox->SetSelection( aIndex );
1000 SendMessageToEESCHEMA();
1001 }
1002
1003 m_skipComponentSelect = false;
1004 }
1005
1006
GetComponentIndices(CVPCB_MAINFRAME::CRITERIA aCriteria)1007 std::vector<unsigned int> CVPCB_MAINFRAME::GetComponentIndices(
1008 CVPCB_MAINFRAME::CRITERIA aCriteria )
1009 {
1010 std::vector<unsigned int> idx;
1011 int lastIdx;
1012
1013 // Make sure a netlist has been loaded and the box has contents
1014 if( m_netlist.IsEmpty() || m_symbolsListBox->GetCount() == 0 )
1015 return idx;
1016
1017 switch( aCriteria )
1018 {
1019 case CVPCB_MAINFRAME::ALL_COMPONENTS:
1020 idx.resize( m_netlist.GetCount() );
1021 std::iota( idx.begin(), idx.end(), 0 );
1022 break;
1023
1024 case CVPCB_MAINFRAME::SEL_COMPONENTS:
1025 // Check to see if anything is selected
1026 if( m_symbolsListBox->GetSelectedItemCount() < 1 )
1027 break;
1028
1029 // Get the symbols
1030 lastIdx = m_symbolsListBox->GetFirstSelected();
1031 idx.emplace_back( lastIdx );
1032
1033 lastIdx = m_symbolsListBox->GetNextSelected( lastIdx );
1034 while( lastIdx > 0 )
1035 {
1036 idx.emplace_back( lastIdx );
1037 lastIdx = m_symbolsListBox->GetNextSelected( lastIdx );
1038 }
1039 break;
1040
1041 case CVPCB_MAINFRAME::NA_COMPONENTS:
1042 for( unsigned int i = 0; i < m_netlist.GetCount(); i++ )
1043 {
1044 if( m_netlist.GetComponent( i )->GetFPID().empty() )
1045 idx.emplace_back( i );
1046 }
1047 break;
1048
1049 case CVPCB_MAINFRAME::ASSOC_COMPONENTS:
1050 for( unsigned int i = 0; i < m_netlist.GetCount(); i++ )
1051 {
1052 if( !m_netlist.GetComponent( i )->GetFPID().empty() )
1053 idx.emplace_back( i );
1054 }
1055 break;
1056
1057 default:
1058 wxASSERT_MSG( false, "Invalid symbol selection criteria" );
1059 }
1060
1061 return idx;
1062 }
1063
1064
GetFootprintViewerFrame() const1065 DISPLAY_FOOTPRINTS_FRAME* CVPCB_MAINFRAME::GetFootprintViewerFrame() const
1066 {
1067 // returns the Footprint Viewer frame, if exists, or NULL
1068 wxWindow* window = wxWindow::FindWindowByName( FOOTPRINTVIEWER_FRAME_NAME );
1069 return dynamic_cast<DISPLAY_FOOTPRINTS_FRAME*>( window );
1070 }
1071
1072
GetToolCanvas() const1073 wxWindow* CVPCB_MAINFRAME::GetToolCanvas() const
1074 {
1075 return GetFootprintViewerFrame();
1076 }
1077
1078
GetFocusedControl() const1079 CVPCB_MAINFRAME::CONTROL_TYPE CVPCB_MAINFRAME::GetFocusedControl() const
1080 {
1081 if( m_librariesListBox->HasFocus() )
1082 return CVPCB_MAINFRAME::CONTROL_LIBRARY;
1083 else if( m_symbolsListBox->HasFocus() )
1084 return CVPCB_MAINFRAME::CONTROL_COMPONENT;
1085 else if( m_footprintListBox->HasFocus() )
1086 return CVPCB_MAINFRAME::CONTROL_FOOTPRINT;
1087
1088 return CVPCB_MAINFRAME::CONTROL_NONE;
1089 }
1090
1091
GetFocusedControlObject() const1092 wxControl* CVPCB_MAINFRAME::GetFocusedControlObject() const
1093 {
1094 if( m_librariesListBox->HasFocus() )
1095 return m_librariesListBox;
1096 else if( m_symbolsListBox->HasFocus() )
1097 return m_symbolsListBox;
1098 else if( m_footprintListBox->HasFocus() )
1099 return m_footprintListBox;
1100
1101 return nullptr;
1102 }
1103
1104
SetFocusedControl(CVPCB_MAINFRAME::CONTROL_TYPE aLB)1105 void CVPCB_MAINFRAME::SetFocusedControl( CVPCB_MAINFRAME::CONTROL_TYPE aLB )
1106 {
1107 switch( aLB )
1108 {
1109 case CVPCB_MAINFRAME::CONTROL_LIBRARY: m_librariesListBox->SetFocus(); break;
1110 case CVPCB_MAINFRAME::CONTROL_COMPONENT: m_symbolsListBox->SetFocus(); break;
1111 case CVPCB_MAINFRAME::CONTROL_FOOTPRINT: m_footprintListBox->SetFocus(); break;
1112 default: break;
1113 }
1114 }
1115
1116
GetSelectedFootprint()1117 wxString CVPCB_MAINFRAME::GetSelectedFootprint()
1118 {
1119 // returns the LIB_ID of the selected footprint in footprint listview
1120 // or a empty string
1121 return m_footprintListBox->GetSelectedFootprint();
1122 }
1123
1124
SetStatusText(const wxString & aText,int aNumber)1125 void CVPCB_MAINFRAME::SetStatusText( const wxString& aText, int aNumber )
1126 {
1127 switch( aNumber )
1128 {
1129 case 0: m_statusLine1->SetLabel( aText ); break;
1130 case 1: m_statusLine2->SetLabel( aText ); break;
1131 case 2: m_statusLine3->SetLabel( aText ); break;
1132 default: wxFAIL_MSG( "Invalid status row number" ); break;
1133 }
1134 }
1135
1136
ShowChangedLanguage()1137 void CVPCB_MAINFRAME::ShowChangedLanguage()
1138 {
1139 EDA_BASE_FRAME::ShowChangedLanguage();
1140 ReCreateHToolbar();
1141 DisplayStatus();
1142 }
1143
1144
KiwayMailIn(KIWAY_EXPRESS & mail)1145 void CVPCB_MAINFRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
1146 {
1147 const std::string& payload = mail.GetPayload();
1148
1149 switch( mail.Command() )
1150 {
1151 case MAIL_EESCHEMA_NETLIST:
1152 // Disable Close events during ReadNetListAndFpFiles() to avoid crash when updating
1153 // widgets:
1154 m_cannotClose = true;
1155 ReadNetListAndFpFiles( payload );
1156 m_cannotClose = false;
1157 /* @todo
1158 Go into SCH_EDIT_FRAME::OnOpenCvpcb( wxCommandEvent& event ) and trim GNL_ALL down.
1159 */
1160 break;
1161
1162 default:
1163 ; // ignore most
1164 }
1165 }
1166