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.

279 lines
8.8 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2017 CERN
  5. * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * @author Maciej Suminski <maciej.suminski@cern.ch>
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 3
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, you may find one here:
  21. * https://www.gnu.org/licenses/gpl-3.0.html
  22. * or you may search the http://www.gnu.org website for the version 3 license,
  23. * or you may write to the Free Software Foundation, Inc.,
  24. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  25. */
  26. #include "dialog_update_fields.h"
  27. #include <sch_edit_frame.h>
  28. #include <sch_component.h>
  29. #include <schematic.h>
  30. #include <class_libentry.h>
  31. #include <algorithm>
  32. int InvokeDialogUpdateFields( SCH_EDIT_FRAME* aCaller, SCH_COMPONENT* aSpecificComponent,
  33. bool aCreateUndoEntry )
  34. {
  35. DIALOG_UPDATE_FIELDS dlg( aCaller, aSpecificComponent, aCreateUndoEntry );
  36. return dlg.ShowQuasiModal();
  37. }
  38. DIALOG_UPDATE_FIELDS::DIALOG_UPDATE_FIELDS( SCH_EDIT_FRAME* aParent,
  39. SCH_COMPONENT* aSpecificComponent,
  40. bool aCreateUndoEntry ) :
  41. DIALOG_UPDATE_FIELDS_BASE( aParent ),
  42. m_frame( aParent ),
  43. m_createUndo( aCreateUndoEntry )
  44. {
  45. if( aSpecificComponent )
  46. {
  47. m_components.emplace_back( aParent->GetScreen(), aSpecificComponent );
  48. }
  49. else
  50. {
  51. for( SCH_SHEET_PATH& path : aParent->Schematic().GetSheets() )
  52. {
  53. SCH_SCREEN* screen = path.LastScreen();
  54. for( SCH_ITEM* item : screen->Items().OfType( SCH_COMPONENT_T ) )
  55. m_components.emplace_back( screen, static_cast<SCH_COMPONENT*>( item ) );
  56. }
  57. }
  58. m_sdbSizerOK->SetDefault();
  59. }
  60. bool DIALOG_UPDATE_FIELDS::TransferDataFromWindow()
  61. {
  62. if( !wxDialog::TransferDataFromWindow() )
  63. return false;
  64. if( m_components.empty() )
  65. return true; // nothing to process
  66. // Create the set of fields to be updated
  67. m_updateFields.clear();
  68. for( unsigned i = 0; i < m_fieldsBox->GetCount(); ++i )
  69. {
  70. if( m_fieldsBox->IsChecked( i ) )
  71. m_updateFields.insert( m_fieldsBox->GetString( i ) );
  72. }
  73. // Undo buffer entry
  74. if( m_createUndo )
  75. {
  76. PICKED_ITEMS_LIST itemsList;
  77. for( std::pair<SCH_SCREEN*, SCH_COMPONENT*>& component : m_components )
  78. itemsList.PushItem( ITEM_PICKER( component.first, component.second, UNDO_REDO::CHANGED ) );
  79. m_frame->SaveCopyInUndoList( itemsList, UNDO_REDO::CHANGED, true );
  80. }
  81. // Do it!
  82. for( std::pair<SCH_SCREEN*, SCH_COMPONENT*>& component : m_components )
  83. updateFields( component.second );
  84. m_frame->SyncView();
  85. m_frame->GetCanvas()->Refresh();
  86. m_frame->OnModify();
  87. return true;
  88. }
  89. bool DIALOG_UPDATE_FIELDS::TransferDataToWindow()
  90. {
  91. if( !wxDialog::TransferDataToWindow() || !m_components.size() )
  92. return false;
  93. // Collect all user field names from library parts of components that are going to be updated
  94. {
  95. for( std::pair<SCH_SCREEN*, SCH_COMPONENT*>& component : m_components )
  96. {
  97. const std::unique_ptr< LIB_PART >& part = component.second->GetPartRef();
  98. if( !part )
  99. continue;
  100. const auto& drawItems = part->GetDrawItems();
  101. for( auto it = drawItems.begin( LIB_FIELD_T ); it != drawItems.end( LIB_FIELD_T ); ++it )
  102. {
  103. const LIB_FIELD* field = static_cast<const LIB_FIELD*>( &( *it ) );
  104. if( field->GetId() >= MANDATORY_FIELDS )
  105. m_updateFields.insert( field->GetName() );
  106. }
  107. }
  108. }
  109. // Update the listbox widget
  110. m_fieldsBox->Clear();
  111. for( int i = 0; i < MANDATORY_FIELDS; ++i )
  112. {
  113. m_fieldsBox->Append( m_components.front().second->GetField( i )->GetName() );
  114. if( i != REFERENCE && i != VALUE )
  115. m_fieldsBox->Check( i, true );
  116. }
  117. for( const wxString& fieldName : m_updateFields )
  118. {
  119. int idx = m_fieldsBox->Append( fieldName );
  120. m_fieldsBox->Check( idx, true );
  121. }
  122. // Now all widgets have the size fixed, call FinishDialogSettings
  123. FinishDialogSettings();
  124. return true;
  125. }
  126. void DIALOG_UPDATE_FIELDS::updateFields( SCH_COMPONENT* aComponent )
  127. {
  128. SCH_FIELDS newFields;
  129. std::unique_ptr< LIB_PART >& libPart = aComponent->GetPartRef();
  130. if( !libPart ) // the symbol is not found in lib: cannot update fields
  131. return;
  132. LIB_PART* alias = m_frame->GetLibPart( aComponent->GetLibId() );
  133. for( const SCH_FIELD& existingField : aComponent->GetFields() )
  134. {
  135. if( existingField.GetId() >= 0 && existingField.GetId() < MANDATORY_FIELDS )
  136. {
  137. newFields.push_back( existingField );
  138. continue;
  139. }
  140. // If requested, transfer only fields that occur also in the original library part
  141. if( m_removeExtraBox->IsChecked() && !libPart->FindField( existingField.GetName() ) )
  142. continue;
  143. newFields.push_back( existingField );
  144. }
  145. // Update the requested fields
  146. for( const wxString& fieldName : m_updateFields )
  147. {
  148. LIB_FIELD* libField = libPart->FindField( fieldName );
  149. if( !libField )
  150. continue;
  151. auto it = std::find_if( newFields.begin(), newFields.end(),
  152. [&] ( const SCH_FIELD& f )
  153. {
  154. return f.GetName() == fieldName;
  155. } );
  156. if( it != newFields.end() )
  157. {
  158. SCH_FIELD* newField = &*it;
  159. wxString fieldValue = libField->GetText();
  160. if( alias )
  161. {
  162. if( fieldName == TEMPLATE_FIELDNAME::GetDefaultFieldName( VALUE ) )
  163. fieldValue = alias->GetName();
  164. else if( fieldName == TEMPLATE_FIELDNAME::GetDefaultFieldName( DATASHEET ) )
  165. fieldValue = alias->GetDatasheetField().GetText();
  166. }
  167. if( fieldValue.IsEmpty() )
  168. {
  169. // If the library field is empty an update would clear an existing entry.
  170. // Check if this is the desired behavior.
  171. if( m_resetEmpty->IsChecked() )
  172. newField->SetText( wxEmptyString );
  173. }
  174. else
  175. {
  176. newField->SetText( fieldValue );
  177. }
  178. if( m_resetVisibility->IsChecked() )
  179. {
  180. newField->SetVisible( libField->IsVisible() );
  181. }
  182. if( m_resetPosition->IsChecked() )
  183. {
  184. newField->SetTextAngle( libField->GetTextAngle() );
  185. // Schematic fields are schematic-relative; symbol editor fields are component-relative
  186. if( m_createUndo )
  187. newField->SetTextPos( libField->GetTextPos() + aComponent->GetPosition() );
  188. else
  189. newField->SetTextPos( libField->GetTextPos() );
  190. }
  191. if( m_resetSizeAndStyle->IsChecked() )
  192. {
  193. newField->SetHorizJustify( libField->GetHorizJustify() );
  194. newField->SetVertJustify( libField->GetVertJustify() );
  195. newField->SetTextSize( libField->GetTextSize() );
  196. newField->SetItalic( libField->IsItalic() );
  197. newField->SetBold( libField->IsBold() );
  198. }
  199. }
  200. else
  201. {
  202. // Missing field, it has to be added to the component
  203. SCH_FIELD newField( wxPoint( 0, 0 ), newFields.size(), aComponent, fieldName );
  204. newField.ImportValues( *libField );
  205. newField.SetText( libField->GetText() );
  206. // Schematic fields are schematic-relative; symbol editor fields are component-relative
  207. if( m_createUndo )
  208. newField.SetTextPos( libField->GetTextPos() + aComponent->GetPosition() );
  209. else
  210. newField.SetTextPos( libField->GetTextPos() );
  211. newFields.push_back( newField );
  212. }
  213. }
  214. // Apply changes & clean-up
  215. aComponent->SetFields( newFields );
  216. }
  217. void DIALOG_UPDATE_FIELDS::checkAll( bool aCheck )
  218. {
  219. for( unsigned i = 0; i < m_fieldsBox->GetCount(); ++i )
  220. m_fieldsBox->Check( i, aCheck );
  221. }