KiCad PCB EDA Suite
dialog_choose_footprint.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-2021 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 #include <algorithm>
27 #include <wx/utils.h>
28 #include <wx/button.h>
29 #include <wx/panel.h>
30 #include <wx/sizer.h>
31 #include <wx/splitter.h>
32 #include <wx/timer.h>
33 #include <wx/wxhtml.h>
34 #include <pcb_base_frame.h>
35 #include <pcbnew_settings.h>
36 #include <pgm_base.h>
37 #include <fp_lib_table.h>
39 #include <widgets/lib_tree.h>
42 #include <kiface_base.h>
43 
44 
46  const wxString& aTitle,
47  wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>& aAdapter )
48  : DIALOG_SHIM( aParent, wxID_ANY, aTitle, wxDefaultPosition, wxDefaultSize,
49  wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
50  m_browser_button( nullptr ),
51  m_hsplitter( nullptr ),
52  m_vsplitter( nullptr ),
53  m_parent( aParent ),
54  m_external_browser_requested( false )
55 {
56  auto sizer = new wxBoxSizer( wxVERTICAL );
57  wxHtmlWindow* details = nullptr;
58 
59  m_vsplitter = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
60  wxSP_LIVE_UPDATE );
61 
62  m_hsplitter = new wxSplitterWindow( m_vsplitter, wxID_ANY, wxDefaultPosition, wxDefaultSize,
63  wxSP_LIVE_UPDATE );
64 
65  //Avoid the splitter window being assigned as the Parent to additional windows
66  m_hsplitter->SetExtraStyle( wxWS_EX_TRANSIENT );
67 
68  auto detailsPanel = new wxPanel( m_vsplitter );
69  auto detailsSizer = new wxBoxSizer( wxVERTICAL );
70  detailsPanel->SetSizer( detailsSizer );
71 
72  details = new wxHtmlWindow( detailsPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize,
73  wxHW_SCROLLBAR_AUTO );
74  detailsSizer->Add( details, 1, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 5 );
75  detailsPanel->Layout();
76  detailsSizer->Fit( detailsPanel );
77 
78  m_vsplitter->SetSashGravity( 0.5 );
79  m_vsplitter->SetMinimumPaneSize( 20 );
80  m_vsplitter->SplitHorizontally( m_hsplitter, detailsPanel );
81 
82  sizer->Add( m_vsplitter, 1, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 5 );
83 
84  m_tree = new LIB_TREE( m_hsplitter, Prj().PcbFootprintLibs(), aAdapter,
85  LIB_TREE::WIDGETS::ALL, details );
86 
87  m_hsplitter->SetSashGravity( 0.8 );
88  m_hsplitter->SetMinimumPaneSize( 20 );
89  m_hsplitter->SplitVertically( m_tree, ConstructRightPanel( m_hsplitter ) );
90 
91  m_dbl_click_timer = new wxTimer( this );
92 
93  auto buttonsSizer = new wxBoxSizer( wxHORIZONTAL );
94 
95  m_browser_button = new wxButton( this, wxID_ANY, _( "Select with Browser" ) );
96  buttonsSizer->Add( m_browser_button, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5 );
97 
98  auto sdbSizer = new wxStdDialogButtonSizer();
99  auto okButton = new wxButton( this, wxID_OK );
100  auto cancelButton = new wxButton( this, wxID_CANCEL );
101  sdbSizer->AddButton( okButton );
102  sdbSizer->AddButton( cancelButton );
103  sdbSizer->Realize();
104 
105  buttonsSizer->Add( sdbSizer, 1, wxALL, 5 );
106 
107  sizer->Add( buttonsSizer, 0, wxEXPAND | wxLEFT, 5 );
108  SetSizer( sizer );
109 
110  aAdapter->FinishTreeInitialization();
111 
112  Bind( wxEVT_TIMER, &DIALOG_CHOOSE_FOOTPRINT::OnCloseTimer, this, m_dbl_click_timer->GetId() );
113  Bind( SYMBOL_PRESELECTED, &DIALOG_CHOOSE_FOOTPRINT::OnComponentPreselected, this );
114  Bind( SYMBOL_SELECTED, &DIALOG_CHOOSE_FOOTPRINT::OnComponentSelected, this );
115  m_browser_button->Bind( wxEVT_COMMAND_BUTTON_CLICKED, &DIALOG_CHOOSE_FOOTPRINT::OnUseBrowser,
116  this );
117 
118  Layout();
119 
120  auto cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>();
121 
122  // We specify the width of the right window (m_symbol_view_panel), because specify
123  // the width of the left window does not work as expected when SetSashGravity() is called
124  if( cfg->m_FootprintChooser.sash_h < 0 )
126 
127  m_hsplitter->SetSashPosition( cfg->m_FootprintChooser.sash_h );
128 
129  if( cfg->m_FootprintChooser.sash_v < 0 )
130  cfg->m_FootprintChooser.sash_v = horizPixelsFromDU( 230 );
131 
132  if( m_vsplitter )
133  m_vsplitter->SetSashPosition( cfg->m_FootprintChooser.sash_v );
134 
135  int w = cfg->m_FootprintChooser.width < 0 ?
136  horizPixelsFromDU( 440 ) : cfg->m_FootprintChooser.width;
137  int h = cfg->m_FootprintChooser.height < 0 ?
138  horizPixelsFromDU( 340 ) : cfg->m_FootprintChooser.height;
139  SetSize( wxSize( w, h ) );
140 
142  okButton->SetDefault();
143 }
144 
145 
147 {
148  Unbind( wxEVT_TIMER, &DIALOG_CHOOSE_FOOTPRINT::OnCloseTimer, this );
149  Unbind( SYMBOL_PRESELECTED, &DIALOG_CHOOSE_FOOTPRINT::OnComponentPreselected, this );
150  Unbind( SYMBOL_SELECTED, &DIALOG_CHOOSE_FOOTPRINT::OnComponentSelected, this );
151  m_browser_button->Unbind( wxEVT_COMMAND_BUTTON_CLICKED,
153 
154  // I am not sure the following two lines are necessary,
155  // but they will not hurt anyone
156  m_dbl_click_timer->Stop();
157  delete m_dbl_click_timer;
158 
159  auto cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>();
160 
161  cfg->m_FootprintChooser.width = GetSize().x;
162  cfg->m_FootprintChooser.height = GetSize().y;
163  cfg->m_FootprintChooser.sash_h = m_hsplitter->GetSashPosition();
164 
165  if( m_vsplitter )
166  cfg->m_FootprintChooser.sash_v = m_vsplitter->GetSashPosition();
167 }
168 
169 
170 wxPanel* DIALOG_CHOOSE_FOOTPRINT::ConstructRightPanel( wxWindow* aParent )
171 {
172  auto panel = new wxPanel( aParent );
173  auto sizer = new wxBoxSizer( wxVERTICAL );
174 
176  sizer->Add( m_preview_ctrl, 1, wxEXPAND | wxTOP | wxRIGHT, 5 );
177 
178  panel->SetSizer( sizer );
179  panel->Layout();
180  sizer->Fit( panel );
181 
182  return panel;
183 }
184 
185 
187 {
188  return m_tree->GetSelectedLibId();
189 }
190 
191 
192 void DIALOG_CHOOSE_FOOTPRINT::OnUseBrowser( wxCommandEvent& aEvent )
193 {
195  EndQuasiModal( wxID_OK );
196 }
197 
198 
199 void DIALOG_CHOOSE_FOOTPRINT::OnCloseTimer( wxTimerEvent& aEvent )
200 {
201  // Hack handler because of eaten MouseUp event. See
202  // DIALOG_CHOOSE_FOOTPRINT::OnComponentSelected for the beginning
203  // of this spaghetti noodle.
204 
205  auto state = wxGetMouseState();
206 
207  if( state.LeftIsDown() )
208  {
209  // Mouse hasn't been raised yet, so fire the timer again. Otherwise the
210  // purpose of this timer is defeated.
212  }
213  else
214  {
215  EndQuasiModal( wxID_OK );
216  }
217 }
218 
219 
221 {
223  return;
224 
225  LIB_ID lib_id = m_tree->GetSelectedLibId();
226 
227  if( !lib_id.IsValid() )
228  {
229  m_preview_ctrl->SetStatusText( _( "No footprint selected" ) );
230  }
231  else
232  {
234  m_preview_ctrl->DisplayFootprint( lib_id );
235  }
236 }
237 
238 
239 void DIALOG_CHOOSE_FOOTPRINT::OnComponentSelected( wxCommandEvent& aEvent )
240 {
241  if( m_tree->GetSelectedLibId().IsValid() )
242  {
243  // Got a selection. We can't just end the modal dialog here, because
244  // wx leaks some events back to the parent window (in particular, the
245  // MouseUp following a double click).
246  //
247  // NOW, here's where it gets really fun. wxTreeListCtrl eats MouseUp.
248  // This isn't really feasible to bypass without a fully custom
249  // wxDataViewCtrl implementation, and even then might not be fully
250  // possible (docs are vague). To get around this, we use a one-shot
251  // timer to schedule the dialog close.
252  //
253  // See DIALOG_CHOOSE_FOOTPRINT::OnCloseTimer for the other end of this
254  // spaghetti noodle.
256  }
257 }
static constexpr int DblClickDelay
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
Definition: kiway_holder.h:53
void OnCloseTimer(wxTimerEvent &aEvent)
wxWindow * GetFocusTarget()
Definition: lib_tree.cpp:240
int horizPixelsFromDU(int x) const
Convert an integer number of dialog units to pixels, horizontally.
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:82
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
All except INITIAL_ADD.
Definition: view_item.h:58
bool IsValid() const
Check if this LID_ID is valid.
Definition: lib_id.h:159
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
FOOTPRINT_PREVIEW_WIDGET * m_preview_ctrl
FOOTPRINT_CHOOSER m_FootprintChooser
void SetInitialFocus(wxWindow *aWindow)
Sets the window (usually a wxTextCtrl) that should be focused when the dialog is shown.
Definition: dialog_shim.h:97
Classes used in Pcbnew, CvPcb and GerbView.
void ClearStatus()
Clear the contents of the status label and hide it.
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
LIB_ID GetSelectedLibId(int *aUnit=nullptr) const
For multi-unit symbols, if the user selects the symbol itself rather than picking an individual unit,...
Definition: lib_tree.cpp:168
LIB_ID GetSelectedLibId() const
To be called after this dialog returns from ShowModal().
#define _(s)
void OnUseBrowser(wxCommandEvent &aEvent)
void SetStatusText(const wxString &aText)
Set the contents of the status label and display it.
void OnComponentPreselected(wxCommandEvent &aEvent)
DIALOG_CHOOSE_FOOTPRINT(PCB_BASE_FRAME *aParent, const wxString &aTitle, wxObjectDataPtr< LIB_TREE_MODEL_ADAPTER > &aAdapter)
Create dialog to choose component.
void EndQuasiModal(int retCode)
wxPanel * ConstructRightPanel(wxWindow *aParent)
see class PGM_BASE
void DisplayFootprint(const LIB_ID &aFPID)
Set the currently displayed footprint.
bool IsInitialized() const
Return whether the widget initialized properly.
void OnComponentSelected(wxCommandEvent &aEvent)
Handle the selection of an item.
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
Widget displaying a tree of symbols with optional search text control and description panel....
Definition: lib_tree.h:45