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.

394 lines
12 KiB

  1. /**
  2. * @file cvpcb/readwrite_dlgs.cpp
  3. */
  4. /*
  5. * This program source code file is part of KiCad, a free EDA CAD application.
  6. *
  7. * Copyright (C) 2018 Jean-Pierre Charras, jean-pierre.charras
  8. * Copyright (C) 2011-2016 Wayne Stambaugh <stambaughw@verizon.net>
  9. * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors.
  10. *
  11. * This program is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU General Public License
  13. * as published by the Free Software Foundation; either version 2
  14. * of the License, or (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program; if not, you may find one here:
  23. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  24. * or you may search the http://www.gnu.org website for the version 2 license,
  25. * or you may write to the Free Software Foundation, Inc.,
  26. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  27. */
  28. #include <fctsys.h>
  29. #include <kiway.h>
  30. #include <common.h>
  31. #include <confirm.h>
  32. #include <build_version.h>
  33. #include <macros.h>
  34. #include <lib_id.h>
  35. #include <fp_lib_table.h>
  36. #include <html_messagebox.h>
  37. #include <cvpcb.h>
  38. #include <cvpcb_mainframe.h>
  39. #include <listboxes.h>
  40. #include <fp_conflict_assignment_selector.h>
  41. void CVPCB_MAINFRAME::SetNewPkg( const wxString& aFootprintName )
  42. {
  43. COMPONENT* component;
  44. int componentIndex;
  45. if( m_netlist.IsEmpty() )
  46. return;
  47. // If no component is selected, select the first one
  48. if( m_compListBox->GetFirstSelected() < 0 )
  49. {
  50. componentIndex = 0;
  51. m_compListBox->SetSelection( componentIndex, true );
  52. }
  53. // iterate over the selection
  54. while( m_compListBox->GetFirstSelected() != -1 )
  55. {
  56. // Get the component for the current iteration
  57. componentIndex = m_compListBox->GetFirstSelected();
  58. component = m_netlist.GetComponent( componentIndex );
  59. if( component == NULL )
  60. return;
  61. SetNewPkg( aFootprintName, componentIndex );
  62. m_compListBox->SetSelection( componentIndex, false );
  63. }
  64. // select the next component, if there is one
  65. if( componentIndex < (m_compListBox->GetCount() - 1) )
  66. componentIndex++;
  67. m_compListBox->SetSelection( componentIndex, true );
  68. // update the statusbar
  69. DisplayStatus();
  70. }
  71. void CVPCB_MAINFRAME::SetNewPkg( const wxString& aFootprintName, int aIndex )
  72. {
  73. COMPONENT* component;
  74. if( m_netlist.IsEmpty() )
  75. return;
  76. component = m_netlist.GetComponent( aIndex );
  77. if( component == NULL )
  78. return;
  79. LIB_ID fpid;
  80. if( !aFootprintName.IsEmpty() )
  81. {
  82. wxCHECK_RET( fpid.Parse( aFootprintName, LIB_ID::ID_PCB ) < 0,
  83. wxString::Format( _( "\"%s\" is not a valid LIB_ID." ), aFootprintName ) );
  84. }
  85. component->SetFPID( fpid );
  86. // create the new component description
  87. wxString description = wxString::Format( CMP_FORMAT, aIndex + 1,
  88. GetChars( component->GetReference() ),
  89. GetChars( component->GetValue() ),
  90. GetChars( FROM_UTF8( component->GetFPID().Format().c_str() ) ) );
  91. // Set the new description and deselect the processed component
  92. m_compListBox->SetString( aIndex, description );
  93. // Mark this "session" as modified
  94. m_modified = true;
  95. // update the statusbar
  96. DisplayStatus();
  97. }
  98. /// Return true if the resultant LIB_ID has a certain nickname. The guess
  99. /// is only made if this footprint resides in only one library.
  100. /// @return int - 0 on success, 1 on not found, 2 on ambiguous i.e. multiple matches
  101. static int guessNickname( FP_LIB_TABLE* aTbl, LIB_ID* aFootprintId )
  102. {
  103. if( aFootprintId->GetLibNickname().size() )
  104. return 0;
  105. wxString nick;
  106. wxString fpname = aFootprintId->GetLibItemName();
  107. std::vector<wxString> nicks = aTbl->GetLogicalLibs();
  108. // Search each library going through libraries alphabetically.
  109. for( unsigned libNdx = 0; libNdx<nicks.size(); ++libNdx )
  110. {
  111. wxArrayString fpnames;
  112. aTbl->FootprintEnumerate( fpnames, nicks[libNdx] );
  113. for( unsigned nameNdx = 0; nameNdx<fpnames.size(); ++nameNdx )
  114. {
  115. if( fpname == fpnames[nameNdx] )
  116. {
  117. if( !nick )
  118. nick = nicks[libNdx];
  119. else
  120. return 2; // duplicate, the guess would not be certain
  121. }
  122. }
  123. }
  124. if( nick.size() )
  125. {
  126. aFootprintId->SetLibNickname( nick );
  127. return 0;
  128. }
  129. return 1;
  130. }
  131. bool CVPCB_MAINFRAME::ReadNetListAndFpFiles( const std::string& aNetlist )
  132. {
  133. wxString msg;
  134. bool hasMissingNicks = false;
  135. ReadSchematicNetlist( aNetlist );
  136. if( m_compListBox == NULL )
  137. return false;
  138. LoadProjectFile();
  139. wxSafeYield();
  140. LoadFootprintFiles();
  141. BuildFOOTPRINTS_LISTBOX();
  142. BuildLIBRARY_LISTBOX();
  143. m_compListBox->Clear();
  144. if( m_netlist.AnyFootprintsLinked() )
  145. {
  146. for( unsigned i = 0; i < m_netlist.GetCount(); i++ )
  147. {
  148. COMPONENT* component = m_netlist.GetComponent( i );
  149. if( component->GetFPID().empty() )
  150. continue;
  151. if( component->GetFPID().IsLegacy() )
  152. hasMissingNicks = true;
  153. }
  154. }
  155. // Check if footprint links were generated before the footprint library table was implemented.
  156. if( hasMissingNicks )
  157. {
  158. msg = _(
  159. "Some of the assigned footprints are legacy entries (are missing lib nicknames). "
  160. "Would you like CvPcb to attempt to convert them to the new required LIB_ID format? "
  161. "(If you answer no, then these assignments will be cleared out and you will "
  162. "have to re-assign these footprints yourself.)"
  163. );
  164. if( IsOK( this, msg ) )
  165. {
  166. msg.Clear();
  167. try
  168. {
  169. for( unsigned i = 0; i < m_netlist.GetCount(); i++ )
  170. {
  171. COMPONENT* component = m_netlist.GetComponent( i );
  172. if( component->GetFPID().IsLegacy() )
  173. {
  174. // get this first here, it's possibly obsoleted if we get it too soon.
  175. FP_LIB_TABLE* tbl = Prj().PcbFootprintLibs( Kiway() );
  176. int guess = guessNickname( tbl, (LIB_ID*) &component->GetFPID() );
  177. switch( guess )
  178. {
  179. case 0:
  180. DBG(printf("%s: guessed OK ref:%s fpid:%s\n", __func__,
  181. TO_UTF8( component->GetReference() ), component->GetFPID().Format().c_str() );)
  182. m_modified = true;
  183. break;
  184. case 1:
  185. msg += wxString::Format( _(
  186. "Component \"%s\" footprint \"%s\" was <b>not found</b> in any library.\n" ),
  187. GetChars( component->GetReference() ),
  188. GetChars( component->GetFPID().GetLibItemName() )
  189. );
  190. break;
  191. case 2:
  192. msg += wxString::Format( _(
  193. "Component \"%s\" footprint \"%s\" was found in <b>multiple</b> libraries.\n" ),
  194. GetChars( component->GetReference() ),
  195. GetChars( component->GetFPID().GetLibItemName() )
  196. );
  197. break;
  198. }
  199. }
  200. }
  201. }
  202. catch( const IO_ERROR& ioe )
  203. {
  204. msg = ioe.What();
  205. msg += wxT( "\n\n" );
  206. msg += _( "First check your footprint library table entries." );
  207. wxMessageBox( msg, _( "Problematic Footprint Library Tables" ) );
  208. return false;
  209. }
  210. if( msg.size() )
  211. {
  212. HTML_MESSAGE_BOX dlg( this, wxEmptyString );
  213. dlg.MessageSet( _( "The following errors occurred attempting to convert the "
  214. "footprint assignments:\n\n" ) );
  215. dlg.ListSet( msg );
  216. dlg.MessageSet( _( "\nYou will need to reassign them manually if you want them "
  217. "to be updated correctly the next time you import the "
  218. "netlist in Pcbnew." ) );
  219. #if 1
  220. dlg.ShowModal();
  221. #else
  222. dlg.Fit();
  223. dlg.Show( true ); // modeless lets user watch while fixing the problems, but its not working.
  224. #endif
  225. }
  226. }
  227. else
  228. {
  229. // Clear the legacy footprint assignments.
  230. for( unsigned i = 0; i < m_netlist.GetCount(); i++ )
  231. {
  232. COMPONENT* component = m_netlist.GetComponent( i );
  233. if( component->GetFPID().IsLegacy() )
  234. {
  235. component->SetFPID( LIB_ID() /* empty */ );
  236. m_modified = true;
  237. }
  238. }
  239. }
  240. }
  241. // Display a dialog to select footprint selection, if the netlist
  242. // and the .cmp file give 2 different valid footprints
  243. std::vector <int > m_indexes; // indexes of footprints in netlist
  244. for( unsigned ii = 0; ii < m_netlist.GetCount(); ii++ )
  245. {
  246. COMPONENT* component = m_netlist.GetComponent( ii );
  247. if( component->GetAltFPID().empty() )
  248. continue;
  249. if( component->GetFPID().IsLegacy() || component->GetAltFPID().IsLegacy())
  250. continue;
  251. m_indexes.push_back( ii );
  252. }
  253. // If a n assignment conflict is found,
  254. // open a dialog to chose between schematic assignment
  255. // and .cmp file assignment:
  256. if( m_indexes.size() > 0 )
  257. {
  258. DIALOG_FP_CONFLICT_ASSIGNMENT_SELECTOR dlg( this );
  259. for( unsigned ii = 0; ii < m_indexes.size(); ii++ )
  260. {
  261. COMPONENT* component = m_netlist.GetComponent( m_indexes[ii] );
  262. wxString cmpfpid = component->GetFPID().Format();
  263. wxString schfpid = component->GetAltFPID().Format();
  264. dlg.Add( component->GetReference(), schfpid, cmpfpid );
  265. }
  266. if( dlg.ShowModal() == wxID_OK )
  267. {
  268. // Update the fp selection:
  269. for( unsigned ii = 0; ii < m_indexes.size(); ii++ )
  270. {
  271. COMPONENT* component = m_netlist.GetComponent( m_indexes[ii] );
  272. int choice = dlg.GetSelection( component->GetReference() );
  273. if( choice == 0 ) // the schematic (alt fpid) is chosen:
  274. component->SetFPID( component->GetAltFPID() );
  275. }
  276. }
  277. }
  278. // Populates the component list box:
  279. for( unsigned i = 0; i < m_netlist.GetCount(); i++ )
  280. {
  281. COMPONENT* component = m_netlist.GetComponent( i );
  282. msg.Printf( CMP_FORMAT, m_compListBox->GetCount() + 1,
  283. GetChars( component->GetReference() ),
  284. GetChars( component->GetValue() ),
  285. GetChars( FROM_UTF8( component->GetFPID().Format().c_str() ) ) );
  286. m_compListBox->AppendLine( msg );
  287. }
  288. if( !m_netlist.IsEmpty() )
  289. m_compListBox->SetSelection( 0, true );
  290. DisplayStatus();
  291. return true;
  292. }
  293. bool CVPCB_MAINFRAME::SaveFootprintAssociation( bool doSaveSchematic )
  294. {
  295. std::string payload;
  296. STRING_FORMATTER sf;
  297. m_netlist.FormatBackAnnotation( &sf );
  298. payload = sf.GetString();
  299. Kiway().ExpressMail( FRAME_SCH, MAIL_BACKANNOTATE_FOOTPRINTS, payload );
  300. if( doSaveSchematic )
  301. {
  302. payload = "";
  303. Kiway().ExpressMail( FRAME_SCH, MAIL_SCH_SAVE, payload );
  304. if( payload == "success" )
  305. SetStatusText( _( "Schematic saved" ), 1 );
  306. }
  307. return true;
  308. }