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.

766 lines
25 KiB

8 months ago
  1. /**
  2. * @file sel_layer.cpp
  3. * @brief minor dialogs for one layer selection and a layer pair selection.
  4. */
  5. /*
  6. * This program source code file is part of KiCad, a free EDA CAD application.
  7. *
  8. * Copyright (C) 2013 Jean-Pierre Charras, jp.charras at wanadoo.fr
  9. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  10. *
  11. * This program is free software: you can redistribute it and/or modify it
  12. * under the terms of the GNU General Public License as published by the
  13. * Free Software Foundation, either version 3 of the License, or (at your
  14. * option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful, but
  17. * WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License along
  22. * with this program. If not, see <http://www.gnu.org/licenses/>.
  23. */
  24. #include <wx/bitmap.h>
  25. #include <kiplatform/ui.h>
  26. #include <confirm.h>
  27. #include <lset.h>
  28. #include <board.h>
  29. #include <pgm_base.h>
  30. #include <project.h>
  31. #include <pcb_base_frame.h>
  32. #include <pcb_layer_presentation.h>
  33. #include <footprint_editor_settings.h>
  34. #include <layer_pairs.h>
  35. #include <dialogs/dialog_layer_selection_base.h>
  36. #include <project/project_file.h>
  37. #include <router/router_tool.h>
  38. #include <settings/settings_manager.h>
  39. #include <settings/color_settings.h>
  40. #include <tools/pcb_actions.h>
  41. #include <widgets/grid_icon_text_helpers.h>
  42. #include <widgets/grid_text_helpers.h>
  43. #include <widgets/layer_box_selector.h>
  44. #include <widgets/wx_grid.h>
  45. #include <widgets/wx_grid_autosizer.h>
  46. #include <widgets/std_bitmap_button.h>
  47. // Column position by function:
  48. #define SELECT_COLNUM 0
  49. #define COLOR_COLNUM 1
  50. #define LAYERNAME_COLNUM 2
  51. #define LAYER_HK_COLUMN 3
  52. PCB_LAYER_PRESENTATION::PCB_LAYER_PRESENTATION( PCB_BASE_FRAME* aFrame ) : m_boardFrame( aFrame )
  53. {
  54. }
  55. COLOR4D PCB_LAYER_PRESENTATION::getLayerColor( int aLayer ) const
  56. {
  57. if( m_boardFrame )
  58. {
  59. return m_boardFrame->GetColorSettings()->GetColor( aLayer );
  60. }
  61. else
  62. {
  63. FOOTPRINT_EDITOR_SETTINGS* cfg = GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" );
  64. COLOR_SETTINGS* current = ::GetColorSettings( cfg ? cfg->m_ColorTheme : DEFAULT_THEME );
  65. return current->GetColor( aLayer );
  66. }
  67. }
  68. wxString PCB_LAYER_PRESENTATION::getLayerName( int aLayer ) const
  69. {
  70. if( m_boardFrame )
  71. return m_boardFrame->GetBoard()->GetLayerName( ToLAYER_ID( aLayer ) );
  72. else
  73. return BOARD::GetStandardLayerName( ToLAYER_ID( aLayer ) );
  74. }
  75. LSEQ PCB_LAYER_PRESENTATION::getOrderedEnabledLayers() const
  76. {
  77. return m_boardFrame->GetBoard()->GetEnabledLayers().UIOrder();
  78. }
  79. wxString PCB_LAYER_PRESENTATION::getLayerPairName( const LAYER_PAIR& aPair ) const
  80. {
  81. const wxString layerAName = getLayerName( aPair.GetLayerA() );
  82. const wxString layerBName = getLayerName( aPair.GetLayerB() );
  83. return layerAName + wxT( " / " ) + layerBName;
  84. }
  85. /**
  86. * Display a PCB layers list in a dialog to select one layer from this list.
  87. */
  88. class PCB_ONE_LAYER_SELECTOR : public DIALOG_LAYER_SELECTION_BASE
  89. {
  90. public:
  91. PCB_ONE_LAYER_SELECTOR( PCB_BASE_FRAME* aParent, PCB_LAYER_ID aDefaultLayer,
  92. LSET aNotAllowedLayersMask, bool aHideCheckBoxes = false );
  93. ~PCB_ONE_LAYER_SELECTOR();
  94. int GetLayerSelection() { return m_layerSelected; }
  95. private:
  96. // Event handlers
  97. void OnLeftGridCellClick( wxGridEvent& aEvent ) override;
  98. void OnRightGridCellClick( wxGridEvent& aEvent ) override;
  99. void OnMouseMove( wxUpdateUIEvent& aEvent ) override;
  100. // Will close the dialog on ESC key
  101. void onCharHook( wxKeyEvent& event );
  102. wxString getLayerHotKey( PCB_LAYER_ID aLayer ) const
  103. {
  104. int code = PCB_ACTIONS::LayerIDToAction( aLayer )->GetHotKey();
  105. return AddHotkeyName( wxS( "" ), code, IS_COMMENT );
  106. }
  107. void buildList();
  108. PCB_LAYER_PRESENTATION m_layerPresentation;
  109. PCB_LAYER_ID m_layerSelected;
  110. LSET m_notAllowedLayersMask;
  111. std::vector<PCB_LAYER_ID> m_layersIdLeftColumn;
  112. std::vector<PCB_LAYER_ID> m_layersIdRightColumn;
  113. };
  114. PCB_ONE_LAYER_SELECTOR::PCB_ONE_LAYER_SELECTOR( PCB_BASE_FRAME* aParent, PCB_LAYER_ID aDefaultLayer,
  115. LSET aNotAllowedLayersMask, bool aHideCheckBoxes ) :
  116. DIALOG_LAYER_SELECTION_BASE( aParent ), m_layerPresentation( aParent )
  117. {
  118. m_useCalculatedSize = true;
  119. m_layerSelected = aDefaultLayer;
  120. m_notAllowedLayersMask = aNotAllowedLayersMask;
  121. m_leftGridLayers->SetCellHighlightPenWidth( 0 );
  122. m_rightGridLayers->SetCellHighlightPenWidth( 0 );
  123. m_leftGridLayers->SetColFormatBool( SELECT_COLNUM );
  124. m_rightGridLayers->SetColFormatBool( SELECT_COLNUM );
  125. m_leftGridLayers->AppendCols( 1 );
  126. buildList();
  127. if( aHideCheckBoxes )
  128. {
  129. m_leftGridLayers->HideCol( SELECT_COLNUM );
  130. m_rightGridLayers->HideCol( SELECT_COLNUM );
  131. }
  132. Connect( wxEVT_CHAR_HOOK, wxKeyEventHandler( PCB_ONE_LAYER_SELECTOR::onCharHook ) );
  133. Layout();
  134. GetSizer()->SetSizeHints( this );
  135. SetFocus();
  136. }
  137. PCB_ONE_LAYER_SELECTOR::~PCB_ONE_LAYER_SELECTOR()
  138. {
  139. Disconnect( wxEVT_CHAR_HOOK, wxKeyEventHandler( PCB_ONE_LAYER_SELECTOR::onCharHook ) );
  140. }
  141. void PCB_ONE_LAYER_SELECTOR::OnMouseMove( wxUpdateUIEvent& aEvent )
  142. {
  143. /// We have to assign this in UpdateUI events because the wxGrid is not properly receiving
  144. /// MouseMove events. It seems to only get them on the edges. So, for now we use this
  145. /// workaround
  146. wxPoint mouse_pos = KIPLATFORM::UI::GetMousePosition();
  147. wxPoint left_pos = m_leftGridLayers->ScreenToClient( mouse_pos );
  148. wxPoint right_pos = m_rightGridLayers->ScreenToClient( mouse_pos );
  149. if( m_leftGridLayers->HitTest( left_pos ) == wxHT_WINDOW_INSIDE )
  150. {
  151. int row = m_leftGridLayers->YToRow( left_pos.y );
  152. if( row != wxNOT_FOUND && row < static_cast<int>( m_layersIdLeftColumn.size() ) )
  153. {
  154. m_layerSelected = m_layersIdLeftColumn[row];
  155. m_leftGridLayers->SelectBlock( row, LAYERNAME_COLNUM, row, LAYER_HK_COLUMN );
  156. return;
  157. }
  158. }
  159. if( m_rightGridLayers->HitTest( right_pos ) == wxHT_WINDOW_INSIDE )
  160. {
  161. int row = m_rightGridLayers->YToRow( right_pos.y );
  162. if( row == wxNOT_FOUND || row >= static_cast<int>( m_layersIdRightColumn.size() ) )
  163. return;
  164. m_layerSelected = m_layersIdRightColumn[row];
  165. m_rightGridLayers->SelectBlock( row, LAYERNAME_COLNUM, row, LAYERNAME_COLNUM );
  166. }
  167. }
  168. void PCB_ONE_LAYER_SELECTOR::onCharHook( wxKeyEvent& event )
  169. {
  170. if( event.GetKeyCode() == WXK_ESCAPE )
  171. Close();
  172. }
  173. void PCB_ONE_LAYER_SELECTOR::buildList()
  174. {
  175. wxColour bg = m_layerPresentation.getLayerColor( LAYER_PCB_BACKGROUND ).ToColour();
  176. int left_row = 0;
  177. int right_row = 0;
  178. wxString layername;
  179. for( PCB_LAYER_ID layerid : m_layerPresentation.getOrderedEnabledLayers() )
  180. {
  181. if( m_notAllowedLayersMask[layerid] )
  182. continue;
  183. wxColour fg = m_layerPresentation.getLayerColor( layerid ).ToColour();
  184. wxColour color( wxColour::AlphaBlend( fg.Red(), bg.Red(), fg.Alpha() / 255.0 ),
  185. wxColour::AlphaBlend( fg.Green(), bg.Green(), fg.Alpha() / 255.0 ),
  186. wxColour::AlphaBlend( fg.Blue(), bg.Blue(), fg.Alpha() / 255.0 ) );
  187. layername = wxT( " " ) + m_layerPresentation.getLayerName( layerid );
  188. if( IsCopperLayer( layerid ) )
  189. {
  190. if( left_row )
  191. m_leftGridLayers->AppendRows( 1 );
  192. m_leftGridLayers->SetCellBackgroundColour( left_row, COLOR_COLNUM, color );
  193. m_leftGridLayers->SetCellValue( left_row, LAYERNAME_COLNUM, layername );
  194. m_leftGridLayers->SetCellValue( left_row, LAYER_HK_COLUMN, getLayerHotKey( layerid ) );
  195. if( m_layerSelected == layerid )
  196. m_leftGridLayers->SetCellValue( left_row, SELECT_COLNUM, wxT( "1" ) );
  197. m_layersIdLeftColumn.push_back( layerid );
  198. left_row++;
  199. }
  200. else
  201. {
  202. if( right_row )
  203. m_rightGridLayers->AppendRows( 1 );
  204. m_rightGridLayers->SetCellBackgroundColour( right_row, COLOR_COLNUM, color );
  205. m_rightGridLayers->SetCellValue( right_row, LAYERNAME_COLNUM, layername );
  206. if( m_layerSelected == layerid )
  207. m_rightGridLayers->SetCellValue( right_row, SELECT_COLNUM, wxT( "1" ) );
  208. m_layersIdRightColumn.push_back( layerid );
  209. right_row++;
  210. }
  211. }
  212. // Show only populated lists:
  213. if( left_row <= 0 )
  214. m_leftGridLayers->Show( false );
  215. if( right_row <= 0 )
  216. m_rightGridLayers->Show( false );
  217. // Now fix min grid column size (it also sets a minimal size)
  218. m_leftGridLayers->AutoSizeColumns();
  219. m_rightGridLayers->AutoSizeColumns();
  220. }
  221. void PCB_ONE_LAYER_SELECTOR::OnLeftGridCellClick( wxGridEvent& event )
  222. {
  223. m_layerSelected = m_layersIdLeftColumn[event.GetRow()];
  224. if( IsQuasiModal() )
  225. EndQuasiModal( 1 );
  226. else
  227. EndDialog( 1 );
  228. }
  229. void PCB_ONE_LAYER_SELECTOR::OnRightGridCellClick( wxGridEvent& event )
  230. {
  231. m_layerSelected = m_layersIdRightColumn[event.GetRow()];
  232. if( IsQuasiModal() )
  233. EndQuasiModal( 2 );
  234. else
  235. EndDialog( 2 );
  236. }
  237. PCB_LAYER_ID PCB_BASE_FRAME::SelectOneLayer( PCB_LAYER_ID aDefaultLayer,
  238. const LSET& aNotAllowedLayersMask,
  239. wxPoint aDlgPosition )
  240. {
  241. PCB_ONE_LAYER_SELECTOR dlg( this, aDefaultLayer, aNotAllowedLayersMask, true );
  242. if( aDlgPosition != wxDefaultPosition )
  243. {
  244. wxSize dlgSize = dlg.GetSize();
  245. aDlgPosition.x -= dlgSize.x / 2;
  246. aDlgPosition.y -= dlgSize.y / 2;
  247. dlg.SetPosition( aDlgPosition );
  248. }
  249. if( dlg.ShowModal() != wxID_CANCEL )
  250. return ToLAYER_ID( dlg.GetLayerSelection() );
  251. else
  252. return UNDEFINED_LAYER;
  253. }
  254. /**
  255. * Class that manages the UI for the copper layer pair presets list
  256. * based on an injected layer pair store.
  257. */
  258. class COPPER_LAYERS_PAIR_PRESETS_UI
  259. {
  260. enum class COLNUMS
  261. {
  262. ENABLED,
  263. SWATCH,
  264. LAYERNAMES,
  265. USERNAME,
  266. };
  267. public:
  268. COPPER_LAYERS_PAIR_PRESETS_UI( WX_GRID& aGrid, PCB_LAYER_PRESENTATION& aPresentation,
  269. LAYER_PAIR_SETTINGS& aLayerPairSettings ) :
  270. m_layerPresentation( aPresentation ), m_grid( aGrid ),
  271. m_layerPairSettings( aLayerPairSettings )
  272. {
  273. wxASSERT_MSG( m_grid.GetNumberRows() == 0, "Grid should be empty at controller start" );
  274. configureGrid();
  275. fillGridFromStore();
  276. m_grid.Bind( wxEVT_GRID_CELL_CHANGED,
  277. [this]( wxGridEvent& aEvent )
  278. {
  279. const int col = aEvent.GetCol();
  280. const int row = aEvent.GetRow();
  281. if( col == (int) COLNUMS::USERNAME )
  282. {
  283. onUserNameChanged( row, m_grid.GetCellValue( row, col ) );
  284. }
  285. else if( col == (int) COLNUMS::ENABLED )
  286. {
  287. onEnableChanged( row, m_grid.GetCellValue( row, col ) == wxS( "1" ) );
  288. }
  289. } );
  290. m_grid.Bind( wxEVT_GRID_CELL_LEFT_DCLICK,
  291. [&]( wxGridEvent& aEvent )
  292. {
  293. const int row = aEvent.GetRow();
  294. const int col = aEvent.GetCol();
  295. if( col == (int) COLNUMS::LAYERNAMES || col == (int) COLNUMS::SWATCH )
  296. {
  297. onPairActivated( row );
  298. }
  299. } );
  300. m_autosizer =
  301. std::make_unique<WX_GRID_AUTOSIZER>( m_grid,
  302. WX_GRID_AUTOSIZER::COL_MIN_WIDTHS{
  303. { (int) COLNUMS::LAYERNAMES, 72 },
  304. { (int) COLNUMS::USERNAME, 72 },
  305. },
  306. (int) COLNUMS::USERNAME );
  307. }
  308. void OnLayerPairAdded( const LAYER_PAIR& aLayerPair )
  309. {
  310. LAYER_PAIR_INFO layerPairInfo{ aLayerPair, true, std::nullopt };
  311. const bool added = m_layerPairSettings.AddLayerPair( layerPairInfo );
  312. if( added )
  313. {
  314. m_grid.AppendRows( 1 );
  315. fillRowFromLayerPair( m_grid.GetNumberRows() - 1, layerPairInfo );
  316. }
  317. }
  318. void OnDeleteSelectedLayerPairs()
  319. {
  320. m_grid.OnDeleteRows(
  321. [&]( int row )
  322. {
  323. const LAYER_PAIR_INFO& layerPairInfo = m_layerPairSettings.GetLayerPairs()[row];
  324. if( m_layerPairSettings.RemoveLayerPair( layerPairInfo.GetLayerPair() ) )
  325. m_grid.DeleteRows( row );
  326. } );
  327. }
  328. private:
  329. void configureGrid()
  330. {
  331. m_grid.UseNativeColHeader( true );
  332. m_grid.SetCellHighlightPenWidth( 0 );
  333. m_grid.SetColFormatBool( (int) COLNUMS::ENABLED );
  334. m_grid.SetSelectionMode( wxGrid::wxGridSelectionModes::wxGridSelectRows );
  335. m_grid.AutoSizeColumn( (int) COLNUMS::USERNAME );
  336. }
  337. void fillGridFromStore()
  338. {
  339. std::span<const LAYER_PAIR_INFO> storePairs = m_layerPairSettings.GetLayerPairs();
  340. m_grid.AppendRows( storePairs.size() );
  341. int row = 0;
  342. for( const LAYER_PAIR_INFO& layerPairInfo : storePairs )
  343. {
  344. fillRowFromLayerPair( row, layerPairInfo );
  345. row++;
  346. }
  347. }
  348. void fillRowFromLayerPair( int aRow, const LAYER_PAIR_INFO& aLayerPairInfo )
  349. {
  350. wxASSERT_MSG( aRow < m_grid.GetNumberRows(), "Row index out of bounds" );
  351. const LAYER_PAIR& layerPair = aLayerPairInfo.GetLayerPair();
  352. const wxString layerNames = m_layerPresentation.getLayerPairName( layerPair );
  353. m_grid.SetCellValue( aRow, (int) COLNUMS::LAYERNAMES, layerNames );
  354. const std::optional<wxString> userName = aLayerPairInfo.GetName();
  355. if( userName )
  356. {
  357. m_grid.SetCellValue( aRow, (int) COLNUMS::USERNAME, *userName );
  358. }
  359. m_grid.SetCellValue( aRow, (int) COLNUMS::ENABLED,
  360. aLayerPairInfo.IsEnabled() ? wxT( "1" ) : wxT( "0" ) );
  361. // Set the color swatch
  362. std::unique_ptr<wxBitmap>& swatch =
  363. m_swatches.emplace_back( m_layerPresentation.CreateLayerPairIcon(
  364. layerPair.GetLayerA(), layerPair.GetLayerB(), KiIconScale( &m_grid ) ) );
  365. m_grid.SetCellRenderer( aRow, (int) COLNUMS::SWATCH,
  366. new GRID_CELL_ICON_RENDERER( *swatch ) );
  367. m_grid.SetReadOnly( aRow, (int) COLNUMS::SWATCH );
  368. m_grid.SetReadOnly( aRow, (int) COLNUMS::LAYERNAMES );
  369. }
  370. void onUserNameChanged( int aRow, const wxString& aNewValue )
  371. {
  372. LAYER_PAIR_INFO& changedPair = m_layerPairSettings.GetLayerPairs()[aRow];
  373. changedPair.SetName( aNewValue );
  374. }
  375. void onEnableChanged( int aRow, bool aNewValue )
  376. {
  377. LAYER_PAIR_INFO& changedPair = m_layerPairSettings.GetLayerPairs()[aRow];
  378. changedPair.SetEnabled( aNewValue );
  379. }
  380. void onPairActivated( int aRow )
  381. {
  382. const LAYER_PAIR_INFO& layerPairInfo = m_layerPairSettings.GetLayerPairs()[aRow];
  383. const LAYER_PAIR& layerPair = layerPairInfo.GetLayerPair();
  384. m_layerPairSettings.SetCurrentLayerPair( layerPair );
  385. }
  386. PCB_LAYER_PRESENTATION& m_layerPresentation;
  387. WX_GRID& m_grid;
  388. LAYER_PAIR_SETTINGS& m_layerPairSettings;
  389. // Lifetime managment of the swatches
  390. std::vector<std::unique_ptr<wxBitmap>> m_swatches;
  391. std::unique_ptr<WX_GRID_AUTOSIZER> m_autosizer;
  392. };
  393. /**
  394. * Class that manages the UI for the copper layer pair selection
  395. * (left and right grids).
  396. */
  397. class COPPER_LAYERS_PAIR_SELECTION_UI
  398. {
  399. enum class CU_LAYER_COLNUMS
  400. {
  401. SELECT = 0,
  402. COLOR = 1,
  403. LAYERNAME = 2,
  404. };
  405. public:
  406. COPPER_LAYERS_PAIR_SELECTION_UI( wxGrid& aLeftGrid, wxGrid& aRightGrid,
  407. PCB_LAYER_PRESENTATION& aPresentation,
  408. LAYER_PAIR_SETTINGS& aLayerPairSettings ) :
  409. m_layerPresentation( aPresentation ), m_layerPairSettings( aLayerPairSettings ),
  410. m_leftGrid( aLeftGrid ), m_rightGrid( aRightGrid )
  411. {
  412. configureGrid( m_leftGrid );
  413. configureGrid( m_rightGrid );
  414. for( const PCB_LAYER_ID& layerId : m_layerPresentation.getOrderedEnabledLayers() )
  415. {
  416. if( IsCopperLayer( layerId ) )
  417. m_layersId.push_back( layerId );
  418. }
  419. fillLayerGrid( m_leftGrid );
  420. fillLayerGrid( m_rightGrid );
  421. m_leftGrid.Bind( wxEVT_GRID_CELL_LEFT_CLICK,
  422. [this]( wxGridEvent& aEvent )
  423. {
  424. onLeftGridRowSelected( aEvent.GetRow() );
  425. } );
  426. m_rightGrid.Bind( wxEVT_GRID_CELL_LEFT_CLICK,
  427. [this]( wxGridEvent& aEvent )
  428. {
  429. onRightGridRowSelected( aEvent.GetRow() );
  430. } );
  431. m_layerPairSettings.Bind( PCB_CURRENT_LAYER_PAIR_CHANGED,
  432. [this]( wxCommandEvent& aEvent )
  433. {
  434. const LAYER_PAIR& newPair = m_layerPairSettings.GetCurrentLayerPair();
  435. setCurrentSelection( rowForLayer( newPair.GetLayerA() ),
  436. rowForLayer( newPair.GetLayerB() ) );
  437. } );
  438. }
  439. private:
  440. void configureGrid( wxGrid& aGrid )
  441. {
  442. aGrid.SetCellHighlightPenWidth( 0 );
  443. aGrid.SetColFormatBool( (int) CU_LAYER_COLNUMS::SELECT );
  444. }
  445. void fillLayerGrid( wxGrid& aGrid )
  446. {
  447. const wxColour bg = m_layerPresentation.getLayerColor( LAYER_PCB_BACKGROUND ).ToColour();
  448. aGrid.AppendRows( m_layersId.size() - 1 );
  449. int row = 0;
  450. for( const PCB_LAYER_ID& layerId : m_layersId )
  451. {
  452. const wxColour fg = m_layerPresentation.getLayerColor( layerId ).ToColour();
  453. const wxColour color( wxColour::AlphaBlend( fg.Red(), bg.Red(), fg.Alpha() / 255.0 ),
  454. wxColour::AlphaBlend( fg.Green(), bg.Green(), fg.Alpha() / 255.0 ),
  455. wxColour::AlphaBlend( fg.Blue(), bg.Blue(), fg.Alpha() / 255.0 ) );
  456. const wxString layerName = wxT( " " ) + m_layerPresentation.getLayerName( layerId );
  457. aGrid.SetCellBackgroundColour( row, (int) CU_LAYER_COLNUMS::COLOR, color );
  458. aGrid.SetCellValue( row, (int) CU_LAYER_COLNUMS::LAYERNAME, layerName );
  459. row++;
  460. };
  461. // Now fix min grid layer name column size (it also sets a minimal size)
  462. aGrid.AutoSizeColumn( (int) CU_LAYER_COLNUMS::LAYERNAME );
  463. }
  464. PCB_LAYER_ID layerForRow( int aRow ) { return m_layersId.at( aRow ); }
  465. int rowForLayer( PCB_LAYER_ID aLayerId )
  466. {
  467. for( unsigned i = 0; i < m_layersId.size(); ++i )
  468. {
  469. if( m_layersId[i] == aLayerId )
  470. return i;
  471. }
  472. wxASSERT_MSG( false, wxString::Format( "Unknown layer in grid: %d", aLayerId ) );
  473. return 0;
  474. }
  475. void onLeftGridRowSelected( int aRow )
  476. {
  477. LAYER_PAIR newPair{
  478. layerForRow( aRow ),
  479. layerForRow( m_rightCurrRow ),
  480. };
  481. setCurrentSelection( aRow, m_rightCurrRow );
  482. m_layerPairSettings.SetCurrentLayerPair( newPair );
  483. }
  484. void onRightGridRowSelected( int aRow )
  485. {
  486. LAYER_PAIR newPair{
  487. layerForRow( m_leftCurrRow ),
  488. layerForRow( aRow ),
  489. };
  490. setCurrentSelection( m_leftCurrRow, aRow );
  491. m_layerPairSettings.SetCurrentLayerPair( newPair );
  492. }
  493. /**
  494. * Set the current layer selection.
  495. *
  496. * The layer pair must be copper layers that the selector this class
  497. * was constructed with knows about.
  498. */
  499. void setCurrentSelection( int aLeftRow, int aRightRow )
  500. {
  501. const auto selectGridRow = []( wxGrid& aGrid, int aRow, bool aSelect )
  502. {
  503. // At start, there is no old row
  504. if( aRow < 0 )
  505. return;
  506. const wxString val = aSelect ? wxT( "1" ) : wxEmptyString;
  507. aGrid.SetCellValue( aRow, (int) CU_LAYER_COLNUMS::SELECT, val );
  508. aGrid.SetGridCursor( aRow, (int) CU_LAYER_COLNUMS::COLOR );
  509. };
  510. if( m_leftCurrRow != aLeftRow )
  511. {
  512. selectGridRow( m_leftGrid, m_leftCurrRow, false );
  513. selectGridRow( m_leftGrid, aLeftRow, true );
  514. m_leftCurrRow = aLeftRow;
  515. }
  516. if( m_rightCurrRow != aRightRow )
  517. {
  518. selectGridRow( m_rightGrid, m_rightCurrRow, false );
  519. selectGridRow( m_rightGrid, aRightRow, true );
  520. m_rightCurrRow = aRightRow;
  521. }
  522. }
  523. PCB_LAYER_PRESENTATION& m_layerPresentation;
  524. LAYER_PAIR_SETTINGS& m_layerPairSettings;
  525. std::vector<PCB_LAYER_ID> m_layersId;
  526. wxGrid& m_leftGrid;
  527. wxGrid& m_rightGrid;
  528. int m_leftCurrRow = -1;
  529. int m_rightCurrRow = -1;
  530. };
  531. /**
  532. * Display a pair PCB copper layers list in a dialog to select a layer pair from these lists.
  533. *
  534. * This is a higher level class that mostly glues together other controller classes and UI
  535. * elements.
  536. */
  537. class SELECT_COPPER_LAYERS_PAIR_DIALOG : public DIALOG_COPPER_LAYER_PAIR_SELECTION_BASE
  538. {
  539. public:
  540. SELECT_COPPER_LAYERS_PAIR_DIALOG( PCB_BASE_FRAME& aParent,
  541. LAYER_PAIR_SETTINGS& aBoardSettings ) :
  542. DIALOG_COPPER_LAYER_PAIR_SELECTION_BASE( &aParent ),
  543. m_boardPairSettings( aBoardSettings ), m_dialogPairSettings( aBoardSettings ),
  544. m_layerPresentation( &aParent ),
  545. m_pairSelectionController( *m_leftGridLayers, *m_rightGridLayers, m_layerPresentation,
  546. m_dialogPairSettings ),
  547. m_presetsGridController( *m_presetsGrid, m_layerPresentation, m_dialogPairSettings )
  548. {
  549. m_addToPresetsButton->SetBitmap( KiBitmapBundle( BITMAPS::right ) );
  550. m_deleteRowButton->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
  551. m_addToPresetsButton->Bind( wxEVT_BUTTON,
  552. [this]( wxCommandEvent& aEvent )
  553. {
  554. const LAYER_PAIR newPair = m_dialogPairSettings.GetCurrentLayerPair();
  555. m_presetsGridController.OnLayerPairAdded( newPair );
  556. } );
  557. m_deleteRowButton->Bind( wxEVT_BUTTON,
  558. [this]( wxCommandEvent& aEvent )
  559. {
  560. m_presetsGridController.OnDeleteSelectedLayerPairs();
  561. } );
  562. SetFocus();
  563. GetSizer()->SetSizeHints( this );
  564. Center();
  565. }
  566. bool TransferDataToWindow() override
  567. {
  568. m_presetsGrid->Freeze();
  569. m_leftGridLayers->Freeze();
  570. m_rightGridLayers->Freeze();
  571. m_dialogPairSettings.SetCurrentLayerPair( m_boardPairSettings.GetCurrentLayerPair() );
  572. m_rightGridLayers->Thaw();
  573. m_leftGridLayers->Thaw();
  574. m_presetsGrid->Thaw();
  575. return true;
  576. }
  577. bool TransferDataFromWindow() override
  578. {
  579. // Pull out the dialog's stored pairs
  580. std::span<const LAYER_PAIR_INFO> storePairs = m_dialogPairSettings.GetLayerPairs();
  581. m_boardPairSettings.SetLayerPairs( storePairs );
  582. m_boardPairSettings.SetCurrentLayerPair( m_dialogPairSettings.GetCurrentLayerPair() );
  583. return true;
  584. }
  585. private:
  586. // The BOARD's pair store to be updated
  587. LAYER_PAIR_SETTINGS& m_boardPairSettings;
  588. // A local copy while we modify it
  589. LAYER_PAIR_SETTINGS m_dialogPairSettings;
  590. // Information about the layer presentation (colors, etc)
  591. PCB_LAYER_PRESENTATION m_layerPresentation;
  592. // UI controllers
  593. COPPER_LAYERS_PAIR_SELECTION_UI m_pairSelectionController;
  594. COPPER_LAYERS_PAIR_PRESETS_UI m_presetsGridController;
  595. };
  596. int ROUTER_TOOL::SelectCopperLayerPair( const TOOL_EVENT& aEvent )
  597. {
  598. LAYER_PAIR_SETTINGS* const boardSettings = frame()->GetLayerPairSettings();
  599. if( !boardSettings )
  600. {
  601. // Should only be used for suitable frame types with layer pairs
  602. wxASSERT_MSG( false, "Could not access layer pair settings" );
  603. return 0;
  604. }
  605. SELECT_COPPER_LAYERS_PAIR_DIALOG dlg( *frame(), *boardSettings );
  606. if( dlg.ShowModal() == wxID_OK )
  607. {
  608. const LAYER_PAIR layerPair = boardSettings->GetCurrentLayerPair();
  609. // select the same layer for both layers is allowed (normal in some boards)
  610. // but could be a mistake. So display an info message
  611. if( layerPair.GetLayerA() == layerPair.GetLayerB() )
  612. {
  613. DisplayInfoMessage( frame(), _( "Warning: top and bottom layers are same." ) );
  614. }
  615. }
  616. return 0;
  617. }