KiCad PCB EDA Suite
footprint_select_widget.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) 2017 KiCad Developers, see AUTHORS.txt for contributors.
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <dialog_shim.h>
21 #include <kiway.h>
22 #include <kiway_player.h>
23 #include <make_unique.h>
24 #include <project.h>
27 
28 #include <functional>
29 #include <wx/combo.h>
30 #include <wx/gauge.h>
31 #include <wx/odcombo.h>
32 #include <wx/simplebook.h>
33 #include <wx/sizer.h>
34 #include <wx/timer.h>
35 #include <wx/utils.h>
36 #include <wx/wupdlock.h>
37 
38 
42 enum
43 {
47 };
48 
49 
53 enum
54 {
57 };
58 
59 
60 wxDEFINE_EVENT( EVT_FOOTPRINT_SELECTED, wxCommandEvent );
61 
62 
64  FOOTPRINT_ASYNC_LOADER& aLoader, std::unique_ptr<FOOTPRINT_LIST>& aFpList, bool aUpdate,
65  int aMaxItems )
66  : wxPanel( aParent ),
67  m_kiway( nullptr ),
68  m_update( aUpdate ),
69  m_finished_loading( false ),
70  m_max_items( aMaxItems ),
71  m_last_item( 0 ),
72  m_fp_loader( aLoader ),
73  m_fp_list( aFpList )
74 {
75  m_sizer = new wxBoxSizer( wxVERTICAL );
76  m_progress_timer = std::make_unique<wxTimer>( this );
77  m_book = new wxSimplebook( this, wxID_ANY );
78  m_progress_ctrl = new wxGauge( m_book, wxID_ANY, 100 );
79  m_fp_sel_ctrl = new FOOTPRINT_CHOICE( m_book, wxID_ANY );
80 
81  m_book->SetEffect( wxSHOW_EFFECT_BLEND );
82  m_book->AddPage( m_progress_ctrl, "", true );
83  m_book->AddPage( m_fp_sel_ctrl, "", false );
84  m_sizer->Add( m_book, 1, wxEXPAND | wxALL, 5 );
85 
86  SetSizer( m_sizer );
87  Layout();
88  m_sizer->Fit( this );
89 
90  Bind( wxEVT_TIMER, &FOOTPRINT_SELECT_WIDGET::OnProgressTimer, this, m_progress_timer->GetId() );
91  m_fp_sel_ctrl->Bind( wxEVT_COMBOBOX, &FOOTPRINT_SELECT_WIDGET::OnComboBox, this );
92  m_fp_sel_ctrl->Bind(
93  EVT_INTERACTIVE_CHOICE, &FOOTPRINT_SELECT_WIDGET::OnComboInteractive, this );
94 }
95 
96 
97 void FOOTPRINT_SELECT_WIDGET::Load( KIWAY& aKiway, PROJECT& aProject )
98 {
99  m_kiway = &aKiway;
100 
101  try
102  {
103  auto fp_lib_table = aProject.PcbFootprintLibs( aKiway );
104 
105  if( m_fp_loader.GetProgress() == 0 || !m_fp_loader.IsSameTable( fp_lib_table ) )
106  {
109  m_fp_loader.Start( fp_lib_table );
110  }
111 
112  m_progress_timer->Start( 200 );
113  }
114  catch( ... )
115  {
116  // no footprint libraries available
117  }
118 }
119 
120 
121 void FOOTPRINT_SELECT_WIDGET::OnProgressTimer( wxTimerEvent& aEvent )
122 {
123  int prog = m_fp_loader.GetProgress();
124  m_progress_ctrl->SetValue( prog );
125 
126  if( prog == 100 )
127  {
128  wxBusyCursor busy;
129 
130  m_fp_loader.Join();
132  m_progress_timer->Stop();
133 
134  m_book->SetSelection( PAGE_SELECT );
135  m_finished_loading = true;
136 
137  if( m_update )
138  UpdateList();
139  }
140 }
141 
142 
143 void FOOTPRINT_SELECT_WIDGET::OnComboBox( wxCommandEvent& aEvent )
144 {
145  wxCommandEvent evt( EVT_FOOTPRINT_SELECTED );
146  int sel = m_fp_sel_ctrl->GetSelection();
147 
148  switch( sel )
149  {
150  case wxNOT_FOUND: return;
151 
152  case POS_SEPARATOR:
153  // User somehow managed to select the separator. This should not be
154  // possible, but just in case... deselect it
155  m_fp_sel_ctrl->SetSelection( m_last_item );
156  break;
157 
158  case POS_OTHER:
159  // When POS_OTHER is selected, a dialog should be shown. However, we don't want to
160  // do this ALL the time, as some times (e.g. when moving around with the arrow keys)
161  // it could be very annoying. Therefore showing the picker is done from the custom
162  // "interactive select" event on FOOTPRINT_CHOICE, which only fires for more direct
163  // choice actions.
164  break;
165 
166  default:
167  {
168  wxStringClientData* clientdata =
169  static_cast<wxStringClientData*>( m_fp_sel_ctrl->GetClientObject( sel ) );
170  wxASSERT( clientdata );
171 
172  evt.SetString( clientdata->GetData() );
173  wxPostEvent( this, evt );
174  }
175  }
176 }
177 
178 
179 void FOOTPRINT_SELECT_WIDGET::OnComboInteractive( wxCommandEvent& aEvent )
180 {
181  if( aEvent.GetInt() == POS_OTHER && !m_fp_sel_ctrl->IsPopupShown() )
182  {
183  DoOther();
184  }
185 }
186 
187 
189 {
190  wxCommandEvent evt( EVT_FOOTPRINT_SELECTED );
191 
192  wxString fpname = ShowPicker();
193  m_other_footprint = fpname;
194  UpdateList();
195  m_fp_sel_ctrl->SetSelection( POS_OTHER );
197 
198  evt.SetString( m_other_footprint );
199  wxPostEvent( this, evt );
200 }
201 
202 
204 {
205  wxString fpname;
206  wxWindow* parent = ::wxGetTopLevelParent( this );
207  DIALOG_SHIM* dsparent = dynamic_cast<DIALOG_SHIM*>( parent );
208 
209  // Only quasimodal dialogs can launch modal kiface dialogs. Otherwise the
210  // event loop goes all silly.
211  wxASSERT( !dsparent || dsparent->IsQuasiModal() );
212 
213  auto frame = m_kiway->Player( FRAME_PCB_MODULE_VIEWER_MODAL, true );
214 
215  if( !frame->ShowModal( &fpname, parent ) )
216  {
217  fpname = wxEmptyString;
218  }
219 
220  frame->Destroy();
221 
222  return fpname;
223 }
224 
225 
227 {
229  m_default_footprint.Clear();
230  m_other_footprint.Clear();
231  m_zero_filter = false;
232 }
233 
234 
236 {
237  m_fp_filter.FilterByPinCount( aPinCount );
238 }
239 
240 
242  wxArrayString const& aFilters, bool aZeroFilters )
243 {
244  if( aZeroFilters && aFilters.size() == 0 )
245  m_zero_filter = true;
246  else
247  m_zero_filter = false;
248 
250 }
251 
252 
254 {
255  m_default_footprint = aFp;
256 }
257 
258 
260 {
261  int n_items = 0;
262 
263  if( !m_fp_list || !m_finished_loading )
264  return false;
265 
266  wxWindowUpdateLocker lock( m_fp_sel_ctrl );
267  m_fp_sel_ctrl->Clear();
268 
269  // Be careful adding items! "Default" must occupy POS_DEFAULT,
270  // "Other" must occupy POS_OTHER, and the separator must occupy POS_SEPARATOR.
271 
272  m_fp_sel_ctrl->Append( m_default_footprint.IsEmpty() ?
273  _( "No default footprint" ) :
274  "[" + _( "Default" ) + "] " + m_default_footprint,
275  new wxStringClientData( m_default_footprint ) );
276 
277  m_fp_sel_ctrl->Append( m_other_footprint.IsEmpty() ?
278  _( "Other..." ) :
279  "[" + _( "Other..." ) + "] " + m_other_footprint,
280  new wxStringClientData( m_other_footprint ) );
281 
282  m_fp_sel_ctrl->Append( "", new wxStringClientData( "" ) );
283 
284  if( !m_zero_filter )
285  {
286  for( auto& fpinfo : m_fp_filter )
287  {
288  wxString display_name( fpinfo.GetNickname() + ":" + fpinfo.GetFootprintName() );
289 
290  m_fp_sel_ctrl->Append( display_name, new wxStringClientData( display_name ) );
291  ++n_items;
292 
293  if( n_items >= m_max_items )
294  break;
295  }
296  }
297 
298  SelectDefault();
299  return true;
300 }
301 
302 
304 {
305  m_fp_sel_ctrl->SetSelection( POS_DEFAULT );
306 }
307 
308 
310 {
311  return m_fp_sel_ctrl->Enable( aEnable );
312 }
void SetList(FOOTPRINT_LIST &aList)
Set the list to filter.
Class PROJECT holds project specific data.
Definition: project.h:56
void FilterByFootprintFilters(wxArrayString const &aFilters, bool aZeroFilters)
Filter by footprint filter list.
void OnComboInteractive(wxCommandEvent &aEvent)
void DoOther()
Handle activation of the "Other..." item.
void FilterByFootprintFilters(wxArrayString const &aFilters)
Set a list of footprint filters to filter by.
FOOTPRINT_ASYNC_LOADER & m_fp_loader
void ClearFilters()
Clear all filter criteria.
void SetDefaultFootprint(wxString const &aFp)
Set the default footprint for a part.
std::unique_ptr< FOOTPRINT_LIST > & m_fp_list
Customized combo box for footprint selection.
This class can be used to populate a FOOTPRINT_LIST asynchronously.
wxString ShowPicker()
Show the component picker and return the selected component. Used by DoOther()
Class DIALOG_SHIM may sit in the inheritance tree between wxDialog and any class written by wxFormBui...
Definition: dialog_shim.h:60
wxDEFINE_EVENT(EVT_FOOTPRINT_SELECTED, wxCommandEvent)
static std::unique_ptr< FOOTPRINT_LIST > GetInstance(KIWAY &aKiway)
Factory function to return a new FOOTPRINT_LIST via Kiway.
void ClearFilters()
Clear all filters.
bool UpdateList()
Update the contents of the list to match the filters.
void FilterByPinCount(int aPinCount)
Set a pin count to filter by.
VTBL_ENTRY KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, KIWAY_PLAYER *aParent=NULL)
Function Player returns the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:302
void Start(FP_LIB_TABLE *aTable, wxString const *aNickname=nullptr, unsigned aNThreads=DEFAULT_THREADS)
Launch the worker threads.
void OnComboBox(wxCommandEvent &aEvent)
bool IsQuasiModal()
Definition: dialog_shim.h:93
Class KIWAY is a minimalistic software bus for communications between various DLLs/DSOs (DSOs) within...
Definition: kiway.h:257
virtual bool Enable(bool aEnable=true) override
Enable or disable the control for input.
void OnProgressTimer(wxTimerEvent &aEvent)
bool Join()
Wait until the worker threads are finished, and then perform any required single-threaded finishing o...
VTBL_ENTRY FP_LIB_TABLE * PcbFootprintLibs(KIWAY &aKiway)
Return the table of footprint libraries.
void SelectDefault()
Set current selection to the default footprint.
FOOTPRINT_SELECT_WIDGET(wxWindow *aParent, FOOTPRINT_ASYNC_LOADER &aLoader, std::unique_ptr< FOOTPRINT_LIST > &aFpList, bool aUpdate=true, int aMaxItems=400)
Construct a footprint selector widget.
Implementation of std::make_unique for pre C++14 compilation environments.
int GetProgress() const
Get the current completion percentage.
bool Destroy() override
Our version of Destroy() which is virtual from wxWidgets.
void Load(KIWAY &aKiway, PROJECT &aProject)
Start loading.
void SetList(FOOTPRINT_LIST *aList)
Assign a FOOTPRINT_LIST to the loader.
std::unique_ptr< wxTimer > m_progress_timer
bool IsSameTable(FP_LIB_TABLE *aOther)
Return true if the given table is the same as the last table loaded.
void FilterByPinCount(int aPinCount)
Filter by pin count.