You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

258 lines
9.1 KiB

  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. #include <dialog_choose_footprint.h>
  25. #include <algorithm>
  26. #include <wx/utils.h>
  27. #include <wx/button.h>
  28. #include <wx/panel.h>
  29. #include <wx/sizer.h>
  30. #include <wx/splitter.h>
  31. #include <wx/timer.h>
  32. #include <wx/wxhtml.h>
  33. #include <pcb_base_frame.h>
  34. #include <pcbnew_settings.h>
  35. #include <pgm_base.h>
  36. #include <fp_lib_table.h>
  37. #include <settings/settings_manager.h>
  38. #include <widgets/lib_tree.h>
  39. #include <widgets/footprint_preview_widget.h>
  40. #include <widgets/footprint_select_widget.h>
  41. #include <kiface_base.h>
  42. DIALOG_CHOOSE_FOOTPRINT::DIALOG_CHOOSE_FOOTPRINT( PCB_BASE_FRAME* aParent,
  43. const wxString& aTitle,
  44. wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>& aAdapter )
  45. : DIALOG_SHIM( aParent, wxID_ANY, aTitle, wxDefaultPosition, wxDefaultSize,
  46. wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
  47. m_browser_button( nullptr ),
  48. m_hsplitter( nullptr ),
  49. m_vsplitter( nullptr ),
  50. m_parent( aParent ),
  51. m_external_browser_requested( false )
  52. {
  53. auto sizer = new wxBoxSizer( wxVERTICAL );
  54. HTML_WINDOW* details = nullptr;
  55. m_vsplitter = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
  56. wxSP_LIVE_UPDATE );
  57. m_hsplitter = new wxSplitterWindow( m_vsplitter, wxID_ANY, wxDefaultPosition, wxDefaultSize,
  58. wxSP_LIVE_UPDATE );
  59. //Avoid the splitter window being assigned as the Parent to additional windows
  60. m_hsplitter->SetExtraStyle( wxWS_EX_TRANSIENT );
  61. auto detailsPanel = new wxPanel( m_vsplitter );
  62. auto detailsSizer = new wxBoxSizer( wxVERTICAL );
  63. detailsPanel->SetSizer( detailsSizer );
  64. details = new HTML_WINDOW( detailsPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize,
  65. wxHW_SCROLLBAR_AUTO );
  66. detailsSizer->Add( details, 1, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 5 );
  67. detailsPanel->Layout();
  68. detailsSizer->Fit( detailsPanel );
  69. m_vsplitter->SetSashGravity( 0.5 );
  70. m_vsplitter->SetMinimumPaneSize( 20 );
  71. m_vsplitter->SplitHorizontally( m_hsplitter, detailsPanel );
  72. sizer->Add( m_vsplitter, 1, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 5 );
  73. m_tree = new LIB_TREE( m_hsplitter, Prj().PcbFootprintLibs(), aAdapter,
  74. LIB_TREE::FLAGS::ALL_WIDGETS, details );
  75. m_hsplitter->SetSashGravity( 0.8 );
  76. m_hsplitter->SetMinimumPaneSize( 20 );
  77. m_hsplitter->SplitVertically( m_tree, ConstructRightPanel( m_hsplitter ) );
  78. m_dbl_click_timer = new wxTimer( this );
  79. auto buttonsSizer = new wxBoxSizer( wxHORIZONTAL );
  80. m_browser_button = new wxButton( this, wxID_ANY, _( "Select with Browser" ) );
  81. buttonsSizer->Add( m_browser_button, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5 );
  82. auto sdbSizer = new wxStdDialogButtonSizer();
  83. auto okButton = new wxButton( this, wxID_OK );
  84. auto cancelButton = new wxButton( this, wxID_CANCEL );
  85. sdbSizer->AddButton( okButton );
  86. sdbSizer->AddButton( cancelButton );
  87. sdbSizer->Realize();
  88. buttonsSizer->Add( sdbSizer, 1, wxALL, 5 );
  89. sizer->Add( buttonsSizer, 0, wxEXPAND | wxLEFT, 5 );
  90. SetSizer( sizer );
  91. aAdapter->FinishTreeInitialization();
  92. SetupStandardButtons();
  93. Bind( wxEVT_TIMER, &DIALOG_CHOOSE_FOOTPRINT::OnCloseTimer, this, m_dbl_click_timer->GetId() );
  94. Bind( SYMBOL_PRESELECTED, &DIALOG_CHOOSE_FOOTPRINT::OnComponentPreselected, this );
  95. Bind( SYMBOL_SELECTED, &DIALOG_CHOOSE_FOOTPRINT::OnComponentSelected, this );
  96. m_browser_button->Bind( wxEVT_COMMAND_BUTTON_CLICKED, &DIALOG_CHOOSE_FOOTPRINT::OnUseBrowser,
  97. this );
  98. Layout();
  99. auto cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>();
  100. // We specify the width of the right window (m_symbol_view_panel), because specify
  101. // the width of the left window does not work as expected when SetSashGravity() is called
  102. if( cfg->m_FootprintChooser.sash_h < 0 )
  103. cfg->m_FootprintChooser.sash_h = horizPixelsFromDU( 220 );
  104. m_hsplitter->SetSashPosition( cfg->m_FootprintChooser.sash_h );
  105. if( cfg->m_FootprintChooser.sash_v < 0 )
  106. cfg->m_FootprintChooser.sash_v = horizPixelsFromDU( 230 );
  107. if( m_vsplitter )
  108. m_vsplitter->SetSashPosition( cfg->m_FootprintChooser.sash_v );
  109. int w = cfg->m_FootprintChooser.width < 0 ?
  110. horizPixelsFromDU( 440 ) : cfg->m_FootprintChooser.width;
  111. int h = cfg->m_FootprintChooser.height < 0 ?
  112. horizPixelsFromDU( 340 ) : cfg->m_FootprintChooser.height;
  113. SetSize( wxSize( w, h ) );
  114. SetInitialFocus( m_tree->GetFocusTarget() );
  115. }
  116. DIALOG_CHOOSE_FOOTPRINT::~DIALOG_CHOOSE_FOOTPRINT()
  117. {
  118. Unbind( wxEVT_TIMER, &DIALOG_CHOOSE_FOOTPRINT::OnCloseTimer, this );
  119. Unbind( SYMBOL_PRESELECTED, &DIALOG_CHOOSE_FOOTPRINT::OnComponentPreselected, this );
  120. Unbind( SYMBOL_SELECTED, &DIALOG_CHOOSE_FOOTPRINT::OnComponentSelected, this );
  121. m_browser_button->Unbind( wxEVT_COMMAND_BUTTON_CLICKED,
  122. &DIALOG_CHOOSE_FOOTPRINT::OnUseBrowser, this );
  123. // I am not sure the following two lines are necessary,
  124. // but they will not hurt anyone
  125. m_dbl_click_timer->Stop();
  126. delete m_dbl_click_timer;
  127. auto cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>();
  128. cfg->m_FootprintChooser.width = GetSize().x;
  129. cfg->m_FootprintChooser.height = GetSize().y;
  130. cfg->m_FootprintChooser.sash_h = m_hsplitter->GetSashPosition();
  131. if( m_vsplitter )
  132. cfg->m_FootprintChooser.sash_v = m_vsplitter->GetSashPosition();
  133. }
  134. wxPanel* DIALOG_CHOOSE_FOOTPRINT::ConstructRightPanel( wxWindow* aParent )
  135. {
  136. auto panel = new wxPanel( aParent );
  137. auto sizer = new wxBoxSizer( wxVERTICAL );
  138. m_preview_ctrl = new FOOTPRINT_PREVIEW_WIDGET( panel, Kiway() );
  139. sizer->Add( m_preview_ctrl, 1, wxEXPAND | wxTOP | wxRIGHT, 5 );
  140. panel->SetSizer( sizer );
  141. panel->Layout();
  142. sizer->Fit( panel );
  143. return panel;
  144. }
  145. LIB_ID DIALOG_CHOOSE_FOOTPRINT::GetSelectedLibId() const
  146. {
  147. return m_tree->GetSelectedLibId();
  148. }
  149. void DIALOG_CHOOSE_FOOTPRINT::OnUseBrowser( wxCommandEvent& aEvent )
  150. {
  151. m_external_browser_requested = true;
  152. EndQuasiModal( wxID_OK );
  153. }
  154. void DIALOG_CHOOSE_FOOTPRINT::OnCloseTimer( wxTimerEvent& aEvent )
  155. {
  156. // Hack handler because of eaten MouseUp event. See
  157. // DIALOG_CHOOSE_FOOTPRINT::OnComponentSelected for the beginning
  158. // of this spaghetti noodle.
  159. auto state = wxGetMouseState();
  160. if( state.LeftIsDown() )
  161. {
  162. // Mouse hasn't been raised yet, so fire the timer again. Otherwise the
  163. // purpose of this timer is defeated.
  164. m_dbl_click_timer->StartOnce( DIALOG_CHOOSE_FOOTPRINT::DblClickDelay );
  165. }
  166. else
  167. {
  168. EndQuasiModal( wxID_OK );
  169. }
  170. }
  171. void DIALOG_CHOOSE_FOOTPRINT::OnComponentPreselected( wxCommandEvent& aEvent )
  172. {
  173. if( !m_preview_ctrl || !m_preview_ctrl->IsInitialized() )
  174. return;
  175. LIB_ID lib_id = m_tree->GetSelectedLibId();
  176. if( !lib_id.IsValid() )
  177. {
  178. m_preview_ctrl->SetStatusText( _( "No footprint selected" ) );
  179. }
  180. else
  181. {
  182. m_preview_ctrl->ClearStatus();
  183. m_preview_ctrl->DisplayFootprint( lib_id );
  184. }
  185. }
  186. void DIALOG_CHOOSE_FOOTPRINT::OnComponentSelected( wxCommandEvent& aEvent )
  187. {
  188. if( m_tree->GetSelectedLibId().IsValid() )
  189. {
  190. // Got a selection. We can't just end the modal dialog here, because
  191. // wx leaks some events back to the parent window (in particular, the
  192. // MouseUp following a double click).
  193. //
  194. // NOW, here's where it gets really fun. wxTreeListCtrl eats MouseUp.
  195. // This isn't really feasible to bypass without a fully custom
  196. // wxDataViewCtrl implementation, and even then might not be fully
  197. // possible (docs are vague). To get around this, we use a one-shot
  198. // timer to schedule the dialog close.
  199. //
  200. // See DIALOG_CHOOSE_FOOTPRINT::OnCloseTimer for the other end of this
  201. // spaghetti noodle.
  202. m_dbl_click_timer->StartOnce( DIALOG_CHOOSE_FOOTPRINT::DblClickDelay );
  203. }
  204. }