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.

336 lines
12 KiB

3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2017-2023 KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software: you can redistribute it and/or modify it
  7. * under the terms of the GNU General Public License as published by the
  8. * Free Software Foundation, either version 3 of the License, or (at your
  9. * option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful, but
  12. * WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <pcb_edit_frame.h>
  20. #include <panel_setup_layers.h>
  21. #include <panel_setup_text_and_graphics.h>
  22. #include <panel_setup_constraints.h>
  23. #include <dialogs/panel_setup_netclasses.h>
  24. #include <panel_setup_tracks_and_vias.h>
  25. #include <panel_setup_mask_and_paste.h>
  26. #include <../board_stackup_manager/panel_board_stackup.h>
  27. #include <../board_stackup_manager/panel_board_finish.h>
  28. #include <confirm.h>
  29. #include <board_design_settings.h>
  30. #include <kiface_base.h>
  31. #include <drc/drc_item.h>
  32. #include <dialog_import_settings.h>
  33. #include <io_mgr.h>
  34. #include <dialogs/panel_setup_severities.h>
  35. #include <panel_text_variables.h>
  36. #include <project.h>
  37. #include <project/project_file.h>
  38. #include <settings/settings_manager.h>
  39. #include <widgets/resettable_panel.h>
  40. #include <widgets/wx_progress_reporters.h>
  41. #include <wildcards_and_files_ext.h>
  42. #include "dialog_board_setup.h"
  43. #include "panel_setup_rules.h"
  44. std::mutex DIALOG_BOARD_SETUP::g_Mutex;
  45. DIALOG_BOARD_SETUP::DIALOG_BOARD_SETUP( PCB_EDIT_FRAME* aFrame ) :
  46. PAGED_DIALOG( aFrame, _( "Board Setup" ), false,
  47. _( "Import Settings from Another Board..." ) ),
  48. m_frame( aFrame )
  49. {
  50. SetEvtHandlerEnabled( false );
  51. m_layers = new PANEL_SETUP_LAYERS( this, m_frame );
  52. m_physicalStackup = new PANEL_SETUP_BOARD_STACKUP( this, m_frame, m_layers );
  53. m_boardFinish = new PANEL_SETUP_BOARD_FINISH( this, m_frame );
  54. m_currentPage = -1;
  55. /*
  56. * WARNING: If you change page names you MUST update calls to ShowBoardSetupDialog().
  57. */
  58. m_treebook->AddPage( new wxPanel( GetTreebook() ), _( "Board Stackup" ) );
  59. /*
  60. * WARNING: Code currently relies on the layers setup coming before the physical stackup panel,
  61. * and thus transferring data to the board first. See comment in
  62. * PANEL_SETUP_BOARD_STACKUP::TransferDataFromWindow and rework this logic if it is determined
  63. * that the order of these pages should be changed.
  64. */
  65. m_treebook->AddSubPage( m_layers, _( "Board Editor Layers" ) );
  66. m_physicalStackupPage = m_treebook->GetPageCount();
  67. m_treebook->AddSubPage( m_physicalStackup, _( "Physical Stackup" ) );
  68. m_treebook->AddSubPage( m_boardFinish, _( "Board Finish" ) );
  69. m_maskAndPagePage = m_treebook->GetPageCount();
  70. m_treebook->AddLazySubPage(
  71. [this]( wxWindow* aParent ) -> wxWindow*
  72. {
  73. return new PANEL_SETUP_MASK_AND_PASTE( aParent, m_frame );
  74. }, _( "Solder Mask/Paste" ) );
  75. m_treebook->AddPage( new wxPanel( GetTreebook() ), _( "Text & Graphics" ) );
  76. m_textAndGraphicsPage = m_treebook->GetPageCount();
  77. m_treebook->AddLazySubPage(
  78. [this]( wxWindow* aParent ) -> wxWindow*
  79. {
  80. return new PANEL_SETUP_TEXT_AND_GRAPHICS( aParent, m_frame );
  81. }, _( "Defaults" ) );
  82. m_formattingPage = m_treebook->GetPageCount();
  83. m_treebook->AddLazySubPage(
  84. [this]( wxWindow* aParent ) -> wxWindow*
  85. {
  86. return new PANEL_SETUP_FORMATTING( aParent, m_frame );
  87. }, _( "Formatting" ) );
  88. m_treebook->AddLazySubPage(
  89. [this]( wxWindow* aParent ) -> wxWindow*
  90. {
  91. return new PANEL_TEXT_VARIABLES( aParent, &Prj() );
  92. }, _( "Text Variables" ) );
  93. m_treebook->AddPage( new wxPanel( GetTreebook() ), _( "Design Rules" ) );
  94. m_constraintsPage = m_treebook->GetPageCount();
  95. m_treebook->AddLazySubPage(
  96. [this]( wxWindow* aParent ) -> wxWindow*
  97. {
  98. return new PANEL_SETUP_CONSTRAINTS( aParent, m_frame );
  99. }, _( "Constraints" ) );
  100. m_tracksAndViasPage = m_treebook->GetPageCount();
  101. m_treebook->AddLazySubPage(
  102. [this]( wxWindow* aParent ) -> wxWindow*
  103. {
  104. return new PANEL_SETUP_TRACKS_AND_VIAS( aParent, m_frame );
  105. }, _( "Pre-defined Sizes" ) );
  106. m_netclassesPage = m_treebook->GetPageCount();
  107. m_treebook->AddLazySubPage(
  108. [this]( wxWindow* aParent ) -> wxWindow*
  109. {
  110. BOARD* board = m_frame->GetBoard();
  111. return new PANEL_SETUP_NETCLASSES( aParent, m_frame,
  112. m_frame->Prj().GetProjectFile().NetSettings(),
  113. board->GetNetClassAssignmentCandidates(),
  114. false );
  115. }, _( "Net Classes" ) );
  116. m_treebook->AddLazySubPage(
  117. [this]( wxWindow* aParent ) -> wxWindow*
  118. {
  119. return new PANEL_SETUP_RULES( aParent, m_frame );
  120. }, _( "Custom Rules" ) );
  121. m_severitiesPage = m_treebook->GetPageCount();
  122. m_treebook->AddLazySubPage(
  123. [this]( wxWindow* aParent ) -> wxWindow*
  124. {
  125. BOARD* board = m_frame->GetBoard();
  126. return new PANEL_SETUP_SEVERITIES( aParent, DRC_ITEM::GetItemsWithSeverities(),
  127. board->GetDesignSettings().m_DRCSeverities );
  128. }, _( "Violation Severity" ) );
  129. for( size_t i = 0; i < m_treebook->GetPageCount(); ++i )
  130. m_treebook->ExpandNode( i );
  131. // This is unfortunate, but it's the cost of lazy-loading the panels
  132. m_treebook->SetMinSize( wxSize( 980, 600 ) );
  133. m_treebook->SetInitialSize( wxSize( 980, 600 ) );
  134. SetEvtHandlerEnabled( true );
  135. finishDialogSettings();
  136. if( Prj().IsReadOnly() )
  137. {
  138. m_infoBar->ShowMessage( _( "Project is missing or read-only. Some settings will not "
  139. "be editable." ), wxICON_WARNING );
  140. }
  141. wxBookCtrlEvent evt( wxEVT_TREEBOOK_PAGE_CHANGED, wxID_ANY, 0 );
  142. wxQueueEvent( m_treebook, evt.Clone() );
  143. }
  144. DIALOG_BOARD_SETUP::~DIALOG_BOARD_SETUP()
  145. {
  146. }
  147. void DIALOG_BOARD_SETUP::onPageChanged( wxBookCtrlEvent& aEvent )
  148. {
  149. PAGED_DIALOG::onPageChanged( aEvent );
  150. size_t page = aEvent.GetSelection();
  151. // Ensure layer page always gets updated even if we aren't moving towards it
  152. if( m_currentPage == m_physicalStackupPage )
  153. m_layers->SyncCopperLayers( m_physicalStackup->GetCopperLayerCount() );
  154. if( page == m_physicalStackupPage )
  155. m_physicalStackup->OnLayersOptionsChanged( m_layers->GetUILayerMask() );
  156. else if( Prj().IsReadOnly() )
  157. KIUI::Disable( m_treebook->GetPage( page ) );
  158. m_currentPage = page;
  159. }
  160. void DIALOG_BOARD_SETUP::onAuxiliaryAction( wxCommandEvent& aEvent )
  161. {
  162. DIALOG_IMPORT_SETTINGS importDlg( this, m_frame );
  163. if( importDlg.ShowModal() == wxID_CANCEL )
  164. return;
  165. wxFileName boardFn( importDlg.GetFilePath() );
  166. wxFileName projectFn( boardFn );
  167. projectFn.SetExt( ProjectFileExtension );
  168. if( !m_frame->GetSettingsManager()->LoadProject( projectFn.GetFullPath(), false ) )
  169. {
  170. wxString msg = wxString::Format( _( "Error importing settings from board:\n"
  171. "Associated project file %s could not be loaded" ),
  172. projectFn.GetFullPath() );
  173. DisplayErrorMessage( this, msg );
  174. return;
  175. }
  176. // Flag so user can stop work if it will result in deleted inner copper layers
  177. // and still clean up this function properly.
  178. bool okToProceed = true;
  179. PROJECT* otherPrj = m_frame->GetSettingsManager()->GetProject( projectFn.GetFullPath() );
  180. PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::KICAD_SEXP ) );
  181. BOARD* otherBoard = nullptr;
  182. try
  183. {
  184. WX_PROGRESS_REPORTER progressReporter( this, _( "Loading PCB" ), 1 );
  185. otherBoard = pi->Load( boardFn.GetFullPath(), nullptr, nullptr, nullptr,
  186. &progressReporter );
  187. if( importDlg.m_LayersOpt->GetValue() )
  188. {
  189. BOARD* loadedBoard = m_frame->GetBoard();
  190. // Check if "Import Settings" board has more layers than the current board.
  191. okToProceed = m_layers->CheckCopperLayerCount( loadedBoard, otherBoard );
  192. }
  193. }
  194. catch( const IO_ERROR& ioe )
  195. {
  196. // You wouldn't think boardFn.GetFullPath() would throw, but we get a stack buffer
  197. // underflow from ASAN. While it's probably an ASAN error, a second try/catch doesn't
  198. // cost us much.
  199. try
  200. {
  201. if( ioe.Problem() != wxT( "CANCEL" ) )
  202. {
  203. wxString msg = wxString::Format( _( "Error loading board file:\n%s" ),
  204. boardFn.GetFullPath() );
  205. DisplayErrorMessage( this, msg, ioe.What() );
  206. }
  207. if( otherPrj != &m_frame->Prj() )
  208. m_frame->GetSettingsManager()->UnloadProject( otherPrj, false );
  209. }
  210. catch(...)
  211. {
  212. // That was already our best-efforts
  213. }
  214. return;
  215. }
  216. if( okToProceed )
  217. {
  218. otherBoard->SetProject( otherPrj );
  219. // If layers options are imported, import also the stackup
  220. // layers options and stackup are linked, so they cannot be imported
  221. // separately, and stackup can be imported only after layers options
  222. if( importDlg.m_LayersOpt->GetValue() )
  223. {
  224. m_physicalStackup->ImportSettingsFrom( otherBoard );
  225. m_layers->ImportSettingsFrom( otherBoard );
  226. m_boardFinish->ImportSettingsFrom( otherBoard );
  227. }
  228. if( importDlg.m_TextAndGraphicsOpt->GetValue() )
  229. {
  230. static_cast<PANEL_SETUP_TEXT_AND_GRAPHICS*>( m_treebook->ResolvePage( m_textAndGraphicsPage ) )
  231. ->ImportSettingsFrom( otherBoard );
  232. }
  233. if( importDlg.m_FormattingOpt->GetValue() )
  234. {
  235. static_cast<PANEL_SETUP_FORMATTING*>( m_treebook->ResolvePage( m_formattingPage ) )
  236. ->ImportSettingsFrom( otherBoard );
  237. }
  238. if( importDlg.m_ConstraintsOpt->GetValue() )
  239. {
  240. static_cast<PANEL_SETUP_CONSTRAINTS*>( m_treebook->ResolvePage( m_constraintsPage ) )
  241. ->ImportSettingsFrom( otherBoard );
  242. }
  243. if( importDlg.m_NetclassesOpt->GetValue() )
  244. {
  245. static_cast<PANEL_SETUP_NETCLASSES*>( m_treebook->ResolvePage( m_netclassesPage ) )
  246. ->ImportSettingsFrom( otherPrj->GetProjectFile().m_NetSettings );
  247. }
  248. if( importDlg.m_TracksAndViasOpt->GetValue() )
  249. {
  250. static_cast<PANEL_SETUP_TRACKS_AND_VIAS*>( m_treebook->ResolvePage( m_tracksAndViasPage ) )
  251. ->ImportSettingsFrom( otherBoard );
  252. }
  253. if( importDlg.m_MaskAndPasteOpt->GetValue() )
  254. {
  255. static_cast<PANEL_SETUP_MASK_AND_PASTE*>( m_treebook->ResolvePage( m_maskAndPagePage ) )
  256. ->ImportSettingsFrom( otherBoard );
  257. }
  258. if( importDlg.m_SeveritiesOpt->GetValue() )
  259. {
  260. static_cast<PANEL_SETUP_SEVERITIES*>( m_treebook->ResolvePage( m_severitiesPage ) )
  261. ->ImportSettingsFrom( otherBoard->GetDesignSettings().m_DRCSeverities );
  262. }
  263. if( otherPrj != &m_frame->Prj() )
  264. otherBoard->ClearProject();
  265. }
  266. // Clean up and free memory before leaving
  267. if( otherPrj != &m_frame->Prj() )
  268. m_frame->GetSettingsManager()->UnloadProject( otherPrj, false );
  269. delete otherBoard;
  270. }