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.

475 lines
14 KiB

17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  6. * Copyright (C) 2013 Wayne Stambaugh <stambaughw@gmail.com>
  7. * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors.
  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 2
  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. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  22. * or you may search the http://www.gnu.org website for the version 2 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 <fctsys.h>
  27. #include <kicad_string.h>
  28. #include <pcb_edit_frame.h>
  29. #include <macros.h>
  30. #include <board_commit.h>
  31. #include <bitmaps.h>
  32. #include <class_board.h>
  33. #include <class_module.h>
  34. #include <project.h>
  35. #include <wx_html_report_panel.h>
  36. #include <kiway.h>
  37. #include <dialog_exchange_footprints.h>
  38. #define ID_MATCH_FP_ALL 4200
  39. #define ID_MATCH_FP_SELECTED 4201
  40. #define ID_MATCH_FP_REF 4202
  41. #define ID_MATCH_FP_VAL 4203
  42. #define ID_MATCH_FP_ID 4204
  43. int g_matchModeForUpdate = ID_MATCH_FP_ALL;
  44. int g_matchModeForUpdateSelected = ID_MATCH_FP_SELECTED;
  45. int g_matchModeForExchange = ID_MATCH_FP_REF;
  46. int g_matchModeForExchangeSelected = ID_MATCH_FP_SELECTED;
  47. DIALOG_EXCHANGE_FOOTPRINTS::DIALOG_EXCHANGE_FOOTPRINTS( PCB_EDIT_FRAME* aParent, MODULE* aModule,
  48. bool updateMode, bool selectedMode ) :
  49. DIALOG_EXCHANGE_FOOTPRINTS_BASE( aParent ),
  50. m_commit( aParent ),
  51. m_parent( aParent ),
  52. m_currentModule( aModule ),
  53. m_updateMode( updateMode )
  54. {
  55. wxString title = updateMode ? _( "Update Footprints from Library" ) : _( "Change Footprints" );
  56. wxString verb = updateMode ? _( "Update" ) : _( "Change" );
  57. wxString label;
  58. SetTitle( title );
  59. if( m_updateMode )
  60. {
  61. label.Printf( m_matchAll->GetLabel(), verb );
  62. m_matchAll->SetLabel( label );
  63. m_changeSizer->Show( false );
  64. }
  65. else
  66. {
  67. m_upperSizer->FindItem( m_matchAll )->Show( false );
  68. m_newIDBrowseButton->SetBitmap( KiBitmap( small_library_xpm ) );
  69. }
  70. if( m_currentModule )
  71. {
  72. label.Printf( m_matchSelected->GetLabel(), verb );
  73. m_matchSelected->SetLabel( label );
  74. m_newID->AppendText( FROM_UTF8( m_currentModule->GetFPID().Format().c_str() ) );
  75. }
  76. else
  77. m_upperSizer->FindItem( m_matchSelected )->Show( false );
  78. label.Printf( m_matchSpecifiedRef->GetLabel(), verb );
  79. m_matchSpecifiedRef->SetLabel( label );
  80. // Use ChangeValue() instead of SetValue() so we don't generate events.
  81. if( m_currentModule )
  82. m_specifiedRef->ChangeValue( m_currentModule->GetReference() );
  83. label.Printf( m_matchSpecifiedValue->GetLabel(), verb );
  84. m_matchSpecifiedValue->SetLabel( label );
  85. if( m_currentModule )
  86. m_specifiedValue->ChangeValue( m_currentModule->GetValue() );
  87. label.Printf( m_matchSpecifiedID->GetLabel(), verb );
  88. m_matchSpecifiedID->SetLabel( label );
  89. if( m_currentModule )
  90. m_specifiedID->ChangeValue( FROM_UTF8( m_currentModule->GetFPID().Format().c_str() ) );
  91. m_specifiedIDBrowseButton->SetBitmap( KiBitmap( small_library_xpm ) );
  92. m_upperSizer->SetEmptyCellSize( wxSize( 0, 0 ) );
  93. m_upperSizer->RecalcSizes();
  94. // initialize match-mode
  95. if( m_updateMode )
  96. m_matchMode = selectedMode ? &g_matchModeForUpdateSelected : &g_matchModeForUpdate;
  97. else
  98. m_matchMode = selectedMode ? &g_matchModeForExchangeSelected : &g_matchModeForExchange;
  99. wxCommandEvent event;
  100. event.SetEventObject( this );
  101. switch( *m_matchMode )
  102. {
  103. case ID_MATCH_FP_ALL: OnMatchAllClicked( event ); break;
  104. case ID_MATCH_FP_SELECTED: OnMatchSelectedClicked( event ); break;
  105. case ID_MATCH_FP_REF: OnMatchRefClicked( event ); break;
  106. case ID_MATCH_FP_VAL: OnMatchValueClicked( event ); break;
  107. case ID_MATCH_FP_ID: OnMatchIDClicked( event ); break;
  108. default: break;
  109. }
  110. // DIALOG_SHIM needs a unique hash_key because classname is not sufficient
  111. // because the update and change versions of this dialog have different controls.
  112. m_hash_key = TO_UTF8( GetTitle() );
  113. // Ensure m_closeButton (with id = wxID_CANCEL) has the right label
  114. // (to fix automatic renaming of button label )
  115. m_sdbSizerCancel->SetLabel( _( "Close" ) );
  116. m_sdbSizerApply->SetDefault();
  117. // Now all widgets have the size fixed, call FinishDialogSettings
  118. FinishDialogSettings();
  119. }
  120. bool DIALOG_EXCHANGE_FOOTPRINTS::isMatch( MODULE* aModule )
  121. {
  122. LIB_ID specifiedID;
  123. switch( *m_matchMode )
  124. {
  125. case ID_MATCH_FP_ALL:
  126. return true;
  127. case ID_MATCH_FP_SELECTED:
  128. return aModule == m_currentModule;
  129. case ID_MATCH_FP_REF:
  130. return WildCompareString( m_specifiedRef->GetValue(), aModule->GetReference(), false );
  131. case ID_MATCH_FP_VAL:
  132. return WildCompareString( m_specifiedValue->GetValue(), aModule->GetValue(), false );
  133. case ID_MATCH_FP_ID:
  134. specifiedID.Parse( m_specifiedID->GetValue(), LIB_ID::ID_PCB );
  135. return aModule->GetFPID() == specifiedID;
  136. default:
  137. return false; // just to quiet compiler warnings....
  138. }
  139. }
  140. wxRadioButton* DIALOG_EXCHANGE_FOOTPRINTS::getRadioButtonForMode()
  141. {
  142. switch( *m_matchMode )
  143. {
  144. case ID_MATCH_FP_ALL: return m_matchAll;
  145. case ID_MATCH_FP_SELECTED: return m_matchSelected;
  146. case ID_MATCH_FP_REF: return m_matchSpecifiedRef;
  147. case ID_MATCH_FP_VAL: return m_matchSpecifiedValue;
  148. case ID_MATCH_FP_ID: return m_matchSpecifiedID;
  149. default: return nullptr;
  150. }
  151. }
  152. void DIALOG_EXCHANGE_FOOTPRINTS::updateMatchModeRadioButtons( wxUpdateUIEvent& )
  153. {
  154. wxRadioButton* rb_button = getRadioButtonForMode();
  155. wxRadioButton* rb_butt_list[] =
  156. {
  157. m_matchAll,
  158. m_matchSelected,
  159. m_matchSpecifiedRef,
  160. m_matchSpecifiedValue,
  161. m_matchSpecifiedID,
  162. nullptr // end of list
  163. };
  164. // Ensure the button state is ok. Only one button can be checked
  165. // Change button state only if its state is incorrect, otherwise
  166. // we have issues depending on the platform.
  167. for( int ii = 0; rb_butt_list[ii]; ++ii )
  168. {
  169. bool state = rb_butt_list[ii] == rb_button;
  170. if( rb_butt_list[ii]->GetValue() != state )
  171. rb_butt_list[ii]->SetValue( state );
  172. }
  173. }
  174. void DIALOG_EXCHANGE_FOOTPRINTS::OnMatchAllClicked( wxCommandEvent& event )
  175. {
  176. *m_matchMode = ID_MATCH_FP_ALL;
  177. if( event.GetEventObject() == this )
  178. SetInitialFocus( m_matchAll );
  179. else
  180. m_matchAll->SetFocus();
  181. }
  182. void DIALOG_EXCHANGE_FOOTPRINTS::OnMatchSelectedClicked( wxCommandEvent& event )
  183. {
  184. *m_matchMode = ID_MATCH_FP_SELECTED;
  185. if( event.GetEventObject() == this )
  186. SetInitialFocus( m_matchSelected );
  187. else
  188. m_matchSelected->SetFocus();
  189. }
  190. void DIALOG_EXCHANGE_FOOTPRINTS::OnMatchRefClicked( wxCommandEvent& event )
  191. {
  192. *m_matchMode = ID_MATCH_FP_REF;
  193. if( event.GetEventObject() == this )
  194. SetInitialFocus( m_specifiedRef );
  195. else if( event.GetEventObject() != m_specifiedRef )
  196. m_specifiedRef->SetFocus();
  197. }
  198. void DIALOG_EXCHANGE_FOOTPRINTS::OnMatchValueClicked( wxCommandEvent& event )
  199. {
  200. *m_matchMode = ID_MATCH_FP_VAL;
  201. if( event.GetEventObject() == this )
  202. SetInitialFocus( m_specifiedValue );
  203. else if( event.GetEventObject() != m_specifiedValue )
  204. m_specifiedValue->SetFocus();
  205. }
  206. void DIALOG_EXCHANGE_FOOTPRINTS::OnMatchIDClicked( wxCommandEvent& event )
  207. {
  208. *m_matchMode = ID_MATCH_FP_ID;
  209. if( event.GetEventObject() == this )
  210. SetInitialFocus( m_specifiedID );
  211. else if( event.GetEventObject() != m_specifiedID )
  212. m_specifiedID->SetFocus();
  213. }
  214. void DIALOG_EXCHANGE_FOOTPRINTS::OnApplyClicked( wxCommandEvent& event )
  215. {
  216. wxBusyCursor dummy;
  217. m_MessageWindow->Clear();
  218. m_MessageWindow->Flush( true );
  219. if( processMatchingModules() )
  220. {
  221. if( m_parent->GetBoard()->IsElementVisible( LAYER_RATSNEST ) )
  222. m_parent->Compile_Ratsnest( true );
  223. m_parent->GetGalCanvas()->Refresh();
  224. }
  225. m_commit.Push( wxT( "Changed footprint" ) );
  226. }
  227. bool DIALOG_EXCHANGE_FOOTPRINTS::processMatchingModules()
  228. {
  229. MODULE* Module;
  230. MODULE* PtBack;
  231. bool change = false;
  232. LIB_ID newFPID;
  233. wxString value;
  234. if( !m_parent->GetBoard()->m_Modules )
  235. return false;
  236. if( !m_updateMode )
  237. {
  238. newFPID.Parse( m_newID->GetValue(), LIB_ID::ID_PCB );
  239. if( !newFPID.IsValid() )
  240. return false;
  241. }
  242. /* The change is done from the last module because processModule() modifies the last item
  243. * in the list.
  244. * Note: for the first module in chain (the last here), Module->Back() points to the board
  245. * or is NULL.
  246. */
  247. Module = m_parent->GetBoard()->m_Modules.GetLast();
  248. for( ; Module && Module->Type() == PCB_MODULE_T; Module = PtBack )
  249. {
  250. PtBack = Module->Back();
  251. if( !isMatch( Module ) )
  252. continue;
  253. if( m_updateMode )
  254. {
  255. if( processModule( Module, Module->GetFPID()) )
  256. change = true;
  257. }
  258. else
  259. {
  260. if( processModule( Module, newFPID ) )
  261. change = true;
  262. }
  263. }
  264. return change;
  265. }
  266. bool DIALOG_EXCHANGE_FOOTPRINTS::processModule( MODULE* aModule, const LIB_ID& aNewFPID )
  267. {
  268. LIB_ID oldFPID = aModule->GetFPID();
  269. REPORTER& reporter = m_MessageWindow->Reporter();
  270. wxString msg;
  271. // Load new module.
  272. msg.Printf( _( "%s footprint \"%s\" (from \"%s\") to \"%s\"" ),
  273. m_updateMode ? _( "Update" ) : _( "Change" ),
  274. aModule->GetReference(),
  275. oldFPID.Format().c_str(),
  276. aNewFPID.Format().c_str() );
  277. MODULE* newModule = m_parent->LoadFootprint( aNewFPID );
  278. if( !newModule )
  279. {
  280. msg << ": " << _( "*** footprint not found ***" );
  281. reporter.Report( msg, REPORTER::RPT_ERROR );
  282. return false;
  283. }
  284. m_parent->Exchange_Module( aModule, newModule, m_commit,
  285. m_removeExtraBox->GetValue(),
  286. m_resetTextItemLayers->GetValue(),
  287. m_resetTextItemEffects->GetValue() );
  288. if( aModule == m_currentModule )
  289. m_currentModule = newModule;
  290. msg += ": OK";
  291. reporter.Report( msg, REPORTER::RPT_ACTION );
  292. return true;
  293. }
  294. void processTextItem( const TEXTE_MODULE& aSrc, TEXTE_MODULE& aDest,
  295. bool resetText, bool resetTextLayers, bool resetTextEffects )
  296. {
  297. if( !resetText )
  298. aDest.SetText( aSrc.GetText() );
  299. if( !resetTextLayers )
  300. {
  301. aDest.SetLayer( aSrc.GetLayer() );
  302. aDest.SetVisible( aSrc.IsVisible() );
  303. }
  304. if( !resetTextEffects )
  305. aDest.SetEffects( aSrc );
  306. }
  307. TEXTE_MODULE* getMatchingTextItem( TEXTE_MODULE* aRefItem, MODULE* aModule )
  308. {
  309. for( auto iItem = aModule->GraphicalItemsList().GetFirst(); iItem; iItem = iItem->Next() )
  310. {
  311. TEXTE_MODULE* candidate = dyn_cast<TEXTE_MODULE*>( iItem );
  312. if( candidate && candidate->GetText() == aRefItem->GetText() )
  313. return candidate;
  314. }
  315. return nullptr;
  316. }
  317. void PCB_EDIT_FRAME::Exchange_Module( MODULE* aSrc, MODULE* aDest, BOARD_COMMIT& aCommit,
  318. bool deleteExtraTexts,
  319. bool resetTextLayers, bool resetTextEffects )
  320. {
  321. aDest->SetParent( GetBoard() );
  322. /* place module without ratsnest refresh: this will be made later
  323. * when all modules are on board */
  324. PlaceModule( aDest, false );
  325. // Copy full placement and pad net names (when possible)
  326. // but not local settings like clearances (use library values)
  327. aSrc->CopyNetlistSettings( aDest, false );
  328. // Copy reference
  329. processTextItem( aSrc->Reference(), aDest->Reference(),
  330. // never reset reference text
  331. false,
  332. resetTextLayers, resetTextEffects );
  333. // Copy value
  334. processTextItem( aSrc->Value(), aDest->Value(),
  335. // reset value text only when it is a proxy for the footprint ID
  336. // (cf replacing value "MountingHole-2.5mm" with "MountingHole-4.0mm")
  337. aSrc->GetValue() == aSrc->GetFPID().GetLibItemName(),
  338. resetTextLayers, resetTextEffects );
  339. // Copy fields in accordance with the reset* flags
  340. for( BOARD_ITEM* item = aSrc->GraphicalItemsList().GetFirst(); item; item = item->Next() )
  341. {
  342. TEXTE_MODULE* srcItem = dyn_cast<TEXTE_MODULE*>( item );
  343. if( srcItem )
  344. {
  345. TEXTE_MODULE* destItem = getMatchingTextItem( srcItem, aDest );
  346. if( destItem )
  347. processTextItem( *srcItem, *destItem, false, resetTextLayers, resetTextEffects );
  348. else if( !deleteExtraTexts )
  349. aDest->GraphicalItemsList().Append( new TEXTE_MODULE( *srcItem ) );
  350. }
  351. }
  352. // Updating other parameters
  353. aDest->SetTimeStamp( aSrc->GetTimeStamp() );
  354. aDest->SetPath( aSrc->GetPath() );
  355. aCommit.Remove( aSrc );
  356. aCommit.Add( aDest );
  357. // @todo LEGACY should be unnecessary
  358. GetBoard()->m_Status_Pcb = 0;
  359. aDest->ClearFlags();
  360. }
  361. void DIALOG_EXCHANGE_FOOTPRINTS::ViewAndSelectFootprint( wxCommandEvent& event )
  362. {
  363. wxString newname = m_newID->GetValue();
  364. KIWAY_PLAYER* frame = Kiway().Player( FRAME_PCB_MODULE_VIEWER_MODAL, true );
  365. if( frame->ShowModal( &newname, this ) )
  366. {
  367. if( event.GetEventObject() == m_newIDBrowseButton )
  368. m_newID->SetValue( newname );
  369. else
  370. m_specifiedID->SetValue( newname );
  371. }
  372. frame->Destroy();
  373. }