1 /*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2012-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2008-2016 Wayne Stambaugh <stambaughw@gmail.com>
6 * Copyright (C) 2004-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 <3d_viewer/eda_3d_viewer_frame.h>
27 #include <bitmaps.h>
28 #include <board_commit.h>
29 #include <board.h>
30 #include <footprint.h>
31 #include <confirm.h>
32 #include <eda_pattern_match.h>
33 #include <footprint_info.h>
34 #include <footprint_viewer_frame.h>
35 #include <fp_lib_table.h>
36 #include <kiway.h>
37 #include <widgets/msgpanel.h>
38 #include <pcb_draw_panel_gal.h>
39 #include <pcb_painter.h>
40 #include <pcbnew_id.h>
41 #include <footprint_editor_settings.h>
42 #include <pgm_base.h>
43 #include <settings/settings_manager.h>
44 #include <tool/action_toolbar.h>
45 #include <tool/common_control.h>
46 #include <tool/common_tools.h>
47 #include <tool/selection.h>
48 #include <tool/tool_dispatcher.h>
49 #include <tool/tool_manager.h>
50 #include <tool/zoom_tool.h>
51 #include <tools/pcb_viewer_tools.h>
52 #include <tools/pcb_actions.h>
53 #include <tools/pcb_editor_conditions.h>
54 #include <tools/pcb_control.h>
55 #include <tools/pcb_picker_tool.h>
56 #include <tools/pcb_selection_tool.h>
57 #include <tools/board_editor_control.h>
58 #include <wildcards_and_files_ext.h>
59 #include <wx/listbox.h>
60 #include <wx/srchctrl.h>
61 #include <wx/tokenzr.h>
62 #include <wx/choice.h>
63
64 using namespace std::placeholders;
65
66
67 #define NEXT_PART 1
68 #define NEW_PART 0
69 #define PREVIOUS_PART -1
70
71
BEGIN_EVENT_TABLE(FOOTPRINT_VIEWER_FRAME,EDA_DRAW_FRAME)72 BEGIN_EVENT_TABLE( FOOTPRINT_VIEWER_FRAME, EDA_DRAW_FRAME )
73 // Window events
74 EVT_SIZE( FOOTPRINT_VIEWER_FRAME::OnSize )
75 EVT_ACTIVATE( FOOTPRINT_VIEWER_FRAME::OnActivate )
76
77 EVT_MENU( wxID_EXIT, FOOTPRINT_VIEWER_FRAME::OnExitKiCad )
78 EVT_MENU( wxID_CLOSE, FOOTPRINT_VIEWER_FRAME::CloseFootprintViewer )
79
80 // Toolbar events
81 EVT_TOOL( ID_MODVIEW_NEXT, FOOTPRINT_VIEWER_FRAME::OnIterateFootprintList )
82 EVT_TOOL( ID_MODVIEW_PREVIOUS, FOOTPRINT_VIEWER_FRAME::OnIterateFootprintList )
83 EVT_TOOL( ID_ADD_FOOTPRINT_TO_BOARD, FOOTPRINT_VIEWER_FRAME::AddFootprintToPCB )
84 EVT_CHOICE( ID_ON_ZOOM_SELECT, FOOTPRINT_VIEWER_FRAME::OnSelectZoom )
85 EVT_CHOICE( ID_ON_GRID_SELECT, FOOTPRINT_VIEWER_FRAME::OnSelectGrid )
86
87 EVT_UPDATE_UI( ID_ADD_FOOTPRINT_TO_BOARD, FOOTPRINT_VIEWER_FRAME::OnUpdateFootprintButton )
88
89 EVT_TEXT( ID_MODVIEW_LIB_FILTER, FOOTPRINT_VIEWER_FRAME::OnLibFilter )
90 EVT_TEXT( ID_MODVIEW_FOOTPRINT_FILTER, FOOTPRINT_VIEWER_FRAME::OnFPFilter )
91
92 // listbox events
93 EVT_LISTBOX( ID_MODVIEW_LIB_LIST, FOOTPRINT_VIEWER_FRAME::ClickOnLibList )
94 EVT_LISTBOX( ID_MODVIEW_FOOTPRINT_LIST, FOOTPRINT_VIEWER_FRAME::ClickOnFootprintList )
95 EVT_LISTBOX_DCLICK( ID_MODVIEW_FOOTPRINT_LIST, FOOTPRINT_VIEWER_FRAME::DClickOnFootprintList )
96
97 END_EVENT_TABLE()
98
99
100 /*
101 * Note: FOOTPRINT_VIEWER_FRAME can be created in "modal mode", or as a usual frame.
102 */
103 #define PARENT_STYLE ( KICAD_DEFAULT_DRAWFRAME_STYLE | wxFRAME_FLOAT_ON_PARENT )
104 #define MODAL_STYLE ( KICAD_DEFAULT_DRAWFRAME_STYLE | wxSTAY_ON_TOP )
105 #define NONMODAL_STYLE ( KICAD_DEFAULT_DRAWFRAME_STYLE )
106
107
108 FOOTPRINT_VIEWER_FRAME::FOOTPRINT_VIEWER_FRAME( KIWAY* aKiway, wxWindow* aParent,
109 FRAME_T aFrameType ) :
110 PCB_BASE_FRAME( aKiway, aParent, aFrameType, _( "Footprint Library Browser" ),
111 wxDefaultPosition, wxDefaultSize,
112 aFrameType == FRAME_FOOTPRINT_VIEWER_MODAL ? ( aParent ? PARENT_STYLE : MODAL_STYLE )
113 : NONMODAL_STYLE,
114 aFrameType == FRAME_FOOTPRINT_VIEWER_MODAL ? FOOTPRINT_VIEWER_FRAME_NAME_MODAL
115 : FOOTPRINT_VIEWER_FRAME_NAME )
116 {
117 wxASSERT( aFrameType == FRAME_FOOTPRINT_VIEWER_MODAL || aFrameType == FRAME_FOOTPRINT_VIEWER );
118
119 if( aFrameType == FRAME_FOOTPRINT_VIEWER_MODAL )
120 SetModal( true );
121
122 m_aboutTitle = _( "KiCad Footprint Library Viewer" );
123
124 // Force the items to always snap
125 m_magneticItems.pads = MAGNETIC_OPTIONS::CAPTURE_ALWAYS;
126 m_magneticItems.tracks = MAGNETIC_OPTIONS::CAPTURE_ALWAYS;
127 m_magneticItems.graphics = true;
128
129 // Force the frame name used in config. the footprint viewer frame has a name
130 // depending on aFrameType (needed to identify the frame by wxWidgets),
131 // but only one configuration is preferable.
132 m_configName = FOOTPRINT_VIEWER_FRAME_NAME;
133
134 // Give an icon
135 wxIcon icon;
136 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_footprint_browser ) );
137 SetIcon( icon );
138
139 wxPanel* libPanel = new wxPanel( this );
140 wxSizer* libSizer = new wxBoxSizer( wxVERTICAL );
141
142 m_libFilter = new wxSearchCtrl( libPanel, ID_MODVIEW_LIB_FILTER, wxEmptyString,
143 wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER );
144 m_libFilter->SetDescriptiveText( _( "Filter" ) );
145 libSizer->Add( m_libFilter, 0, wxEXPAND, 5 );
146
147 m_libList = new wxListBox( libPanel, ID_MODVIEW_LIB_LIST, wxDefaultPosition, wxDefaultSize,
148 0, nullptr, wxLB_HSCROLL | wxNO_BORDER );
149 libSizer->Add( m_libList, 1, wxEXPAND, 5 );
150
151 libPanel->SetSizer( libSizer );
152 libPanel->Fit();
153
154 wxPanel* fpPanel = new wxPanel( this );
155 wxSizer* fpSizer = new wxBoxSizer( wxVERTICAL );
156
157 m_fpFilter = new wxSearchCtrl( fpPanel, ID_MODVIEW_FOOTPRINT_FILTER, wxEmptyString,
158 wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER );
159 m_fpFilter->SetDescriptiveText( _( "Filter" ) );
160 m_fpFilter->SetToolTip(
161 _( "Filter on footprint name, keywords, description and pad count.\n"
162 "Search terms are separated by spaces. All search terms must match.\n"
163 "A term which is a number will also match against the pad count." ) );
164 fpSizer->Add( m_fpFilter, 0, wxEXPAND, 5 );
165
166 #ifdef __WXGTK__
167 // wxSearchCtrl vertical height is not calculated correctly on some GTK setups
168 // See https://gitlab.com/kicad/code/kicad/-/issues/9019
169 m_libFilter->SetMinSize( wxSize( -1, GetTextExtent( wxT( "qb" ) ).y + 10 ) );
170 m_fpFilter->SetMinSize( wxSize( -1, GetTextExtent( wxT( "qb" ) ).y + 10 ) );
171 #endif
172
173 m_fpList = new wxListBox( fpPanel, ID_MODVIEW_FOOTPRINT_LIST, wxDefaultPosition, wxDefaultSize,
174 0, nullptr, wxLB_HSCROLL | wxNO_BORDER );
175 fpSizer->Add( m_fpList, 1, wxEXPAND, 5 );
176
177 fpPanel->SetSizer( fpSizer );
178 fpPanel->Fit();
179
180 // Create GAL canvas
181 m_canvasType = loadCanvasTypeSetting();
182
183 PCB_DRAW_PANEL_GAL* drawPanel = new PCB_DRAW_PANEL_GAL( this, -1, wxPoint( 0, 0 ), m_frameSize,
184 GetGalDisplayOptions(), m_canvasType );
185 SetCanvas( drawPanel );
186
187 SetBoard( new BOARD() );
188
189 // This board will only be used to hold a footprint for viewing
190 GetBoard()->SetBoardUse( BOARD_USE::FPHOLDER );
191
192 // In viewer, the default net clearance is not known (it depends on the actual board).
193 // So we do not show the default clearance, by setting it to 0
194 // The footprint or pad specific clearance will be shown
195 GetBoard()->GetDesignSettings().GetDefault()->SetClearance( 0 );
196
197 // Don't show the default board solder mask clearance in the footprint viewer. Only the
198 // footprint or pad clearance setting should be shown if it is not 0.
199 GetBoard()->GetDesignSettings().m_SolderMaskMargin = 0;
200
201 // Ensure all layers and items are visible:
202 GetBoard()->SetVisibleAlls();
203 SetScreen( new PCB_SCREEN( GetPageSizeIU() ) );
204
205 GetScreen()->m_Center = true; // Center coordinate origins on screen.
206 LoadSettings( config() );
207 GetGalDisplayOptions().m_axesEnabled = true;
208
209 // Call resolveCanvasType after loading settings:
210 resolveCanvasType();
211
212 // Create the manager and dispatcher & route draw panel events to the dispatcher
213 m_toolManager = new TOOL_MANAGER;
214 m_toolManager->SetEnvironment( GetBoard(), drawPanel->GetView(),
215 drawPanel->GetViewControls(), config(), this );
216 m_actions = new PCB_ACTIONS();
217 m_toolDispatcher = new TOOL_DISPATCHER( m_toolManager );
218 drawPanel->SetEventDispatcher( m_toolDispatcher );
219
220 m_toolManager->RegisterTool( new PCB_CONTROL );
221 m_toolManager->RegisterTool( new PCB_SELECTION_TOOL );
222 m_toolManager->RegisterTool( new COMMON_TOOLS ); // for std context menus (zoom & grid)
223 m_toolManager->RegisterTool( new COMMON_CONTROL );
224 m_toolManager->RegisterTool( new PCB_PICKER_TOOL ); // for setting grid origin
225 m_toolManager->RegisterTool( new ZOOM_TOOL );
226 m_toolManager->RegisterTool( new PCB_VIEWER_TOOLS );
227
228 m_toolManager->GetTool<PCB_VIEWER_TOOLS>()->SetFootprintFrame( true );
229
230 m_toolManager->InitTools();
231 m_toolManager->InvokeTool( "pcbnew.InteractiveSelection" );
232
233 setupUIConditions();
234 ReCreateMenuBar();
235 ReCreateHToolbar();
236 ReCreateVToolbar();
237 ReCreateOptToolbar();
238
239 ReCreateLibraryList();
240 UpdateTitle();
241
242 // If a footprint was previously loaded, reload it
243 if( getCurNickname().size() && getCurFootprintName().size() )
244 {
245 LIB_ID id;
246
247 id.SetLibNickname( getCurNickname() );
248 id.SetLibItemName( getCurFootprintName() );
249 GetBoard()->Add( loadFootprint( id ) );
250 }
251
252 drawPanel->DisplayBoard( m_pcb );
253
254 m_auimgr.SetManagedWindow( this );
255
256 // Horizontal items; layers 4 - 6
257 m_auimgr.AddPane( m_mainToolBar, EDA_PANE().VToolbar().Name( "MainToolbar" ).Top().Layer(6) );
258 m_auimgr.AddPane( m_optionsToolBar, EDA_PANE().VToolbar().Name( "OptToolbar" ).Left().Layer(3) );
259 m_auimgr.AddPane( m_messagePanel, EDA_PANE().Messages().Name( "MsgPanel" ).Bottom().Layer(6) );
260
261 // Vertical items; layers 1 - 3
262 m_auimgr.AddPane( libPanel, EDA_PANE().Palette().Name( "Libraries" ).Left().Layer(2)
263 .CaptionVisible( false ).MinSize( 100, -1 ).BestSize( 200, -1 ) );
264 m_auimgr.AddPane( fpPanel, EDA_PANE().Palette().Name( "Footprints" ).Left().Layer( 1)
265 .CaptionVisible( false ).MinSize( 100, -1 ).BestSize( 300, -1 ) );
266
267 m_auimgr.AddPane( GetCanvas(), EDA_PANE().Canvas().Name( "DrawFrame" ).Center() );
268
269 // after changing something to the aui manager call Update() to reflect the changes
270 m_auimgr.Update();
271
272 // The canvas should not steal the focus from the list boxes
273 GetCanvas()->SetCanFocus( false );
274 GetCanvas()->GetGAL()->SetAxesEnabled( true );
275 ActivateGalCanvas();
276
277 // Restore last zoom. (If auto-zooming we'll adjust when we load the footprint.)
278 PCBNEW_SETTINGS* cfg = GetPcbNewSettings();
279 wxASSERT( cfg );
280 GetCanvas()->GetView()->SetScale( cfg->m_FootprintViewerZoom );
281
282 updateView();
283 setupUnits( config() );
284
285 if( !IsModal() ) // For modal mode, calling ShowModal() will show this frame
286 {
287 ReCreateFootprintList();
288 Raise(); // On some window managers, this is needed
289 Show( true );
290 }
291 }
292
293
~FOOTPRINT_VIEWER_FRAME()294 FOOTPRINT_VIEWER_FRAME::~FOOTPRINT_VIEWER_FRAME()
295 {
296 // Shutdown all running tools
297 if( m_toolManager )
298 m_toolManager->ShutdownAllTools();
299
300 GetCanvas()->StopDrawing();
301 GetCanvas()->GetView()->Clear();
302 // Be sure any event cannot be fired after frame deletion:
303 GetCanvas()->SetEvtHandlerEnabled( false );
304 }
305
306
GetCurrentSelection()307 SELECTION& FOOTPRINT_VIEWER_FRAME::GetCurrentSelection()
308 {
309 return m_toolManager->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
310 }
311
312
setupUIConditions()313 void FOOTPRINT_VIEWER_FRAME::setupUIConditions()
314 {
315 PCB_BASE_FRAME::setupUIConditions();
316
317 ACTION_MANAGER* mgr = m_toolManager->GetActionManager();
318 PCB_EDITOR_CONDITIONS cond( this );
319
320 wxASSERT( mgr );
321
322 #define ENABLE( x ) ACTION_CONDITIONS().Enable( x )
323 #define CHECK( x ) ACTION_CONDITIONS().Check( x )
324
325 mgr->SetConditions( ACTIONS::toggleGrid, CHECK( cond.GridVisible() ) );
326 mgr->SetConditions( ACTIONS::toggleCursorStyle, CHECK( cond.FullscreenCursor() ) );
327 mgr->SetConditions( ACTIONS::millimetersUnits,
328 CHECK( cond.Units( EDA_UNITS::MILLIMETRES ) ) );
329 mgr->SetConditions( ACTIONS::inchesUnits, CHECK( cond.Units( EDA_UNITS::INCHES ) ) );
330 mgr->SetConditions( ACTIONS::milsUnits, CHECK( cond.Units( EDA_UNITS::MILS ) ) );
331
332
333 mgr->SetConditions( ACTIONS::zoomTool,
334 CHECK( cond.CurrentTool( ACTIONS::zoomTool ) ) );
335 mgr->SetConditions( ACTIONS::measureTool,
336 CHECK( cond.CurrentTool( ACTIONS::measureTool ) ) );
337 mgr->SetConditions( ACTIONS::selectionTool,
338 CHECK( cond.CurrentTool( ACTIONS::selectionTool ) ) );
339
340 mgr->SetConditions( PCB_ACTIONS::showPadNumbers, CHECK( cond.PadNumbersDisplay() ) );
341 mgr->SetConditions( PCB_ACTIONS::padDisplayMode, CHECK( !cond.PadFillDisplay() ) );
342 mgr->SetConditions( PCB_ACTIONS::textOutlines, CHECK( !cond.TextFillDisplay() ) );
343 mgr->SetConditions( PCB_ACTIONS::graphicsOutlines, CHECK( !cond.GraphicsFillDisplay() ) );
344
345 #undef ENABLE
346 #undef CHECK
347 }
348
349
doCloseWindow()350 void FOOTPRINT_VIEWER_FRAME::doCloseWindow()
351 {
352 // A workaround to avoid flicker, in modal mode when modview frame is destroyed,
353 // when the aui toolbar is not docked (i.e. shown in a miniframe)
354 // (useful on windows only)
355 m_mainToolBar->SetFocus();
356
357 GetCanvas()->StopDrawing();
358
359 if( IsModal() )
360 {
361 // Only dismiss a modal frame once, so that the return values set by
362 // the prior DismissModal() are not bashed for ShowModal().
363 if( !IsDismissed() )
364 DismissModal( false );
365
366 // window to be destroyed by the caller of KIWAY_PLAYER::ShowModal()
367 }
368 else
369 {
370 Destroy();
371 }
372 }
373
374
OnSize(wxSizeEvent & SizeEv)375 void FOOTPRINT_VIEWER_FRAME::OnSize( wxSizeEvent& SizeEv )
376 {
377 if( m_auimgr.GetManagedWindow() )
378 m_auimgr.Update();
379
380 SizeEv.Skip();
381 }
382
383
ReCreateLibraryList()384 void FOOTPRINT_VIEWER_FRAME::ReCreateLibraryList()
385 {
386 m_libList->Clear();
387
388 std::vector<wxString> nicknames = Prj().PcbFootprintLibs()->GetLogicalLibs();
389 std::set<wxString> excludes;
390
391 if( !m_libFilter->GetValue().IsEmpty() )
392 {
393 wxStringTokenizer tokenizer( m_libFilter->GetValue() );
394
395 while( tokenizer.HasMoreTokens() )
396 {
397 const wxString term = tokenizer.GetNextToken().Lower();
398 EDA_COMBINED_MATCHER matcher( term );
399 int matches, position;
400
401 for( const wxString& nickname : nicknames )
402 {
403 if( !matcher.Find( nickname.Lower(), matches, position ) )
404 excludes.insert( nickname );
405 }
406 }
407 }
408
409 for( const wxString& nickname : nicknames )
410 {
411 if( !excludes.count( nickname ) )
412 m_libList->Append( nickname );
413 }
414
415 // Search for a previous selection:
416 int index = m_libList->FindString( getCurNickname(), true );
417
418 if( index == wxNOT_FOUND )
419 {
420 if( m_libList->GetCount() > 0 )
421 {
422 m_libList->SetSelection( 0 );
423 wxCommandEvent dummy;
424 ClickOnLibList( dummy );
425 }
426 else
427 {
428 setCurNickname( wxEmptyString );
429 setCurFootprintName( wxEmptyString );
430 }
431 }
432 else
433 {
434 m_libList->SetSelection( index, true );
435 wxCommandEvent dummy;
436 ClickOnLibList( dummy );
437 }
438
439 GetCanvas()->Refresh();
440 }
441
442
ReCreateFootprintList()443 void FOOTPRINT_VIEWER_FRAME::ReCreateFootprintList()
444 {
445 m_fpList->Clear();
446
447 if( !getCurNickname() )
448 setCurFootprintName( wxEmptyString );
449
450 auto fp_info_list = FOOTPRINT_LIST::GetInstance( Kiway() );
451
452 wxString nickname = getCurNickname();
453
454 fp_info_list->ReadFootprintFiles( Prj().PcbFootprintLibs(), !nickname ? nullptr : &nickname );
455
456 if( fp_info_list->GetErrorCount() )
457 {
458 fp_info_list->DisplayErrors( this );
459
460 // For footprint libraries that support one footprint per file, there may have been
461 // valid footprints read so show the footprints that loaded properly.
462 if( fp_info_list->GetList().empty() )
463 return;
464 }
465
466 std::set<wxString> excludes;
467
468 if( !m_fpFilter->GetValue().IsEmpty() )
469 {
470 wxStringTokenizer tokenizer( m_fpFilter->GetValue() );
471
472 while( tokenizer.HasMoreTokens() )
473 {
474 const wxString term = tokenizer.GetNextToken().Lower();
475 EDA_COMBINED_MATCHER matcher( term );
476 int matches, position;
477
478 for( const std::unique_ptr<FOOTPRINT_INFO>& footprint : fp_info_list->GetList() )
479 {
480 wxString search = footprint->GetFootprintName() + " " + footprint->GetSearchText();
481 bool matched = matcher.Find( search.Lower(), matches, position );
482
483 if( !matched && term.IsNumber() )
484 matched = ( wxAtoi( term ) == (int)footprint->GetPadCount() );
485
486 if( !matched )
487 excludes.insert( footprint->GetFootprintName() );
488 }
489 }
490 }
491
492 for( const std::unique_ptr<FOOTPRINT_INFO>& footprint : fp_info_list->GetList() )
493 {
494 if( !excludes.count( footprint->GetFootprintName() ) )
495 m_fpList->Append( footprint->GetFootprintName() );
496 }
497
498 int index = m_fpList->FindString( getCurFootprintName(), true );
499
500 if( index == wxNOT_FOUND )
501 {
502 if( m_fpList->GetCount() > 0 )
503 {
504 m_fpList->SetSelection( 0 );
505 m_fpList->EnsureVisible( 0 );
506
507 wxCommandEvent dummy;
508 ClickOnFootprintList( dummy );
509 }
510 else
511 {
512 setCurFootprintName( wxEmptyString );
513 }
514 }
515 else
516 {
517 m_fpList->SetSelection( index, true );
518 m_fpList->EnsureVisible( index );
519 }
520 }
521
522
OnLibFilter(wxCommandEvent & aEvent)523 void FOOTPRINT_VIEWER_FRAME::OnLibFilter( wxCommandEvent& aEvent )
524 {
525 ReCreateLibraryList();
526
527 // Required to avoid interaction with SetHint()
528 // See documentation for wxTextEntry::SetHint
529 aEvent.Skip();
530 }
531
532
OnFPFilter(wxCommandEvent & aEvent)533 void FOOTPRINT_VIEWER_FRAME::OnFPFilter( wxCommandEvent& aEvent )
534 {
535 ReCreateFootprintList();
536
537 // Required to avoid interaction with SetHint()
538 // See documentation for wxTextEntry::SetHint
539 aEvent.Skip();
540 }
541
542
OnCharHook(wxKeyEvent & aEvent)543 void FOOTPRINT_VIEWER_FRAME::OnCharHook( wxKeyEvent& aEvent )
544 {
545 if( aEvent.GetKeyCode() == WXK_UP )
546 {
547 if( m_libFilter->HasFocus() || m_libList->HasFocus() )
548 selectPrev( m_libList );
549 else
550 selectPrev( m_fpList );
551 }
552 else if( aEvent.GetKeyCode() == WXK_DOWN )
553 {
554 if( m_libFilter->HasFocus() || m_libList->HasFocus() )
555 selectNext( m_libList );
556 else
557 selectNext( m_fpList );
558 }
559 else if( aEvent.GetKeyCode() == WXK_TAB && m_libFilter->HasFocus() )
560 {
561 if( !aEvent.ShiftDown() )
562 m_fpFilter->SetFocus();
563 else
564 aEvent.Skip();
565 }
566 else if( aEvent.GetKeyCode() == WXK_TAB && m_fpFilter->HasFocus() )
567 {
568 if( aEvent.ShiftDown() )
569 m_libFilter->SetFocus();
570 else
571 aEvent.Skip();
572 }
573 else if( aEvent.GetKeyCode() == WXK_RETURN && m_fpList->GetSelection() >= 0 )
574 {
575 wxCommandEvent dummy;
576 AddFootprintToPCB( dummy );
577 }
578 else
579 {
580 aEvent.Skip();
581 }
582 }
583
584
selectPrev(wxListBox * aListBox)585 void FOOTPRINT_VIEWER_FRAME::selectPrev( wxListBox* aListBox )
586 {
587 int prev = aListBox->GetSelection() - 1;
588
589 if( prev >= 0 )
590 {
591 aListBox->SetSelection( prev );
592 aListBox->EnsureVisible( prev );
593
594 wxCommandEvent dummy;
595
596 if( aListBox == m_libList )
597 ClickOnLibList( dummy );
598 else
599 ClickOnFootprintList( dummy );
600 }
601 }
602
603
selectNext(wxListBox * aListBox)604 void FOOTPRINT_VIEWER_FRAME::selectNext( wxListBox* aListBox )
605 {
606 int next = aListBox->GetSelection() + 1;
607
608 if( next < (int)aListBox->GetCount() )
609 {
610 aListBox->SetSelection( next );
611 aListBox->EnsureVisible( next );
612
613 wxCommandEvent dummy;
614
615 if( aListBox == m_libList )
616 ClickOnLibList( dummy );
617 else
618 ClickOnFootprintList( dummy );
619 }
620 }
621
622
ClickOnLibList(wxCommandEvent & aEvent)623 void FOOTPRINT_VIEWER_FRAME::ClickOnLibList( wxCommandEvent& aEvent )
624 {
625 int ii = m_libList->GetSelection();
626
627 if( ii < 0 )
628 return;
629
630 wxString name = m_libList->GetString( ii );
631
632 if( getCurNickname() == name )
633 return;
634
635 setCurNickname( name );
636
637 ReCreateFootprintList();
638 UpdateTitle();
639 }
640
641
ClickOnFootprintList(wxCommandEvent & aEvent)642 void FOOTPRINT_VIEWER_FRAME::ClickOnFootprintList( wxCommandEvent& aEvent )
643 {
644 if( m_fpList->GetCount() == 0 )
645 return;
646
647 int ii = m_fpList->GetSelection();
648
649 if( ii < 0 )
650 return;
651
652 wxString name = m_fpList->GetString( ii );
653
654 if( getCurFootprintName().CmpNoCase( name ) != 0 )
655 {
656 setCurFootprintName( name );
657
658 // Delete the current footprint (MUST reset tools first)
659 GetToolManager()->ResetTools( TOOL_BASE::MODEL_RELOAD );
660
661 GetBoard()->DeleteAllFootprints();
662
663 LIB_ID id;
664 id.SetLibNickname( getCurNickname() );
665 id.SetLibItemName( getCurFootprintName() );
666
667 try
668 {
669 GetBoard()->Add( loadFootprint( id ) );
670 }
671 catch( const IO_ERROR& ioe )
672 {
673 wxString msg =
674 wxString::Format( _( "Could not load footprint '%s' from library '%s'."
675 "\n\n%s" ),
676 getCurFootprintName(), getCurNickname(), ioe.Problem() );
677 DisplayError( this, msg );
678 }
679
680 UpdateTitle();
681
682 updateView();
683
684 GetCanvas()->Refresh();
685 Update3DView( true, true );
686 }
687 }
688
689
DClickOnFootprintList(wxCommandEvent & aEvent)690 void FOOTPRINT_VIEWER_FRAME::DClickOnFootprintList( wxCommandEvent& aEvent )
691 {
692 AddFootprintToPCB( aEvent );
693 }
694
695
AddFootprintToPCB(wxCommandEvent & aEvent)696 void FOOTPRINT_VIEWER_FRAME::AddFootprintToPCB( wxCommandEvent& aEvent )
697 {
698 if( IsModal() )
699 {
700 if( m_fpList->GetSelection() >= 0 )
701 {
702 LIB_ID fpid( getCurNickname(), m_fpList->GetStringSelection() );
703 DismissModal( true, fpid.Format() );
704 }
705 else
706 {
707 DismissModal( false );
708 }
709 }
710 else if( GetBoard()->GetFirstFootprint() )
711 {
712 PCB_EDIT_FRAME* pcbframe = (PCB_EDIT_FRAME*) Kiway().Player( FRAME_PCB_EDITOR, false );
713
714 if( pcbframe == nullptr ) // happens when the board editor is not active (or closed)
715 {
716 DisplayErrorMessage( this, _( "No board currently open." ) );
717 return;
718 }
719
720 TOOL_MANAGER* toolMgr = pcbframe->GetToolManager();
721
722 if( toolMgr->GetTool<BOARD_EDITOR_CONTROL>()->PlacingFootprint() )
723 {
724 DisplayError( this, _( "Previous footprint placement still in progress." ) );
725 return;
726 }
727
728 toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
729 BOARD_COMMIT commit( pcbframe );
730
731 // Create the "new" footprint
732 FOOTPRINT* newFootprint = (FOOTPRINT*) GetBoard()->GetFirstFootprint()->Duplicate();
733 newFootprint->SetParent( pcbframe->GetBoard() );
734 newFootprint->SetLink( niluuid );
735 newFootprint->SetFlags(IS_NEW ); // whatever
736
737 for( PAD* pad : newFootprint->Pads() )
738 {
739 // Set the pads ratsnest settings to the global settings
740 pad->SetLocalRatsnestVisible( pcbframe->GetDisplayOptions().m_ShowGlobalRatsnest );
741
742 // Pads in the library all have orphaned nets. Replace with Default.
743 pad->SetNetCode( 0 );
744 }
745
746 // Put it on FRONT layer,
747 // (Can be stored flipped if the lib is an archive built from a board)
748 if( newFootprint->IsFlipped() )
749 newFootprint->Flip( newFootprint->GetPosition(),
750 pcbframe->Settings().m_FlipLeftRight );
751
752 KIGFX::VIEW_CONTROLS* viewControls = pcbframe->GetCanvas()->GetViewControls();
753 VECTOR2D cursorPos = viewControls->GetCursorPosition();
754
755 commit.Add( newFootprint );
756 viewControls->SetCrossHairCursorPosition( VECTOR2D( 0, 0 ), false );
757 pcbframe->PlaceFootprint( newFootprint );
758
759 newFootprint->SetPosition( wxPoint( 0, 0 ) );
760 viewControls->SetCrossHairCursorPosition( cursorPos, false );
761 commit.Push( wxT( "Insert footprint" ) );
762
763 pcbframe->Raise();
764 toolMgr->RunAction( PCB_ACTIONS::placeFootprint, true, newFootprint );
765
766 newFootprint->ClearFlags();
767 }
768 }
769
770
LoadSettings(APP_SETTINGS_BASE * aCfg)771 void FOOTPRINT_VIEWER_FRAME::LoadSettings( APP_SETTINGS_BASE* aCfg )
772 {
773 PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( aCfg );
774 wxCHECK( cfg, /*void*/ );
775
776 // We don't allow people to change this right now, so make sure it's on
777 GetWindowSettings( cfg )->cursor.always_show_cursor = true;
778
779 PCB_BASE_FRAME::LoadSettings( aCfg );
780
781 // Fetch display and grid settings from Footprint Editor
782 auto fpedit = Pgm().GetSettingsManager().GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>();
783 m_displayOptions = fpedit->m_Display;
784 GetGalDisplayOptions().ReadWindowSettings( fpedit->m_Window );
785 }
786
787
SaveSettings(APP_SETTINGS_BASE * aCfg)788 void FOOTPRINT_VIEWER_FRAME::SaveSettings( APP_SETTINGS_BASE* aCfg )
789 {
790 PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( aCfg );
791 wxCHECK( cfg, /*void*/ );
792
793 GetGalDisplayOptions().m_axesEnabled = true;
794
795 // We don't want to store anything other than the window settings
796 PCB_BASE_FRAME::SaveSettings( cfg );
797
798 if( GetCanvas() && GetCanvas()->GetView() )
799 cfg->m_FootprintViewerZoom = GetCanvas()->GetView()->GetScale();
800 }
801
802
GetWindowSettings(APP_SETTINGS_BASE * aCfg)803 WINDOW_SETTINGS* FOOTPRINT_VIEWER_FRAME::GetWindowSettings( APP_SETTINGS_BASE* aCfg )
804 {
805 PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( aCfg );
806 wxCHECK_MSG( cfg, nullptr, "config not existing" );
807
808 return &cfg->m_FootprintViewer;
809 }
810
811
GetColorSettings() const812 COLOR_SETTINGS* FOOTPRINT_VIEWER_FRAME::GetColorSettings() const
813 {
814 auto* settings = Pgm().GetSettingsManager().GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>();
815
816 if( settings )
817 return Pgm().GetSettingsManager().GetColorSettings( settings->m_ColorTheme );
818 else
819 return Pgm().GetSettingsManager().GetColorSettings();
820 }
821
822
CommonSettingsChanged(bool aEnvVarsChanged,bool aTextVarsChanged)823 void FOOTPRINT_VIEWER_FRAME::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged )
824 {
825 PCB_BASE_FRAME::CommonSettingsChanged( aEnvVarsChanged, aTextVarsChanged );
826
827 if( aEnvVarsChanged )
828 ReCreateLibraryList();
829 }
830
831
getCurNickname()832 const wxString FOOTPRINT_VIEWER_FRAME::getCurNickname()
833 {
834 return Prj().GetRString( PROJECT::PCB_FOOTPRINT_VIEWER_LIB_NICKNAME );
835 }
836
837
setCurNickname(const wxString & aNickname)838 void FOOTPRINT_VIEWER_FRAME::setCurNickname( const wxString& aNickname )
839 {
840 Prj().SetRString( PROJECT::PCB_FOOTPRINT_VIEWER_LIB_NICKNAME, aNickname );
841 }
842
843
getCurFootprintName()844 const wxString FOOTPRINT_VIEWER_FRAME::getCurFootprintName()
845 {
846 return Prj().GetRString( PROJECT::PCB_FOOTPRINT_VIEWER_FP_NAME );
847 }
848
849
setCurFootprintName(const wxString & aName)850 void FOOTPRINT_VIEWER_FRAME::setCurFootprintName( const wxString& aName )
851 {
852 Prj().SetRString( PROJECT::PCB_FOOTPRINT_VIEWER_FP_NAME, aName );
853 }
854
855
OnActivate(wxActivateEvent & event)856 void FOOTPRINT_VIEWER_FRAME::OnActivate( wxActivateEvent& event )
857 {
858 if( event.GetActive() )
859 {
860 // Ensure we have the right library list:
861 std::vector< wxString > libNicknames = Prj().PcbFootprintLibs()->GetLogicalLibs();
862 bool stale = false;
863
864 if( libNicknames.size() != m_libList->GetCount() )
865 stale = true;
866 else
867 {
868 for( unsigned ii = 0; ii < libNicknames.size(); ii++ )
869 {
870 if( libNicknames[ii] != m_libList->GetString( ii ) )
871 {
872 stale = true;
873 break;
874 }
875 }
876 }
877
878 if( stale )
879 {
880 ReCreateLibraryList();
881 UpdateTitle();
882 }
883 }
884
885 event.Skip(); // required under wxMAC
886 }
887
888
OnUpdateFootprintButton(wxUpdateUIEvent & aEvent)889 void FOOTPRINT_VIEWER_FRAME::OnUpdateFootprintButton( wxUpdateUIEvent& aEvent )
890 {
891 aEvent.Enable( GetBoard()->GetFirstFootprint() != nullptr );
892 }
893
894
ShowModal(wxString * aFootprint,wxWindow * aParent)895 bool FOOTPRINT_VIEWER_FRAME::ShowModal( wxString* aFootprint, wxWindow* aParent )
896 {
897 if( aFootprint && !aFootprint->IsEmpty() )
898 {
899 wxString msg;
900 LIB_TABLE* fpTable = Prj().PcbFootprintLibs();
901 LIB_ID fpid;
902
903 fpid.Parse( *aFootprint, true );
904
905 if( fpid.IsValid() )
906 {
907 wxString libraryName = fpid.GetLibNickname();
908
909 if( !fpTable->HasLibrary( fpid.GetLibNickname(), false ) )
910 {
911 msg.sprintf( _( "The current configuration does not include library '%s'. Use "
912 "Manage Footprint Libraries to edit the configuration." ),
913 libraryName );
914 DisplayErrorMessage( aParent, _( "Footprint library not found." ), msg );
915 }
916 else if ( !fpTable->HasLibrary( fpid.GetLibNickname(), true ) )
917 {
918 msg.sprintf( _( "Library '%s' is not enabled in the current configuration. Use "
919 "Manage Footprint Libraries to edit the configuration." ),
920 libraryName );
921 DisplayErrorMessage( aParent, _( "Footprint library not enabled." ), msg );
922 }
923 else
924 {
925 // Update last selection:
926 setCurNickname( libraryName );
927 setCurFootprintName( fpid.GetLibItemName() );
928 m_libList->SetStringSelection( libraryName );
929 }
930 }
931 }
932
933 // Rebuild the fp list from the last selected library,
934 // and show the last selected footprint
935 ReCreateFootprintList();
936 SelectAndViewFootprint( NEW_PART );
937
938 bool retval = KIWAY_PLAYER::ShowModal( aFootprint, aParent );
939
940 m_libFilter->SetFocus();
941 return retval;
942 }
943
944
Update3DView(bool aMarkDirty,bool aRefresh,const wxString * aTitle)945 void FOOTPRINT_VIEWER_FRAME::Update3DView( bool aMarkDirty, bool aRefresh, const wxString* aTitle )
946 {
947 wxString title = _( "3D Viewer" ) + wxT( " \u2014 " ) + getCurFootprintName();
948 PCB_BASE_FRAME::Update3DView( aMarkDirty, aRefresh, &title );
949 }
950
951
GetGridColor()952 COLOR4D FOOTPRINT_VIEWER_FRAME::GetGridColor()
953 {
954 return GetColorSettings()->GetColor( LAYER_GRID );
955 }
956
957
OnIterateFootprintList(wxCommandEvent & event)958 void FOOTPRINT_VIEWER_FRAME::OnIterateFootprintList( wxCommandEvent& event )
959 {
960 switch( event.GetId() )
961 {
962 case ID_MODVIEW_NEXT:
963 SelectAndViewFootprint( NEXT_PART );
964 break;
965
966 case ID_MODVIEW_PREVIOUS:
967 SelectAndViewFootprint( PREVIOUS_PART );
968 break;
969
970 default:
971 wxString id = wxString::Format( "%i", event.GetId() );
972 wxFAIL_MSG( "FOOTPRINT_VIEWER_FRAME::OnIterateFootprintList error: id = " + id );
973 }
974 }
975
976
UpdateTitle()977 void FOOTPRINT_VIEWER_FRAME::UpdateTitle()
978 {
979 wxString title;
980 wxString path;
981
982 if( !getCurNickname().IsEmpty() )
983 {
984 title = getCurNickname();
985
986 FP_LIB_TABLE* libtable = Prj().PcbFootprintLibs();
987 const LIB_TABLE_ROW* row = libtable->FindRow( getCurNickname() );
988
989 if( row )
990 title += wxT( " \u2014 " ) + row->GetFullURI( true );
991 }
992 else
993 {
994 title = _( "[no library selected]" );
995 }
996
997 title += wxT( " \u2014 " ) + _( "Footprint Library Browser" );
998
999 SetTitle( title );
1000 }
1001
1002
SelectAndViewFootprint(int aMode)1003 void FOOTPRINT_VIEWER_FRAME::SelectAndViewFootprint( int aMode )
1004 {
1005 if( !getCurNickname() )
1006 return;
1007
1008 int selection = m_fpList->FindString( getCurFootprintName(), true );
1009
1010 if( aMode == NEXT_PART )
1011 {
1012 if( selection != wxNOT_FOUND && selection < (int)m_fpList->GetCount() - 1 )
1013 selection++;
1014 }
1015
1016 if( aMode == PREVIOUS_PART )
1017 {
1018 if( selection != wxNOT_FOUND && selection > 0 )
1019 selection--;
1020 }
1021
1022 if( selection != wxNOT_FOUND )
1023 {
1024 m_fpList->SetSelection( selection );
1025 m_fpList->EnsureVisible( selection );
1026
1027 setCurFootprintName( m_fpList->GetString((unsigned) selection ) );
1028
1029 // Delete the current footprint
1030 GetBoard()->DeleteAllFootprints();
1031
1032 FOOTPRINT* footprint = Prj().PcbFootprintLibs()->FootprintLoad( getCurNickname(),
1033 getCurFootprintName() );
1034
1035 if( footprint )
1036 GetBoard()->Add( footprint, ADD_MODE::APPEND );
1037
1038 Update3DView( true, true );
1039
1040 updateView();
1041 }
1042
1043 UpdateTitle();
1044
1045 GetCanvas()->Refresh();
1046 }
1047
1048
updateView()1049 void FOOTPRINT_VIEWER_FRAME::updateView()
1050 {
1051 GetCanvas()->UpdateColors();
1052 GetCanvas()->DisplayBoard( GetBoard() );
1053
1054 m_toolManager->ResetTools( TOOL_BASE::MODEL_RELOAD );
1055
1056 if( m_zoomSelectBox->GetSelection() == 0 )
1057 m_toolManager->RunAction( ACTIONS::zoomFitScreen, true );
1058 else
1059 m_toolManager->RunAction( ACTIONS::centerContents, true );
1060
1061 UpdateMsgPanel();
1062 }
1063
1064
OnExitKiCad(wxCommandEvent & event)1065 void FOOTPRINT_VIEWER_FRAME::OnExitKiCad( wxCommandEvent& event )
1066 {
1067 Kiway().OnKiCadExit();
1068 }
1069
1070
CloseFootprintViewer(wxCommandEvent & event)1071 void FOOTPRINT_VIEWER_FRAME::CloseFootprintViewer( wxCommandEvent& event )
1072 {
1073 Close( false );
1074 }
1075
1076
GetModel() const1077 BOARD_ITEM_CONTAINER* FOOTPRINT_VIEWER_FRAME::GetModel() const
1078 {
1079 return GetBoard()->GetFirstFootprint();
1080 }
1081
1082