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.

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