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) 2017 Chris Pavlina <pavlina.chris@gmail.com>
6  * Copyright (C) 2016-2017 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 
27 
28 #include <algorithm>
29 #include <set>
30 #include <wx/utils.h>
31 
32 #include <wx/button.h>
33 #include <wx/dataview.h>
34 #include <wx/panel.h>
35 #include <wx/sizer.h>
36 #include <wx/splitter.h>
37 #include <wx/timer.h>
38 #include <wx/utils.h>
39 
40 #include <class_library.h>
41 #include <sch_base_frame.h>
42 #include <template_fieldnames.h>
43 #include <widgets/component_tree.h>
46 
47 
49 std::unique_ptr<FOOTPRINT_LIST> DIALOG_CHOOSE_COMPONENT::m_fp_list;
50 
52  CMP_TREE_MODEL_ADAPTER::PTR& aAdapter, int aDeMorganConvert, bool aAllowFieldEdits )
53  : DIALOG_SHIM( aParent, wxID_ANY, aTitle, wxDefaultPosition, wxSize( 800, 650 ),
54  wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
55  m_parent( aParent ),
56  m_deMorganConvert( aDeMorganConvert >= 0 ? aDeMorganConvert : 0 ),
57  m_allow_field_edits( aAllowFieldEdits ),
58  m_external_browser_requested( false )
59 {
60  wxBusyCursor busy_while_loading;
61 
62  auto sizer = new wxBoxSizer( wxVERTICAL );
63 
64  auto splitter = new wxSplitterWindow(
65  this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_LIVE_UPDATE );
66  m_tree = new COMPONENT_TREE( splitter, aAdapter );
67  auto right_panel = ConstructRightPanel( splitter );
68  auto buttons = new wxStdDialogButtonSizer();
69  m_dbl_click_timer = new wxTimer( this );
70 
71  splitter->SetSashGravity( 0.9 );
72  splitter->SetMinimumPaneSize( 1 );
73  splitter->SplitVertically( m_tree, right_panel, -300 );
74 
75  buttons->AddButton( new wxButton( this, wxID_OK ) );
76  buttons->AddButton( new wxButton( this, wxID_CANCEL ) );
77  buttons->Realize();
78 
79  sizer->Add( splitter, 1, wxEXPAND | wxALL, 5 );
80  sizer->Add( buttons, 0, wxEXPAND | wxBOTTOM, 10 );
81  SetSizer( sizer );
82 
83  Bind( wxEVT_INIT_DIALOG, &DIALOG_CHOOSE_COMPONENT::OnInitDialog, this );
84  Bind( wxEVT_TIMER, &DIALOG_CHOOSE_COMPONENT::OnCloseTimer, this, m_dbl_click_timer->GetId() );
85  Bind( COMPONENT_PRESELECTED, &DIALOG_CHOOSE_COMPONENT::OnComponentPreselected, this );
86  Bind( COMPONENT_SELECTED, &DIALOG_CHOOSE_COMPONENT::OnComponentSelected, this );
87 
88  m_sch_view_ctrl->Bind( wxEVT_LEFT_DCLICK, &DIALOG_CHOOSE_COMPONENT::OnSchViewDClick, this );
89  m_sch_view_ctrl->Bind( wxEVT_PAINT, &DIALOG_CHOOSE_COMPONENT::OnSchViewPaint, this );
90 
91  if( m_fp_sel_ctrl )
92  m_fp_sel_ctrl->Bind(
93  EVT_FOOTPRINT_SELECTED, &DIALOG_CHOOSE_COMPONENT::OnFootprintSelected, this );
94 
95  Layout();
96 }
97 
98 
100 {
101  // I am not sure the following two lines are necessary,
102  // but they will not hurt anyone
103  m_dbl_click_timer->Stop();
104  Unbind( wxEVT_TIMER, &DIALOG_CHOOSE_COMPONENT::OnCloseTimer, this );
105 
106  delete m_dbl_click_timer;
107 }
108 
109 
110 wxPanel* DIALOG_CHOOSE_COMPONENT::ConstructRightPanel( wxWindow* aParent )
111 {
112  auto panel = new wxPanel( aParent );
113  auto sizer = new wxBoxSizer( wxVERTICAL );
114 
115  m_sch_view_ctrl = new wxPanel( panel, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ),
116  wxFULL_REPAINT_ON_RESIZE | wxSUNKEN_BORDER | wxTAB_TRAVERSAL );
117  m_sch_view_ctrl->SetLayoutDirection( wxLayout_LeftToRight );
118 
119  if( m_allow_field_edits )
121  else
122  m_fp_sel_ctrl = nullptr;
123 
125 
126 
127  sizer->Add( m_sch_view_ctrl, 1, wxEXPAND | wxALL, 5 );
128 
129  if( m_fp_sel_ctrl )
130  sizer->Add( m_fp_sel_ctrl, 0, wxEXPAND | wxALL, 5 );
131 
132  sizer->Add( m_fp_view_ctrl, 1, wxEXPAND | wxALL, 5 );
133 
134 
135  panel->SetSizer( sizer );
136  panel->Layout();
137  sizer->Fit( panel );
138 
139  return panel;
140 }
141 
142 
143 void DIALOG_CHOOSE_COMPONENT::OnInitDialog( wxInitDialogEvent& aEvent )
144 {
146  {
147  // This hides the GAL panel and shows the status label
148  m_fp_view_ctrl->SetStatusText( wxEmptyString );
149  }
150 
151  if( m_fp_sel_ctrl )
152  m_fp_sel_ctrl->Load( Kiway(), Prj() );
153 }
154 
155 
157 {
158  return m_tree->GetSelectedAlias( aUnit );
159 }
160 
161 
162 void DIALOG_CHOOSE_COMPONENT::OnCloseTimer( wxTimerEvent& aEvent )
163 {
164  // Hack handler because of eaten MouseUp event. See
165  // DIALOG_CHOOSE_COMPONENT::OnDoubleClickTreeActivation for the beginning
166  // of this spaghetti noodle.
167 
168  auto state = wxGetMouseState();
169 
170  if( state.LeftIsDown() )
171  {
172  // Mouse hasn't been raised yet, so fire the timer again. Otherwise the
173  // purpose of this timer is defeated.
175  }
176  else
177  {
178  EndQuasiModal( wxID_OK );
179  }
180 }
181 
182 
183 void DIALOG_CHOOSE_COMPONENT::OnSchViewDClick( wxMouseEvent& aEvent )
184 {
186  EndQuasiModal( wxID_OK );
187 }
188 
189 
191 {
192  if( !m_fp_view_ctrl->IsInitialized() )
193  return;
194 
195  LIB_FIELD* fp_field = aAlias->GetPart()->GetField( FOOTPRINT );
196  wxString fp_name = fp_field ? fp_field->GetFullText() : wxString( "" );
197 
198  ShowFootprint( fp_name );
199 }
200 
201 
202 void DIALOG_CHOOSE_COMPONENT::ShowFootprint( wxString const& aName )
203 {
204  if( aName == wxEmptyString )
205  {
206  m_fp_view_ctrl->SetStatusText( _( "No footprint specified" ) );
207  }
208  else
209  {
210  LIB_ID lib_id( aName );
211 
213  m_fp_view_ctrl->CacheFootprint( lib_id );
214  m_fp_view_ctrl->DisplayFootprint( lib_id );
215  }
216 }
217 
218 
220 {
221  if( !m_fp_sel_ctrl )
222  return;
223 
225 
226  if( aAlias )
227  {
228  LIB_PINS temp_pins;
229  LIB_FIELD* fp_field = aAlias->GetPart()->GetField( FOOTPRINT );
230  wxString fp_name = fp_field ? fp_field->GetFullText() : wxString( "" );
231 
232  aAlias->GetPart()->GetPins( temp_pins );
233 
234  m_fp_sel_ctrl->FilterByPinCount( temp_pins.size() );
239  }
240  else
241  {
243  m_fp_sel_ctrl->Disable();
244  }
245 }
246 
247 
248 void DIALOG_CHOOSE_COMPONENT::OnSchViewPaint( wxPaintEvent& aEvent )
249 {
250  int unit = 0;
251  LIB_ALIAS* alias = m_tree->GetSelectedAlias( &unit );
252  LIB_PART* part = alias ? alias->GetPart() : nullptr;
253 
254  // Don't draw anything (not even the background) if we don't have
255  // a part to show
256  if( !part )
257  return;
258 
259  if( alias->IsRoot() )
260  {
261  // just show the part directly
262  RenderPreview( part, unit );
263  }
264  else
265  {
266  // switch out the name temporarily for the alias name
267  wxString tmp( part->GetName() );
268  part->SetName( alias->GetName() );
269 
270  RenderPreview( part, unit );
271 
272  part->SetName( tmp );
273  }
274 }
275 
276 
277 void DIALOG_CHOOSE_COMPONENT::OnFootprintSelected( wxCommandEvent& aEvent )
278 {
279  m_fp_override = aEvent.GetString();
280 
281  m_field_edits.erase(
282  std::remove_if( m_field_edits.begin(), m_field_edits.end(),
283  []( std::pair<int, wxString> const& i ) { return i.first == FOOTPRINT; } ),
284  m_field_edits.end() );
285 
286  m_field_edits.push_back( std::make_pair( FOOTPRINT, m_fp_override ) );
287 
289 }
290 
291 
293 {
294  int unit = 0;
295  LIB_ALIAS* alias = m_tree->GetSelectedAlias( &unit );
296 
297  m_sch_view_ctrl->Refresh();
298 
299  if( alias )
300  {
301  ShowFootprintFor( alias );
302  PopulateFootprintSelector( alias );
303  }
304  else
305  {
307  m_fp_view_ctrl->SetStatusText( wxEmptyString );
308 
309  PopulateFootprintSelector( nullptr );
310  }
311 }
312 
313 
314 void DIALOG_CHOOSE_COMPONENT::OnComponentSelected( wxCommandEvent& aEvent )
315 {
316  if( m_tree->GetSelectedAlias() )
317  {
318  // Got a selection. We can't just end the modal dialog here, because
319  // wx leaks some events back to the parent window (in particular, the
320  // MouseUp following a double click).
321  //
322  // NOW, here's where it gets really fun. wxTreeListCtrl eats MouseUp.
323  // This isn't really feasible to bypass without a fully custom
324  // wxDataViewCtrl implementation, and even then might not be fully
325  // possible (docs are vague). To get around this, we use a one-shot
326  // timer to schedule the dialog close.
327  //
328  // See DIALOG_CHOOSE_COMPONENT::OnCloseTimer for the other end of this
329  // spaghetti noodle.
331  }
332 }
333 
334 
335 void DIALOG_CHOOSE_COMPONENT::RenderPreview( LIB_PART* aComponent, int aUnit )
336 {
337  wxPaintDC dc( m_sch_view_ctrl );
338 
339  const wxSize dc_size = dc.GetSize();
340 
341  // Avoid rendering when either dimension is zero
342  if( dc_size.x == 0 || dc_size.y == 0 )
343  return;
344 
345  GRResetPenAndBrush( &dc );
346 
347  COLOR4D bgColor = m_parent->GetDrawBgColor();
348 
349  dc.SetBackground( wxBrush( bgColor.ToColour() ) );
350  dc.Clear();
351 
352  if( !aComponent )
353  return;
354 
355  int unit = aUnit > 0 ? aUnit : 1;
356  int convert = m_deMorganConvert > 0 ? m_deMorganConvert : 1;
357 
358  dc.SetDeviceOrigin( dc_size.x / 2, dc_size.y / 2 );
359 
360  // Find joint bounding box for everything we are about to draw.
361  EDA_RECT bBox = aComponent->GetUnitBoundingBox( unit, convert );
362  const double xscale = (double) dc_size.x / bBox.GetWidth();
363  const double yscale = (double) dc_size.y / bBox.GetHeight();
364  const double scale = std::min( xscale, yscale ) * 0.85;
365 
366  dc.SetUserScale( scale, scale );
367 
368  wxPoint offset = -bBox.Centre();
369 
370  auto opts = PART_DRAW_OPTIONS::Default();
371  opts.draw_hidden_fields = false;
372  aComponent->Draw( nullptr, &dc, offset, unit, convert, opts );
373 }
FOOTPRINT_SELECT_WIDGET * m_fp_sel_ctrl
void GRResetPenAndBrush(wxDC *DC)
Definition: gr_basic.cpp:196
LIB_FIELD * GetField(int aId)
Return pointer to the requested field.
void Draw(EDA_DRAW_PANEL *aPanel, wxDC *aDc, const wxPoint &aOffset, int aMulti, int aConvert, const PART_DRAW_OPTIONS &aOpts)
Draw part.
const EDA_RECT GetUnitBoundingBox(int aUnit, int aConvert) const
Function GetBoundingBox.
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
LIB_ALIAS * GetSelectedAlias(int *aUnit=nullptr) const
For multi-unit components, if the user selects the component itself rather than picking an individual...
void FilterByFootprintFilters(wxArrayString const &aFilters, bool aZeroFilters)
Filter by footprint filter list.
std::vector< std::pair< int, wxString > > m_field_edits
void OnFootprintSelected(wxCommandEvent &aEvent)
Class LIB_FIELD is used in symbol libraries.
Definition: lib_field.h:60
void SetDefaultFootprint(wxString const &aFp)
Set the default footprint for a part.
int GetHeight() const
This class can be used to populate a FOOTPRINT_LIST asynchronously.
wxObjectDataPtr< CMP_TREE_MODEL_ADAPTER > PTR
Reference-counting container for a pointer to CMP_TREE_MODEL_ADAPTER.
Class DIALOG_SHIM may sit in the inheritance tree between wxDialog and any class written by wxFormBui...
Definition: dialog_shim.h:60
wxString GetFullText(int unit=1) const
Return the text of a field.
Definition: lib_field.cpp:533
static FOOTPRINT_ASYNC_LOADER m_fp_loader
Class LIB_ID.
Definition: lib_id.h:56
Field Name Module PCB, i.e. "16DIP300".
void OnComponentPreselected(wxCommandEvent &aEvent)
void ClearFilters()
Clear all filters.
DIALOG_CHOOSE_COMPONENT(SCH_BASE_FRAME *aParent, const wxString &aTitle, CMP_TREE_MODEL_ADAPTER::PTR &aAdapter, int aDeMorganConvert, bool aAllowFieldEdits)
Create dialog to choose component.
bool UpdateList()
Update the contents of the list to match the filters.
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.
bool IsRoot() const
void OnInitDialog(wxInitDialogEvent &aEvent)
void ShowFootprint(wxString const &aName)
Display the given footprint by name.
void ClearStatus()
Clear the contents of the status label and hide it.
const wxString & GetName() const
LIB_ALIAS * GetSelectedAlias(int *aUnit=nullptr) const
To be called after this dialog returns from ShowModal().
Class LIB_PART defines a library part object.
static std::unique_ptr< FOOTPRINT_LIST > m_fp_list
void OnCloseTimer(wxTimerEvent &aEvent)
virtual bool Enable(bool aEnable=true) override
Enable or disable the control for input.
wxPanel * ConstructRightPanel(wxWindow *aParent)
wxPoint Centre() const
static PART_DRAW_OPTIONS Default()
void SetStatusText(wxString const &aText)
Set the contents of the status label and display it.
COLOR4D GetDrawBgColor() const override
Widget displaying a tree of components with optional search text control and description panel...
LIB_PART * GetPart() const
Function GetPart gets the shared LIB_PART.
void EndQuasiModal(int retCode)
std::vector< LIB_PIN * > LIB_PINS
Helper for defining a list of pin object pointers.
Definition: lib_draw_item.h:70
const int scale
void CacheFootprint(const LIB_ID &aFPID)
Preload a footprint into the cache.
void PopulateFootprintSelector(LIB_ALIAS *aAlias)
Populate the footprint selector for a given alias.
const wxString & GetName() const
bool IsInitialized() const
Return whether the widget initialized properly.
void DisplayFootprint(const LIB_ID &aFPID)
Set the currently displayed footprint.
static constexpr int DblClickDelay
Class EDA_RECT handles the component boundary box.
wxArrayString & GetFootPrints()
int GetWidth() const
void OnSchViewDClick(wxMouseEvent &aEvent)
virtual void SetName(const wxString &aName)
void Load(KIWAY &aKiway, PROJECT &aProject)
Start loading.
FOOTPRINT_PREVIEW_WIDGET * m_fp_view_ctrl
Definition for part library class.
void OnSchViewPaint(wxPaintEvent &aEvent)
Class SCH_BASE_FRAME is a shim class between EDA_DRAW_FRAME and several derived classes: LIB_EDIT_FRA...
void OnComponentSelected(wxCommandEvent &aEvent)
Handle the selection of an item.
void FilterByPinCount(int aPinCount)
Filter by pin count.
void ShowFootprintFor(LIB_ALIAS *aAlias)
Look up the footprint for a given alias and display it.
#define min(a, b)
Definition: auxiliary.h:85
void RenderPreview(LIB_PART *aComponent, int aUnit)
Display a given component into the schematic symbol preview.
Class COLOR4D is the color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:39