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.

455 lines
14 KiB

3 years ago
3 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2018 CERN
  5. * Copyright (C) 2021-2022 KiCad Developers, see AUTHORS.txt for contributors.
  6. * @author Jon Evans <jon@craftyjon.com>
  7. *
  8. * This program is free software: you can redistribute it and/or modify it
  9. * under the terms of the GNU General Public License as published by the
  10. * Free Software Foundation, either version 3 of the License, or (at your
  11. * option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #include <widgets/wx_grid.h>
  22. #include <widgets/std_bitmap_button.h>
  23. #include <confirm.h>
  24. #include <sch_edit_frame.h>
  25. #include <schematic.h>
  26. #include <dialogs/panel_setup_buses.h>
  27. #include "grid_tricks.h"
  28. PANEL_SETUP_BUSES::PANEL_SETUP_BUSES( wxWindow* aWindow, SCH_EDIT_FRAME* aFrame ) :
  29. PANEL_SETUP_BUSES_BASE( aWindow ),
  30. m_frame( aFrame ),
  31. m_lastAlias( 0 ),
  32. m_membersGridDirty( false ),
  33. m_errorGrid( nullptr ),
  34. m_errorRow( -1 )
  35. {
  36. m_membersLabelTemplate = m_membersLabel->GetLabel();
  37. m_addAlias->SetBitmap( KiBitmap( BITMAPS::small_plus ) );
  38. m_deleteAlias->SetBitmap( KiBitmap( BITMAPS::small_trash ) );
  39. m_addMember->SetBitmap( KiBitmap( BITMAPS::small_plus ) );
  40. m_removeMember->SetBitmap( KiBitmap( BITMAPS::small_trash ) );
  41. m_source->SetFont( KIUI::GetInfoFont( aWindow ) );
  42. m_aliasesGrid->PushEventHandler( new GRID_TRICKS( m_aliasesGrid,
  43. [this]( wxCommandEvent& aEvent )
  44. {
  45. OnAddAlias( aEvent );
  46. } ) );
  47. m_membersGrid->PushEventHandler( new GRID_TRICKS( m_membersGrid,
  48. [this]( wxCommandEvent& aEvent )
  49. {
  50. wxIdleEvent dummy;
  51. reloadMembersGridOnIdle( dummy );
  52. OnAddMember( aEvent );
  53. } ) );
  54. // wxFormBuilder doesn't include this event...
  55. m_aliasesGrid->Connect( wxEVT_GRID_CELL_CHANGING,
  56. wxGridEventHandler( PANEL_SETUP_BUSES::OnAliasesGridCellChanging ),
  57. nullptr, this );
  58. m_membersGrid->Connect( wxEVT_GRID_CELL_CHANGING,
  59. wxGridEventHandler( PANEL_SETUP_BUSES::OnMemberGridCellChanging ),
  60. nullptr, this );
  61. Layout();
  62. }
  63. PANEL_SETUP_BUSES::~PANEL_SETUP_BUSES()
  64. {
  65. // Delete the GRID_TRICKS.
  66. m_aliasesGrid->PopEventHandler( true );
  67. m_membersGrid->PopEventHandler( true );
  68. m_aliasesGrid->Disconnect( wxEVT_GRID_CELL_CHANGING,
  69. wxGridEventHandler( PANEL_SETUP_BUSES::OnAliasesGridCellChanging ),
  70. nullptr, this );
  71. m_membersGrid->Disconnect( wxEVT_GRID_CELL_CHANGING,
  72. wxGridEventHandler( PANEL_SETUP_BUSES::OnMemberGridCellChanging ),
  73. nullptr, this );
  74. }
  75. bool PANEL_SETUP_BUSES::TransferDataToWindow()
  76. {
  77. auto contains =
  78. [&]( const std::shared_ptr<BUS_ALIAS>& alias ) -> bool
  79. {
  80. wxString aName = alias->GetName();
  81. std::vector<wxString> aMembers = alias->Members();
  82. std::sort( aMembers.begin(), aMembers.end() );
  83. for( const std::shared_ptr<BUS_ALIAS>& candidate : m_aliases )
  84. {
  85. wxString bName = candidate->GetName();
  86. std::vector<wxString> bMembers = candidate->Members();
  87. std::sort( bMembers.begin(), bMembers.end() );
  88. if( aName == bName && aMembers == bMembers )
  89. return true;
  90. }
  91. return false;
  92. };
  93. SCH_SCREENS screens( m_frame->Schematic().Root() );
  94. // collect aliases from each open sheet
  95. for( SCH_SCREEN* screen = screens.GetFirst(); screen != nullptr; screen = screens.GetNext() )
  96. {
  97. for( const std::shared_ptr<BUS_ALIAS>& alias : screen->GetBusAliases() )
  98. {
  99. if( !contains( alias ) )
  100. m_aliases.push_back( alias->Clone() );
  101. }
  102. }
  103. int ii = 0;
  104. m_aliasesGrid->ClearRows();
  105. m_aliasesGrid->AppendRows( m_aliases.size() );
  106. for( const std::shared_ptr<BUS_ALIAS>& alias : m_aliases )
  107. m_aliasesGrid->SetCellValue( ii++, 0, alias->GetName() );
  108. m_membersBook->SetSelection( 1 );
  109. return true;
  110. }
  111. bool PANEL_SETUP_BUSES::TransferDataFromWindow()
  112. {
  113. if( !m_aliasesGrid->CommitPendingChanges() || !m_membersGrid->CommitPendingChanges() )
  114. return false;
  115. // Copy names back just in case they didn't get caught on the GridCellChanging event
  116. for( int ii = 0; ii < m_aliasesGrid->GetNumberRows(); ++ii )
  117. m_aliases[ii]->SetName( m_aliasesGrid->GetCellValue( ii, 0 ) );
  118. SCH_SCREENS screens( m_frame->Schematic().Root() );
  119. for( SCH_SCREEN* screen = screens.GetFirst(); screen != nullptr; screen = screens.GetNext() )
  120. screen->ClearBusAliases();
  121. for( const std::shared_ptr<BUS_ALIAS>& alias : m_aliases )
  122. alias->GetParent()->AddBusAlias( alias );
  123. return true;
  124. }
  125. void PANEL_SETUP_BUSES::OnAddAlias( wxCommandEvent& aEvent )
  126. {
  127. if( !m_aliasesGrid->CommitPendingChanges() || !m_membersGrid->CommitPendingChanges() )
  128. return;
  129. // New aliases get stored on the currently visible sheet
  130. m_aliases.push_back( std::make_shared<BUS_ALIAS>( m_frame->GetScreen() ) );
  131. int row = m_aliasesGrid->GetNumberRows();
  132. m_aliasesGrid->AppendRows();
  133. m_aliasesGrid->MakeCellVisible( row, 0 );
  134. m_aliasesGrid->SetGridCursor( row, 0 );
  135. m_aliasesGrid->EnableCellEditControl( true );
  136. m_aliasesGrid->ShowCellEditControl();
  137. }
  138. void PANEL_SETUP_BUSES::OnDeleteAlias( wxCommandEvent& aEvent )
  139. {
  140. if( !m_aliasesGrid->CommitPendingChanges() || !m_membersGrid->CommitPendingChanges() )
  141. return;
  142. int curRow = m_aliasesGrid->GetGridCursorRow();
  143. if( curRow < 0 )
  144. return;
  145. // Clear the members grid first so we don't try to write it back to a deleted alias
  146. m_membersGrid->ClearRows();
  147. m_lastAlias = -1;
  148. m_lastAliasName = wxEmptyString;
  149. m_aliases.erase( m_aliases.begin() + curRow );
  150. m_aliasesGrid->DeleteRows( curRow, 1 );
  151. if( m_aliasesGrid->GetNumberRows() > 0 )
  152. {
  153. m_aliasesGrid->MakeCellVisible( std::max( 0, curRow-1 ), 0 );
  154. m_aliasesGrid->SetGridCursor( std::max( 0, curRow-1 ), 0 );
  155. }
  156. }
  157. void PANEL_SETUP_BUSES::OnAddMember( wxCommandEvent& aEvent )
  158. {
  159. if( !m_membersGrid->CommitPendingChanges() )
  160. return;
  161. int row = m_membersGrid->GetNumberRows();
  162. m_membersGrid->AppendRows();
  163. m_membersGrid->MakeCellVisible( row, 0 );
  164. m_membersGrid->SetGridCursor( row, 0 );
  165. m_membersGrid->EnableCellEditControl( true );
  166. m_membersGrid->ShowCellEditControl();
  167. }
  168. void PANEL_SETUP_BUSES::OnRemoveMember( wxCommandEvent& aEvent )
  169. {
  170. if( !m_membersGrid->CommitPendingChanges() )
  171. return;
  172. int curRow = m_membersGrid->GetGridCursorRow();
  173. if( curRow < 0 )
  174. return;
  175. m_membersGrid->DeleteRows( curRow, 1 );
  176. // Update the member list of the current bus alias from the members grid
  177. const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[ m_lastAlias ];
  178. alias->Members().clear();
  179. for( int ii = 0; ii < m_membersGrid->GetNumberRows(); ++ii )
  180. alias->Members().push_back( m_membersGrid->GetCellValue( ii, 0 ) );
  181. m_membersGridDirty = true;
  182. if( m_membersGrid->GetNumberRows() > 0 )
  183. {
  184. m_membersGrid->MakeCellVisible( std::max( 0, curRow-1 ), 0 );
  185. m_membersGrid->SetGridCursor( std::max( 0, curRow-1 ), 0 );
  186. }
  187. }
  188. void PANEL_SETUP_BUSES::OnAliasesGridCellChanging( wxGridEvent& event )
  189. {
  190. int row = event.GetRow();
  191. if( row >= 0 )
  192. {
  193. wxString name = event.GetString();
  194. for( int ii = 0; ii < m_aliasesGrid->GetNumberRows(); ++ii )
  195. {
  196. if( ii == event.GetRow() )
  197. continue;
  198. if( name == m_aliasesGrid->GetCellValue( ii, 0 )
  199. && m_aliases[ row ]->GetParent() == m_aliases[ ii ]->GetParent() )
  200. {
  201. m_errorMsg = wxString::Format( _( "Alias name '%s' already in use." ), name );
  202. m_errorGrid = m_aliasesGrid;
  203. m_errorRow = row;
  204. event.Veto();
  205. return;
  206. }
  207. }
  208. m_aliases[ row ]->SetName( name );
  209. }
  210. }
  211. void PANEL_SETUP_BUSES::OnMemberGridCellChanging( wxGridEvent& event )
  212. {
  213. int row = event.GetRow();
  214. if( row >= 0 )
  215. {
  216. wxString name = event.GetString();
  217. if( name.IsEmpty() )
  218. {
  219. m_errorMsg = _( "Member net/alias name cannot be empty." );
  220. m_errorGrid = m_membersGrid;
  221. m_errorRow = event.GetRow();
  222. event.Veto();
  223. return;
  224. }
  225. const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[ m_lastAlias ];
  226. alias->Members().clear();
  227. for( int ii = 0; ii < m_membersGrid->GetNumberRows(); ++ii )
  228. {
  229. if( ii == row )
  230. {
  231. // Parse a space-separated list and add each one
  232. wxStringTokenizer tok( name, " " );
  233. while( tok.HasMoreTokens() )
  234. alias->Members().push_back( tok.GetNextToken() );
  235. }
  236. else
  237. {
  238. alias->Members().push_back( m_membersGrid->GetCellValue( ii, 0 ) );
  239. }
  240. }
  241. m_membersGridDirty = true;
  242. Bind( wxEVT_IDLE, &PANEL_SETUP_BUSES::reloadMembersGridOnIdle, this );
  243. }
  244. }
  245. void PANEL_SETUP_BUSES::doReloadMembersGrid()
  246. {
  247. if( m_lastAlias >= 0 && m_lastAlias < m_aliasesGrid->GetNumberRows() )
  248. {
  249. const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[ m_lastAlias ];
  250. wxString source;
  251. wxString membersLabel;
  252. if( alias->GetParent() )
  253. {
  254. wxFileName sheet_name( alias->GetParent()->GetFileName() );
  255. source.Printf( wxS( "(" ) + sheet_name.GetFullName() + wxS( ")" ) );
  256. }
  257. membersLabel.Printf( m_membersLabelTemplate, m_lastAliasName );
  258. m_source->SetLabel( source );
  259. m_membersLabel->SetLabel( membersLabel );
  260. m_membersGrid->ClearRows();
  261. m_membersGrid->AppendRows( alias->Members().size() );
  262. int ii = 0;
  263. for( const wxString& member : alias->Members() )
  264. m_membersGrid->SetCellValue( ii++, 0, member );
  265. }
  266. m_membersGridDirty = false;
  267. }
  268. void PANEL_SETUP_BUSES::reloadMembersGridOnIdle( wxIdleEvent& aEvent )
  269. {
  270. if( m_membersGridDirty )
  271. doReloadMembersGrid();
  272. Unbind( wxEVT_IDLE, &PANEL_SETUP_BUSES::reloadMembersGridOnIdle, this );
  273. }
  274. void PANEL_SETUP_BUSES::OnSizeGrid( wxSizeEvent& event )
  275. {
  276. auto setColSize =
  277. []( WX_GRID* grid )
  278. {
  279. int colSize = std::max( grid->GetClientSize().x, grid->GetVisibleWidth( 0 ) );
  280. if( grid->GetColSize( 0 ) != colSize )
  281. grid->SetColSize( 0, colSize );
  282. };
  283. setColSize( m_aliasesGrid );
  284. setColSize( m_membersGrid );
  285. // Always propagate for a grid repaint (needed if the height changes, as well as width)
  286. event.Skip();
  287. }
  288. void PANEL_SETUP_BUSES::OnUpdateUI( wxUpdateUIEvent& event )
  289. {
  290. // Handle a grid error. This is delayed to OnUpdateUI so that we can change focus
  291. // even when the original validation was triggered from a killFocus event.
  292. if( !m_errorMsg.IsEmpty() )
  293. {
  294. // We will re-enter this routine when the error dialog is displayed, so make
  295. // sure we don't keep putting up more dialogs.
  296. wxString errorMsg = m_errorMsg;
  297. m_errorMsg = wxEmptyString;
  298. DisplayErrorMessage( this, errorMsg );
  299. m_errorGrid->SetFocus();
  300. m_errorGrid->MakeCellVisible( m_errorRow, 0 );
  301. m_errorGrid->SetGridCursor( m_errorRow, 0 );
  302. m_errorGrid->EnableCellEditControl( true );
  303. m_errorGrid->ShowCellEditControl();
  304. return;
  305. }
  306. if( !m_membersGrid->IsCellEditControlShown() )
  307. {
  308. int row = -1;
  309. wxString aliasName;
  310. if( m_aliasesGrid->IsCellEditControlShown() )
  311. {
  312. row = m_aliasesGrid->GetGridCursorRow();
  313. wxGridCellEditor* cellEditor = m_aliasesGrid->GetCellEditor( row, 0 );
  314. if( wxTextEntry* txt = dynamic_cast<wxTextEntry*>( cellEditor->GetControl() ) )
  315. aliasName = txt->GetValue();
  316. cellEditor->DecRef();
  317. }
  318. else if( m_aliasesGrid->GetGridCursorRow() >= 0 )
  319. {
  320. row = m_aliasesGrid->GetGridCursorRow();
  321. aliasName = m_aliasesGrid->GetCellValue( row, 0 );
  322. }
  323. else if( m_lastAlias >= 0 && m_lastAlias < m_aliasesGrid->GetNumberRows() )
  324. {
  325. row = m_lastAlias;
  326. aliasName = m_lastAliasName;
  327. }
  328. if( row < 0 )
  329. {
  330. m_membersBook->SetSelection( 1 );
  331. }
  332. else if( row != m_lastAlias || aliasName != m_lastAliasName )
  333. {
  334. m_lastAlias = row;
  335. m_lastAliasName = aliasName;
  336. m_membersBook->SetSelection( 0 );
  337. m_membersBook->GetPage( 0 )->Layout();
  338. const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[ row ];
  339. alias->SetName( aliasName );
  340. m_membersGridDirty = true;
  341. Bind( wxEVT_IDLE, &PANEL_SETUP_BUSES::reloadMembersGridOnIdle, this );
  342. }
  343. }
  344. }