KiCad PCB EDA Suite
dialog_choose_component.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) 2014 Henner Zeller <h.zeller@acm.org>
5  * Copyright (C) 2016-2018 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
26 
27 #include <algorithm>
28 #include <set>
29 #include <wx/utils.h>
30 
31 #include <wx/button.h>
32 #include <wx/dataview.h>
33 #include <wx/panel.h>
34 #include <wx/sizer.h>
35 #include <wx/splitter.h>
36 #include <wx/timer.h>
37 #include <wx/utils.h>
38 
39 #include <class_library.h>
40 #include <sch_base_frame.h>
41 #include <template_fieldnames.h>
42 #include <symbol_lib_table.h>
43 #include <widgets/lib_tree.h>
47 
48 
52 
54 
55 
58  int aDeMorganConvert, bool aAllowFieldEdits,
59  bool aShowFootprints, bool aAllowBrowser )
60  : DIALOG_SHIM( aParent, wxID_ANY, aTitle, wxDefaultPosition, wxDefaultSize,
61  wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
62  m_browser_button( nullptr ),
63  m_hsplitter( nullptr ),
64  m_vsplitter( nullptr ),
65  m_fp_sel_ctrl( nullptr ),
66  m_fp_preview( nullptr ),
67  m_parent( aParent ),
68  m_deMorganConvert( aDeMorganConvert >= 0 ? aDeMorganConvert : 0 ),
69  m_allow_field_edits( aAllowFieldEdits ),
70  m_show_footprints( aShowFootprints ),
71  m_external_browser_requested( false )
72 {
73  auto sizer = new wxBoxSizer( wxVERTICAL );
74  wxHtmlWindow* details = nullptr;
75 
76  // Use a slightly different layout, with a details pane spanning the entire window,
77  // if we're not showing footprints.
78  if( aShowFootprints )
79  {
80  m_hsplitter = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
81  wxSP_LIVE_UPDATE );
82 
83  //Avoid the splitter window being assigned as the Parent to additional windows
84  m_hsplitter->SetExtraStyle( wxWS_EX_TRANSIENT );
85 
86  sizer->Add( m_hsplitter, 1, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 5 );
87  }
88  else
89  {
90  m_vsplitter = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
91  wxSP_LIVE_UPDATE );
92 
93  m_hsplitter = new wxSplitterWindow( m_vsplitter, wxID_ANY, wxDefaultPosition, wxDefaultSize,
94  wxSP_LIVE_UPDATE );
95 
96  //Avoid the splitter window being assigned as the Parent to additional windows
97  m_hsplitter->SetExtraStyle( wxWS_EX_TRANSIENT );
98 
99  auto detailsPanel = new wxPanel( m_vsplitter );
100  auto detailsSizer = new wxBoxSizer( wxVERTICAL );
101  detailsPanel->SetSizer( detailsSizer );
102 
103  details = new wxHtmlWindow( detailsPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize,
104  wxHW_SCROLLBAR_AUTO );
105  detailsSizer->Add( details, 1, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 5 );
106  detailsPanel->Layout();
107  detailsSizer->Fit( detailsPanel );
108 
109  m_vsplitter->SetSashGravity( 0.5 );
110  m_vsplitter->SetMinimumPaneSize( 20 );
111  m_vsplitter->SplitHorizontally( m_hsplitter, detailsPanel );
112 
113  sizer->Add( m_vsplitter, 1, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 5 );
114  }
115 
116  m_tree = new LIB_TREE( m_hsplitter, Prj().SchSymbolLibTable(), aAdapter,
117  LIB_TREE::WIDGETS::ALL, details );
118 
119  m_hsplitter->SetSashGravity( 0.8 );
120  m_hsplitter->SetMinimumPaneSize( 20 );
121  m_hsplitter->SplitVertically( m_tree, ConstructRightPanel( m_hsplitter ) );
122 
123  m_dbl_click_timer = new wxTimer( this );
124 
125  auto buttonsSizer = new wxBoxSizer( wxHORIZONTAL );
126 
127  if( aAllowBrowser )
128  {
129  m_browser_button = new wxButton( this, wxID_ANY, _( "Select with Browser" ) );
130  buttonsSizer->Add( m_browser_button, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5 );
131  }
132 
133  auto sdbSizer = new wxStdDialogButtonSizer();
134  auto okButton = new wxButton( this, wxID_OK );
135  auto cancelButton = new wxButton( this, wxID_CANCEL );
136  sdbSizer->AddButton( okButton );
137  sdbSizer->AddButton( cancelButton );
138  sdbSizer->Realize();
139 
140  buttonsSizer->Add( sdbSizer, 1, wxALL, 5 );
141 
142  sizer->Add( buttonsSizer, 0, wxEXPAND | wxLEFT, 5 );
143  SetSizer( sizer );
144 
145  Bind( wxEVT_INIT_DIALOG, &DIALOG_CHOOSE_COMPONENT::OnInitDialog, this );
146  Bind( wxEVT_TIMER, &DIALOG_CHOOSE_COMPONENT::OnCloseTimer, this, m_dbl_click_timer->GetId() );
147  Bind( COMPONENT_PRESELECTED, &DIALOG_CHOOSE_COMPONENT::OnComponentPreselected, this );
148  Bind( COMPONENT_SELECTED, &DIALOG_CHOOSE_COMPONENT::OnComponentSelected, this );
149 
150  if( m_browser_button )
151  m_browser_button->Bind( wxEVT_COMMAND_BUTTON_CLICKED,
153 
154  if( m_fp_sel_ctrl )
155  m_fp_sel_ctrl->Bind( EVT_FOOTPRINT_SELECTED,
157 
158  Layout();
159 
160  // We specify the width of the right window (m_symbol_view_panel), because specify
161  // the width of the left window does not work as expected when SetSashGravity() is called
162  m_hsplitter->SetSashPosition( m_h_sash_pos ? m_h_sash_pos : HorizPixelsFromDU( 240 ) );
163 
164  if( m_vsplitter )
165  m_vsplitter->SetSashPosition( m_v_sash_pos ? m_v_sash_pos : VertPixelsFromDU( 170 ) );
166 
167  if( m_last_dlg_size == wxSize( -1, -1 ) )
168  SetSizeInDU( 360, 280 );
169  else
170  SetSize( m_last_dlg_size );
171 
173  okButton->SetDefault();
174 }
175 
176 
178 {
179  Unbind( wxEVT_INIT_DIALOG, &DIALOG_CHOOSE_COMPONENT::OnInitDialog, this );
180  Unbind( wxEVT_TIMER, &DIALOG_CHOOSE_COMPONENT::OnCloseTimer, this );
181  Unbind( COMPONENT_PRESELECTED, &DIALOG_CHOOSE_COMPONENT::OnComponentPreselected, this );
182  Unbind( COMPONENT_SELECTED, &DIALOG_CHOOSE_COMPONENT::OnComponentSelected, this );
183 
184  if( m_browser_button )
185  m_browser_button->Unbind( wxEVT_COMMAND_BUTTON_CLICKED,
187 
188  if( m_fp_sel_ctrl )
189  m_fp_sel_ctrl->Unbind( EVT_FOOTPRINT_SELECTED,
191 
192  // I am not sure the following two lines are necessary,
193  // but they will not hurt anyone
194  m_dbl_click_timer->Stop();
195  delete m_dbl_click_timer;
196 
197  m_last_dlg_size = GetSize();
198  m_h_sash_pos = m_hsplitter->GetSashPosition();
199 
200  if( m_vsplitter )
201  m_v_sash_pos = m_vsplitter->GetSashPosition();
202 }
203 
204 
205 wxPanel* DIALOG_CHOOSE_COMPONENT::ConstructRightPanel( wxWindow* aParent )
206 {
207  auto panel = new wxPanel( aParent );
208  auto sizer = new wxBoxSizer( wxVERTICAL );
209 
212  m_symbol_preview->SetLayoutDirection( wxLayout_LeftToRight );
213 
214  if( m_show_footprints )
215  {
217 
218  sizer->Add( m_symbol_preview, 1, wxEXPAND | wxTOP | wxBOTTOM | wxRIGHT, 5 );
219 
220  if ( fp_list )
221  {
222 
223  if( m_allow_field_edits )
224  m_fp_sel_ctrl = new FOOTPRINT_SELECT_WIDGET( panel, fp_list, true );
225 
226  m_fp_preview = new FOOTPRINT_PREVIEW_WIDGET( panel, Kiway() );
227  }
228 
229  if( m_fp_sel_ctrl )
230  sizer->Add( m_fp_sel_ctrl, 0, wxEXPAND | wxBOTTOM | wxTOP | wxRIGHT, 5 );
231 
232  if( m_fp_preview )
233  sizer->Add( m_fp_preview, 1, wxEXPAND | wxBOTTOM | wxRIGHT, 5 );
234  }
235  else
236  {
237  sizer->Add( m_symbol_preview, 1, wxEXPAND | wxTOP | wxRIGHT, 5 );
238  }
239 
240  panel->SetSizer( sizer );
241  panel->Layout();
242  sizer->Fit( panel );
243 
244  return panel;
245 }
246 
247 
248 void DIALOG_CHOOSE_COMPONENT::OnInitDialog( wxInitDialogEvent& aEvent )
249 {
251  {
252  // This hides the GAL panel and shows the status label
253  m_fp_preview->SetStatusText( wxEmptyString );
254  }
255 
256  if( m_fp_sel_ctrl )
257  m_fp_sel_ctrl->Load( Kiway(), Prj() );
258 }
259 
260 
262 {
263  return m_tree->GetSelectedLibId( aUnit );
264 }
265 
266 
267 void DIALOG_CHOOSE_COMPONENT::OnUseBrowser( wxCommandEvent& aEvent )
268 {
270  EndQuasiModal( wxID_OK );
271 }
272 
273 
274 void DIALOG_CHOOSE_COMPONENT::OnCloseTimer( wxTimerEvent& aEvent )
275 {
276  // Hack handler because of eaten MouseUp event. See
277  // DIALOG_CHOOSE_COMPONENT::OnComponentSelected for the beginning
278  // of this spaghetti noodle.
279 
280  auto state = wxGetMouseState();
281 
282  if( state.LeftIsDown() )
283  {
284  // Mouse hasn't been raised yet, so fire the timer again. Otherwise the
285  // purpose of this timer is defeated.
287  }
288  else
289  {
290  EndQuasiModal( wxID_OK );
291  }
292 }
293 
294 
296 {
298  return;
299 
300  LIB_ALIAS* alias = nullptr;
301 
302  try
303  {
304  alias = Prj().SchSymbolLibTable()->LoadSymbol( aLibId );
305  }
306  catch( const IO_ERROR& ioe )
307  {
308  wxLogError( wxString::Format( _( "Error loading symbol %s from library %s.\n\n%s" ),
309  aLibId.GetLibItemName().wx_str(),
310  aLibId.GetLibNickname().wx_str(),
311  ioe.What() ) );
312  }
313 
314  if( !alias )
315  return;
316 
317  LIB_FIELD* fp_field = alias->GetPart()->GetField( FOOTPRINT );
318  wxString fp_name = fp_field ? fp_field->GetFullText() : wxString( "" );
319 
320  ShowFootprint( fp_name );
321 }
322 
323 
324 void DIALOG_CHOOSE_COMPONENT::ShowFootprint( wxString const& aName )
325 {
327  return;
328 
329  if( aName == wxEmptyString )
330  {
331  m_fp_preview->SetStatusText( _( "No footprint specified" ) );
332  }
333  else
334  {
335  LIB_ID lib_id;
336 
337  if( lib_id.Parse( aName, LIB_ID::ID_PCB ) == -1 && lib_id.IsValid() )
338  {
340  m_fp_preview->CacheFootprint( lib_id );
341  m_fp_preview->DisplayFootprint( lib_id );
342  }
343  else
344  {
345  m_fp_preview->SetStatusText( _( "Invalid footprint specified" ) );
346  }
347  }
348 }
349 
350 
352 {
353  if( !m_fp_sel_ctrl )
354  return;
355 
357 
358  LIB_ALIAS* alias = nullptr;
359 
360  if( aLibId.IsValid() )
361  {
362  try
363  {
364  alias = Prj().SchSymbolLibTable()->LoadSymbol( aLibId );
365  }
366  catch( const IO_ERROR& ioe )
367  {
368  wxLogError( wxString::Format( _( "Error occurred loading symbol %s from library %s."
369  "\n\n%s" ),
370  aLibId.GetLibItemName().wx_str(),
371  aLibId.GetLibNickname().wx_str(),
372  ioe.What() ) );
373  }
374  }
375 
376  if( alias != nullptr )
377  {
378  LIB_PINS temp_pins;
379  LIB_FIELD* fp_field = alias->GetPart()->GetField( FOOTPRINT );
380  wxString fp_name = fp_field ? fp_field->GetFullText() : wxString( "" );
381 
382  alias->GetPart()->GetPins( temp_pins );
383 
384  m_fp_sel_ctrl->FilterByPinCount( temp_pins.size() );
389  }
390  else
391  {
393  m_fp_sel_ctrl->Disable();
394  }
395 }
396 
397 
398 void DIALOG_CHOOSE_COMPONENT::OnFootprintSelected( wxCommandEvent& aEvent )
399 {
400  m_fp_override = aEvent.GetString();
401 
402  m_field_edits.erase(
403  std::remove_if( m_field_edits.begin(), m_field_edits.end(),
404  []( std::pair<int, wxString> const& i ) { return i.first == FOOTPRINT; } ),
405  m_field_edits.end() );
406 
407  m_field_edits.push_back( std::make_pair( FOOTPRINT, m_fp_override ) );
408 
410 }
411 
412 
414 {
415  int unit = 0;
416 
417  LIB_ID id = m_tree->GetSelectedLibId( &unit );
418 
419 
420  if( id.IsValid() )
421  {
422  m_symbol_preview->DisplaySymbol( id, unit );
423 
424  ShowFootprintFor( id );
426  }
427  else
428  {
429  m_symbol_preview->SetStatusText( _( "No symbol selected" ) );
430 
432  m_fp_preview->SetStatusText( wxEmptyString );
433 
435  }
436 }
437 
438 
439 void DIALOG_CHOOSE_COMPONENT::OnComponentSelected( wxCommandEvent& aEvent )
440 {
441  if( m_tree->GetSelectedLibId().IsValid() )
442  {
443  // Got a selection. We can't just end the modal dialog here, because
444  // wx leaks some events back to the parent window (in particular, the
445  // MouseUp following a double click).
446  //
447  // NOW, here's where it gets really fun. wxTreeListCtrl eats MouseUp.
448  // This isn't really feasible to bypass without a fully custom
449  // wxDataViewCtrl implementation, and even then might not be fully
450  // possible (docs are vague). To get around this, we use a one-shot
451  // timer to schedule the dialog close.
452  //
453  // See DIALOG_CHOOSE_COMPONENT::OnCloseTimer for the other end of this
454  // spaghetti noodle.
456  }
457 }
458 
459 
FOOTPRINT_SELECT_WIDGET * m_fp_sel_ctrl
bool IsValid() const
Definition: lib_id.h:171
LIB_FIELD * GetField(int aId)
Return pointer to the requested field.
Part library alias object definition.
KIWAY & Kiway() const
Function Kiway returns a reference to the KIWAY that this object has an opportunity to participate in...
Definition: kiway_player.h:60
void FilterByFootprintFilters(wxArrayString const &aFilters, bool aZeroFilters)
Filter by footprint filter list.
std::vector< std::pair< int, wxString > > m_field_edits
void PopulateFootprintSelector(LIB_ID const &aLibId)
Populate the footprint selector for a given alias.
GAL_TYPE GetBackend() const
Function GetBackend Returns the type of backend currently used by GAL canvas.
void OnFootprintSelected(wxCommandEvent &aEvent)
Field object used in symbol libraries.
Definition: lib_field.h:59
SYMBOL_PREVIEW_WIDGET * m_symbol_preview
void SetDefaultFootprint(wxString const &aFp)
Set the default footprint for a part.
void ShowFootprint(wxString const &aFootprint)
Display the given footprint by name.
Class DIALOG_SHIM may sit in the inheritance tree between wxDialog and any class written by wxFormBui...
Definition: dialog_shim.h:53
wxString GetFullText(int unit=1) const
Return the text of a field.
Definition: lib_field.cpp:363
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
void SetStatusText(wxString const &aText)
Set the contents of the status label and display it.
void SetInitialFocus(wxWindow *aWindow)
Sets the window (usually a wxTextCtrl) that should be focused when the dialog is shown.
Definition: dialog_shim.h:86
Field Name Module PCB, i.e. "16DIP300".
void OnComponentPreselected(wxCommandEvent &aEvent)
void ClearFilters()
Clear all filters.
bool UpdateList()
Update the contents of the list to match the filters.
void DisplaySymbol(const LIB_ID &aSymbolID, int aUnit)
Set the currently displayed symbol.
void GetPins(LIB_PINS &aList, int aUnit=0, int aConvert=0)
Return a list of pin object pointers from the draw item list.
PROJECT & Prj() const
Function Prj returns a reference to the PROJECT "associated with" this KIWAY.
static FOOTPRINT_LIST * GetInstance(KIWAY &aKiway)
Factory function to return a FOOTPRINT_LIST via Kiway.
wxString wx_str() const
Definition: utf8.cpp:51
DIALOG_CHOOSE_COMPONENT(SCH_BASE_FRAME *aParent, const wxString &aTitle, SYMBOL_TREE_MODEL_ADAPTER::PTR &aAdapter, int aDeMorganConvert, bool aAllowFieldEdits, bool aShowFootprints, bool aAllowBrowser)
Create dialog to choose component.
void OnInitDialog(wxInitDialogEvent &aEvent)
void ClearStatus()
Clear the contents of the status label and hide it.
const UTF8 & GetLibItemName() const
Definition: lib_id.h:114
LIB_ID GetSelectedLibId(int *aUnit=nullptr) const
For multi-unit components, if the user selects the component itself rather than picking an individual...
Definition: lib_tree.cpp:144
void SetSizeInDU(int x, int y)
Set the dialog to the given dimensions in "dialog units".
void OnCloseTimer(wxTimerEvent &aEvent)
virtual bool Enable(bool aEnable=true) override
Enable or disable the control for input.
wxPanel * ConstructRightPanel(wxWindow *aParent)
Definition: hash_eda.h:46
void SetStatusText(wxString const &aText)
Set the contents of the status label and display it.
void OnUseBrowser(wxCommandEvent &aEvent)
wxArrayString & GetFootprints()
int VertPixelsFromDU(int y)
Convert an integer number of dialog units to pixels, vertically.
LIB_PART * GetPart() const
Get the shared LIB_PART.
void EndQuasiModal(int retCode)
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:33
std::vector< LIB_PIN * > LIB_PINS
Helper for defining a list of pin object pointers.
Definition: lib_draw_item.h:61
void CacheFootprint(const LIB_ID &aFPID)
Preload a footprint into the cache.
Holds a list of FOOTPRINT_INFO objects, along with a list of IO_ERRORs or PARSE_ERRORs that were thro...
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
bool IsInitialized() const
Return whether the widget initialized properly.
void DisplayFootprint(const LIB_ID &aFPID)
Set the currently displayed footprint.
static constexpr int DblClickDelay
size_t i
Definition: json11.cpp:597
FOOTPRINT_PREVIEW_WIDGET * m_fp_preview
LIB_ID GetSelectedLibId(int *aUnit=nullptr) const
To be called after this dialog returns from ShowModal().
void Load(KIWAY &aKiway, PROJECT &aProject)
Start loading.
int HorizPixelsFromDU(int x)
Convert an integer number of dialog units to pixels, horizontally.
EDA_DRAW_PANEL_GAL * GetGalCanvas() const
Return a pointer to GAL-based canvas of given EDA draw frame.
Definition: draw_frame.h:908
void ShowFootprintFor(LIB_ID const &aLibId)
Look up the footprint for a given symbol specified in the LIB_ID and display it.
wxObjectDataPtr< LIB_TREE_MODEL_ADAPTER > PTR
Reference-counting container for a pointer to CMP_TREE_MODEL_ADAPTER_BASE.
Definition for part library class.
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition: lib_id.h:97
int Parse(const UTF8 &aId, LIB_ID_TYPE aType, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:122
A shim class between EDA_DRAW_FRAME and several derived classes: LIB_EDIT_FRAME, LIB_VIEW_FRAME, and SCH_EDIT_FRAME, and it brings in a common way of handling the provided virtual functions for the derived classes.
void OnComponentSelected(wxCommandEvent &aEvent)
Handle the selection of an item.
Struct IO_ERROR is a class used to hold an error message and may be used when throwing exceptions con...
Definition: ki_exception.h:76
void FilterByPinCount(int aPinCount)
Filter by pin count.
Widget displaying a tree of components with optional search text control and description panel...
Definition: lib_tree.h:42