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.

351 lines
11 KiB

4 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2020 Roberto Fernandez Bautista <roberto.fer.bau@gmail.com>
  5. * Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software: you can redistribute it and/or modify it
  8. * under the terms of the GNU General Public License as published by the
  9. * Free Software Foundation, either version 3 of the License, or (at your
  10. * option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. #include <layer_ids.h>
  21. #include <dialog_imported_layers.h>
  22. #include <wx/msgdlg.h>
  23. wxString DIALOG_IMPORTED_LAYERS::WrapRequired( const wxString& aLayerName )
  24. {
  25. return aLayerName + wxT( " *" );
  26. }
  27. wxString DIALOG_IMPORTED_LAYERS::UnwrapRequired( const wxString& aLayerName )
  28. {
  29. if( !aLayerName.EndsWith( wxT( " *" ) ) )
  30. return aLayerName;
  31. return aLayerName.Left( aLayerName.Length() - 2 );
  32. }
  33. const INPUT_LAYER_DESC* DIALOG_IMPORTED_LAYERS::GetLayerDescription(
  34. const wxString& aLayerName ) const
  35. {
  36. wxString layerName = UnwrapRequired( aLayerName );
  37. for( const INPUT_LAYER_DESC& layerDescription : m_input_layers )
  38. {
  39. if( layerDescription.Name == layerName )
  40. return &layerDescription;
  41. }
  42. return nullptr;
  43. }
  44. PCB_LAYER_ID DIALOG_IMPORTED_LAYERS::GetSelectedLayerID()
  45. {
  46. // First check if there is a KiCad element selected
  47. wxString selectedKiCadLayerName;
  48. long itemIndex = -1;
  49. if( ( itemIndex = m_kicad_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
  50. wxLIST_STATE_SELECTED ) ) != wxNOT_FOUND )
  51. {
  52. selectedKiCadLayerName = m_kicad_layers_list->GetItemText( itemIndex );
  53. }
  54. else
  55. {
  56. return PCB_LAYER_ID::UNDEFINED_LAYER;
  57. }
  58. // There should only be one selected (or none) as the list is set with wxLC_SINGLE_SEL style
  59. wxASSERT_MSG( ( m_kicad_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
  60. wxLIST_STATE_SELECTED ) ) == wxNOT_FOUND,
  61. wxT( "There are more than one KiCad layer selected (unexpected)" ) );
  62. for( int layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
  63. {
  64. if( LayerName( ToLAYER_ID( layer ) ) == selectedKiCadLayerName )
  65. return ToLAYER_ID( layer );
  66. }
  67. return PCB_LAYER_ID::UNDEFINED_LAYER;
  68. }
  69. PCB_LAYER_ID DIALOG_IMPORTED_LAYERS::GetAutoMatchLayerID( const wxString& aInputLayerName )
  70. {
  71. wxString pureInputLayerName = UnwrapRequired( aInputLayerName );
  72. for( INPUT_LAYER_DESC inputLayerDesc : m_input_layers )
  73. {
  74. if( inputLayerDesc.Name == pureInputLayerName
  75. && inputLayerDesc.AutoMapLayer != PCB_LAYER_ID::UNSELECTED_LAYER )
  76. {
  77. return inputLayerDesc.AutoMapLayer;
  78. }
  79. }
  80. return PCB_LAYER_ID::UNDEFINED_LAYER;
  81. }
  82. void DIALOG_IMPORTED_LAYERS::AddMappings()
  83. {
  84. PCB_LAYER_ID selectedKiCadLayerID = GetSelectedLayerID();
  85. if( selectedKiCadLayerID == PCB_LAYER_ID::UNDEFINED_LAYER )
  86. return;
  87. // Now iterate through each selected layer in the unmatched layers list
  88. int itemIndex = -1;
  89. wxArrayInt rowsToDelete;
  90. while( ( itemIndex = m_unmatched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
  91. wxLIST_STATE_SELECTED ) )
  92. != wxNOT_FOUND )
  93. {
  94. wxString selectedLayerName = m_unmatched_layers_list->GetItemText( itemIndex );
  95. wxString kiName = LayerName( selectedKiCadLayerID );
  96. // add layer pair to the GUI list and also to the map
  97. long newItemIndex = m_matched_layers_list->InsertItem( 0, selectedLayerName );
  98. m_matched_layers_list->SetItem( newItemIndex, 1, kiName );
  99. m_matched_layers_map.insert(
  100. { UnwrapRequired( selectedLayerName ), selectedKiCadLayerID } );
  101. // remove selected layer from vector and also GUI list
  102. for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end();
  103. ++iter )
  104. {
  105. if( *iter == selectedLayerName )
  106. {
  107. m_unmatched_layer_names.erase( iter );
  108. break;
  109. }
  110. }
  111. rowsToDelete.Add( itemIndex );
  112. }
  113. DeleteListItems( rowsToDelete, m_unmatched_layers_list );
  114. // Auto select the first item to improve ease-of-use
  115. m_unmatched_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
  116. }
  117. void DIALOG_IMPORTED_LAYERS::RemoveMappings( int aStatus )
  118. {
  119. wxArrayInt rowsToDelete;
  120. int itemIndex = -1;
  121. while( ( itemIndex = m_matched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL, aStatus ) )
  122. != wxNOT_FOUND )
  123. {
  124. wxString selectedLayerName = m_matched_layers_list->GetItemText( itemIndex, 0 );
  125. wxString pureSelectedLayerName = UnwrapRequired( selectedLayerName );
  126. wxCHECK( m_matched_layers_map.find( pureSelectedLayerName ) != m_matched_layers_map.end(),
  127. /*void*/ );
  128. m_matched_layers_map.erase( pureSelectedLayerName );
  129. rowsToDelete.Add( itemIndex );
  130. m_unmatched_layers_list->InsertItem( 0, selectedLayerName );
  131. m_unmatched_layer_names.push_back( selectedLayerName );
  132. }
  133. DeleteListItems( rowsToDelete, m_matched_layers_list );
  134. }
  135. void DIALOG_IMPORTED_LAYERS::DeleteListItems( const wxArrayInt& aRowsToDelete,
  136. wxListCtrl* aListCtrl )
  137. {
  138. for( long n = ( aRowsToDelete.GetCount() - 1 ); 0 <= n; n-- )
  139. {
  140. aListCtrl->DeleteItem( aRowsToDelete[n] );
  141. }
  142. }
  143. void DIALOG_IMPORTED_LAYERS::OnAutoMatchLayersClicked( wxCommandEvent& event )
  144. {
  145. // Iterate through each selected layer in the unmatched layers list
  146. int itemIndex = -1;
  147. wxArrayInt rowsToDelete;
  148. while( ( itemIndex = m_unmatched_layers_list->GetNextItem( itemIndex, wxLIST_NEXT_ALL,
  149. wxLIST_STATE_DONTCARE ) )
  150. != wxNOT_FOUND )
  151. {
  152. wxString layerName = m_unmatched_layers_list->GetItemText( itemIndex );
  153. PCB_LAYER_ID autoMatchLayer = GetAutoMatchLayerID( layerName );
  154. if( autoMatchLayer == PCB_LAYER_ID::UNDEFINED_LAYER )
  155. continue;
  156. wxString kiName = LayerName( autoMatchLayer );
  157. // add layer pair to the GUI list and also to the map
  158. long newItemIndex = m_matched_layers_list->InsertItem( 0, layerName );
  159. m_matched_layers_list->SetItem( newItemIndex, 1, kiName );
  160. m_matched_layers_map.insert( { UnwrapRequired( layerName ), autoMatchLayer } );
  161. // remove selected layer from vector and also GUI list
  162. for( auto iter = m_unmatched_layer_names.begin(); iter != m_unmatched_layer_names.end();
  163. ++iter )
  164. {
  165. if( *iter == layerName )
  166. {
  167. m_unmatched_layer_names.erase( iter );
  168. break;
  169. }
  170. }
  171. rowsToDelete.Add( itemIndex );
  172. }
  173. DeleteListItems( rowsToDelete, m_unmatched_layers_list );
  174. }
  175. DIALOG_IMPORTED_LAYERS::DIALOG_IMPORTED_LAYERS( wxWindow* aParent,
  176. const std::vector<INPUT_LAYER_DESC>& aLayerDesc ) :
  177. DIALOG_IMPORTED_LAYERS_BASE( aParent )
  178. {
  179. LSET kiCadLayers;
  180. // Read in the input layers
  181. for( INPUT_LAYER_DESC inLayer : aLayerDesc )
  182. {
  183. m_input_layers.push_back( inLayer );
  184. wxString layerName = inLayer.Required ? WrapRequired( inLayer.Name ) : inLayer.Name;
  185. m_unmatched_layer_names.push_back( layerName );
  186. kiCadLayers |= inLayer.PermittedLayers;
  187. }
  188. int maxTextWidth = GetTextExtent( _( "Imported Layer" ) ).x;
  189. for( const INPUT_LAYER_DESC& layer : m_input_layers )
  190. maxTextWidth = std::max( maxTextWidth, GetTextExtent( layer.Name ).x );
  191. // Initialize columns in the wxListCtrl elements:
  192. wxListItem importedLayersHeader;
  193. importedLayersHeader.SetId( 0 );
  194. importedLayersHeader.SetText( _( "Imported Layer" ) );
  195. importedLayersHeader.SetWidth( maxTextWidth + 15 );
  196. m_unmatched_layers_list->InsertColumn( 0, importedLayersHeader );
  197. int kicadMaxTextWidth = GetTextExtent( wxT( "User.Drawings" ) ).x;
  198. wxListItem kicadLayersHeader;
  199. kicadLayersHeader.SetId( 0 );
  200. kicadLayersHeader.SetText( _( "KiCad Layer" ) );
  201. kicadLayersHeader.SetWidth( kicadMaxTextWidth + 5 );
  202. m_kicad_layers_list->InsertColumn( 0, kicadLayersHeader );
  203. kicadLayersHeader.SetId( 1 );
  204. importedLayersHeader.SetWidth( maxTextWidth + 15 );
  205. kicadLayersHeader.SetWidth( kicadMaxTextWidth + 5 );
  206. m_matched_layers_list->InsertColumn( 0, importedLayersHeader );
  207. m_matched_layers_list->InsertColumn( 1, kicadLayersHeader );
  208. // Load the input layer list to unmatched layers
  209. int row = 0;
  210. for( wxString importedLayerName : m_unmatched_layer_names )
  211. {
  212. wxListItem item;
  213. item.SetId( row );
  214. item.SetText( importedLayerName );
  215. m_unmatched_layers_list->InsertItem( item );
  216. ++row;
  217. }
  218. // Auto select the first item to improve ease-of-use
  219. m_unmatched_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
  220. // Load the KiCad Layer names
  221. row = 0;
  222. LSEQ kicadLayersSeq = kiCadLayers.Seq();
  223. for( PCB_LAYER_ID layer : kicadLayersSeq )
  224. {
  225. wxString kiName = LayerName( layer );
  226. wxListItem item;
  227. item.SetId( row );
  228. item.SetText( kiName );
  229. m_kicad_layers_list->InsertItem( item );
  230. ++row;
  231. }
  232. // Auto select the first item to improve ease-of-use
  233. m_kicad_layers_list->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
  234. SetupStandardButtons();
  235. Fit();
  236. finishDialogSettings();
  237. }
  238. std::vector<wxString> DIALOG_IMPORTED_LAYERS::GetUnmappedRequiredLayers() const
  239. {
  240. std::vector<wxString> unmappedLayers;
  241. for( const wxString& layerName : m_unmatched_layer_names )
  242. {
  243. const INPUT_LAYER_DESC* layerDesc = GetLayerDescription( layerName );
  244. wxASSERT_MSG( layerDesc != nullptr, wxT( "Expected to find layer description" ) );
  245. if( layerDesc->Required )
  246. unmappedLayers.push_back( layerDesc->Name );
  247. }
  248. return unmappedLayers;
  249. }
  250. std::map<wxString, PCB_LAYER_ID>
  251. DIALOG_IMPORTED_LAYERS::GetMapModal( wxWindow* aParent,
  252. const std::vector<INPUT_LAYER_DESC>& aLayerDesc )
  253. {
  254. DIALOG_IMPORTED_LAYERS dlg( aParent, aLayerDesc );
  255. bool dataOk = false;
  256. while( !dataOk )
  257. {
  258. dlg.ShowModal();
  259. if( dlg.GetUnmappedRequiredLayers().size() > 0 )
  260. {
  261. wxMessageBox( _( "All required layers (marked with '*') must be matched. "
  262. "Please click on 'Auto-Match Layers' to "
  263. "automatically match the remaining layers" ),
  264. _( "Unmatched Layers" ), wxICON_ERROR | wxOK );
  265. }
  266. else
  267. {
  268. dataOk = true;
  269. }
  270. }
  271. return dlg.m_matched_layers_map;
  272. }