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.

270 lines
8.9 KiB

  1. /*
  2. * This program source code file is part of KICAD, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2020-2024 KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #include <panel_text_variables.h>
  24. #include <algorithm>
  25. #include <bitmaps.h>
  26. #include <confirm.h>
  27. #include <validators.h>
  28. #include <project.h>
  29. #include <grid_tricks.h>
  30. #include <widgets/std_bitmap_button.h>
  31. #include <widgets/grid_text_helpers.h>
  32. #include <widgets/wx_grid_autosizer.h>
  33. enum TEXT_VAR_GRID_COLUMNS
  34. {
  35. TV_NAME_COL = 0,
  36. TV_VALUE_COL
  37. };
  38. PANEL_TEXT_VARIABLES::PANEL_TEXT_VARIABLES( wxWindow* aParent, PROJECT* aProject ) :
  39. PANEL_TEXT_VARIABLES_BASE( aParent ), m_project( aProject ), m_lastCheckedTicker( 0 ),
  40. m_errorRow( -1 ), m_errorCol( -1 )
  41. {
  42. m_btnAddTextVar->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
  43. m_btnDeleteTextVar->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
  44. m_TextVars->ClearRows();
  45. m_TextVars->SetUseNativeColLabels();
  46. // prohibit these characters in the alias names: []{}()%~<>"='`;:.,&?/\|$
  47. m_nameValidator.SetStyle( wxFILTER_EXCLUDE_CHAR_LIST );
  48. m_nameValidator.SetCharExcludes( wxT( "{}[]()%~<>\"='`;:.,&?/\\|$" ) );
  49. m_TextVars->PushEventHandler( new GRID_TRICKS( m_TextVars, [this]( wxCommandEvent& aEvent )
  50. {
  51. OnAddTextVar( aEvent );
  52. } ) );
  53. m_TextVars->SetSelectionMode( wxGrid::wxGridSelectionModes::wxGridSelectRows );
  54. // wxFormBuilder doesn't include this event...
  55. m_TextVars->Connect( wxEVT_GRID_CELL_CHANGING,
  56. wxGridEventHandler( PANEL_TEXT_VARIABLES::OnGridCellChanging ),
  57. nullptr, this );
  58. Bind( wxEVT_IDLE,
  59. [this]( wxIdleEvent& aEvent )
  60. {
  61. // Careful of consuming CPU in an idle event handler. Check the ticker first to
  62. // see if there's even a possibility of the text variables having changed.
  63. if( m_project->GetTextVarsTicker() > m_lastCheckedTicker )
  64. {
  65. wxWindow* dialog = wxGetTopLevelParent( this );
  66. wxWindow* topLevelFocus = wxGetTopLevelParent( wxWindow::FindFocus() );
  67. if( topLevelFocus == dialog && m_lastLoaded != m_project->GetTextVars() )
  68. checkReload();
  69. }
  70. } );
  71. m_autoSizer = std::make_unique<WX_GRID_AUTOSIZER>( *m_TextVars,
  72. WX_GRID_AUTOSIZER::COL_MIN_WIDTHS{
  73. { TV_NAME_COL, 72 },
  74. { TV_VALUE_COL, 120 },
  75. },
  76. TV_VALUE_COL );
  77. }
  78. PANEL_TEXT_VARIABLES::~PANEL_TEXT_VARIABLES()
  79. {
  80. // Delete the GRID_TRICKS.
  81. m_TextVars->PopEventHandler( true );
  82. m_TextVars->Disconnect( wxEVT_GRID_CELL_CHANGING,
  83. wxGridEventHandler( PANEL_TEXT_VARIABLES::OnGridCellChanging ),
  84. nullptr, this );
  85. }
  86. void PANEL_TEXT_VARIABLES::checkReload()
  87. {
  88. // MUST update the ticker before calling IsOK (or we'll end up re-entering through the idle
  89. // event until we crash the stack).
  90. m_lastCheckedTicker = m_project->GetTextVarsTicker();
  91. if( IsOK( m_parent, _( "The text variables have been changed outside the Setup dialog.\n"
  92. "Do you wish to reload them?" ) ) )
  93. {
  94. m_TextVars->ClearRows();
  95. m_lastLoaded = m_project->GetTextVars();
  96. for( const auto& var : m_lastLoaded )
  97. AppendTextVar( var.first, var.second );
  98. }
  99. }
  100. bool PANEL_TEXT_VARIABLES::TransferDataToWindow()
  101. {
  102. m_lastLoaded = m_project->GetTextVars();
  103. m_lastCheckedTicker = m_project->GetTextVarsTicker();
  104. for( const auto& var : m_lastLoaded )
  105. AppendTextVar( var.first, var.second );
  106. return true;
  107. }
  108. void PANEL_TEXT_VARIABLES::AppendTextVar( const wxString& aName, const wxString& aValue )
  109. {
  110. int i = m_TextVars->GetNumberRows();
  111. m_TextVars->AppendRows( 1 );
  112. m_TextVars->SetCellValue( i, TV_NAME_COL, aName );
  113. wxGridCellAttr* nameCellAttr = m_TextVars->GetOrCreateCellAttr( i, TV_NAME_COL );
  114. wxGridCellTextEditor* nameTextEditor = new GRID_CELL_TEXT_EDITOR();
  115. nameTextEditor->SetValidator( m_nameValidator );
  116. nameCellAttr->SetEditor( nameTextEditor );
  117. nameCellAttr->DecRef();
  118. m_TextVars->SetCellValue( i, TV_VALUE_COL, aValue );
  119. }
  120. bool PANEL_TEXT_VARIABLES::TransferDataFromWindow()
  121. {
  122. if( !m_TextVars->CommitPendingChanges() )
  123. return false;
  124. for( int row = 0; row < m_TextVars->GetNumberRows(); ++row )
  125. {
  126. if( m_TextVars->GetCellValue( row, TV_NAME_COL ).IsEmpty() )
  127. {
  128. m_errorRow = row;
  129. m_errorCol = TV_NAME_COL;
  130. m_errorMsg = _( "Variable name cannot be empty." );
  131. return false;
  132. }
  133. }
  134. std::map<wxString, wxString>& variables = m_project->GetTextVars();
  135. variables.clear();
  136. for( int row = 0; row < m_TextVars->GetNumberRows(); ++row )
  137. {
  138. wxString name = m_TextVars->GetCellValue( row, TV_NAME_COL );
  139. wxString value = m_TextVars->GetCellValue( row, TV_VALUE_COL );
  140. variables[ name ] = value;
  141. }
  142. return true;
  143. }
  144. void PANEL_TEXT_VARIABLES::OnGridCellChanging( wxGridEvent& event )
  145. {
  146. int row = event.GetRow();
  147. int col = event.GetCol();
  148. wxString text = event.GetString();
  149. if( text.IsEmpty() && col == TV_NAME_COL )
  150. {
  151. m_errorMsg = _( "Variable name cannot be empty." );
  152. m_errorRow = row;
  153. m_errorCol = col;
  154. event.Veto();
  155. }
  156. }
  157. void PANEL_TEXT_VARIABLES::OnAddTextVar( wxCommandEvent& event )
  158. {
  159. if( !m_TextVars->CommitPendingChanges() )
  160. return;
  161. AppendTextVar( wxEmptyString, wxEmptyString );
  162. m_TextVars->MakeCellVisible( m_TextVars->GetNumberRows() - 1, TV_NAME_COL );
  163. m_TextVars->SetGridCursor( m_TextVars->GetNumberRows() - 1, TV_NAME_COL );
  164. m_TextVars->EnableCellEditControl( true );
  165. m_TextVars->ShowCellEditControl();
  166. }
  167. void PANEL_TEXT_VARIABLES::OnRemoveTextVar( wxCommandEvent& event )
  168. {
  169. int curRow = m_TextVars->GetGridCursorRow();
  170. if( curRow < 0 || m_TextVars->GetNumberRows() <= curRow )
  171. return;
  172. m_TextVars->CommitPendingChanges( true /* silent mode; we don't care if it's valid */ );
  173. m_TextVars->DeleteRows( curRow, 1 );
  174. m_TextVars->MakeCellVisible( std::max( 0, curRow-1 ), m_TextVars->GetGridCursorCol() );
  175. m_TextVars->SetGridCursor( std::max( 0, curRow-1 ), m_TextVars->GetGridCursorCol() );
  176. }
  177. void PANEL_TEXT_VARIABLES::OnUpdateUI( wxUpdateUIEvent& event )
  178. {
  179. // Handle a grid error. This is delayed to OnUpdateUI so that we can change focus
  180. // even when the original validation was triggered from a killFocus event (and for
  181. // dialog with notebooks, so that the corresponding notebook page can be shown in
  182. // the background when triggered from an OK).
  183. if( !m_errorMsg.IsEmpty() )
  184. {
  185. // We will re-enter this routine when the error dialog is displayed, so make
  186. // sure we don't keep putting up more dialogs.
  187. wxString errorMsg = m_errorMsg;
  188. m_errorMsg = wxEmptyString;
  189. wxWindow* topLevelParent = wxGetTopLevelParent( this );
  190. DisplayErrorMessage( topLevelParent, errorMsg );
  191. m_TextVars->SetFocus();
  192. m_TextVars->MakeCellVisible( m_errorRow, m_errorCol );
  193. m_TextVars->SetGridCursor( m_errorRow, m_errorCol );
  194. m_TextVars->EnableCellEditControl( true );
  195. m_TextVars->ShowCellEditControl();
  196. }
  197. }
  198. void PANEL_TEXT_VARIABLES::ImportSettingsFrom( const PROJECT* aOtherProject )
  199. {
  200. // Fetch from other project...
  201. m_lastLoaded = aOtherProject->GetTextVars();
  202. // ... but use ticker from current project:
  203. m_lastCheckedTicker = m_project->GetTextVarsTicker();
  204. m_TextVars->ClearRows();
  205. for( const auto& var : m_lastLoaded )
  206. AppendTextVar( var.first, var.second );
  207. }