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.
		
		
		
		
		
			
		
			
				
					
					
						
							774 lines
						
					
					
						
							25 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							774 lines
						
					
					
						
							25 KiB
						
					
					
				
								/**
							 | 
						|
								 * @file sel_layer.cpp
							 | 
						|
								 * @brief minor dialogs for one layer selection and a layer pair selection.
							 | 
						|
								 */
							 | 
						|
								/*
							 | 
						|
								 * This program source code file is part of KiCad, a free EDA CAD application.
							 | 
						|
								 *
							 | 
						|
								 * Copyright (C) 2013 Jean-Pierre Charras, jp.charras at wanadoo.fr
							 | 
						|
								 * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
							 | 
						|
								 *
							 | 
						|
								 * This program is free software: you can redistribute it and/or modify it
							 | 
						|
								 * under the terms of the GNU General Public License as published by the
							 | 
						|
								 * Free Software Foundation, either version 3 of the License, or (at your
							 | 
						|
								 * option) any later version.
							 | 
						|
								 *
							 | 
						|
								 * This program is distributed in the hope that it will be useful, but
							 | 
						|
								 * WITHOUT ANY WARRANTY; without even the implied warranty of
							 | 
						|
								 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
							 | 
						|
								 * General Public License for more details.
							 | 
						|
								 *
							 | 
						|
								 * You should have received a copy of the GNU General Public License along
							 | 
						|
								 * with this program.  If not, see <http://www.gnu.org/licenses/>.
							 | 
						|
								 */
							 | 
						|
								
							 | 
						|
								#include <wx/bitmap.h>
							 | 
						|
								
							 | 
						|
								#include <kiplatform/ui.h>
							 | 
						|
								#include <confirm.h>
							 | 
						|
								#include <lset.h>
							 | 
						|
								#include <board.h>
							 | 
						|
								#include <pgm_base.h>
							 | 
						|
								#include <project.h>
							 | 
						|
								#include <pcb_base_frame.h>
							 | 
						|
								#include <pcb_layer_presentation.h>
							 | 
						|
								#include <footprint_editor_settings.h>
							 | 
						|
								#include <layer_pairs.h>
							 | 
						|
								#include <dialogs/dialog_layer_selection_base.h>
							 | 
						|
								#include <project/project_file.h>
							 | 
						|
								#include <router/router_tool.h>
							 | 
						|
								#include <settings/settings_manager.h>
							 | 
						|
								#include <settings/color_settings.h>
							 | 
						|
								#include <tools/pcb_actions.h>
							 | 
						|
								#include <widgets/grid_icon_text_helpers.h>
							 | 
						|
								#include <widgets/grid_text_helpers.h>
							 | 
						|
								#include <widgets/layer_box_selector.h>
							 | 
						|
								#include <widgets/wx_grid.h>
							 | 
						|
								#include <widgets/wx_grid_autosizer.h>
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								// Column position by function:
							 | 
						|
								#define SELECT_COLNUM 0
							 | 
						|
								#define COLOR_COLNUM 1
							 | 
						|
								#define LAYERNAME_COLNUM 2
							 | 
						|
								#define LAYER_HK_COLUMN 3
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								PCB_LAYER_PRESENTATION::PCB_LAYER_PRESENTATION( PCB_BASE_FRAME* aFrame ) : m_boardFrame( aFrame )
							 | 
						|
								{
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								COLOR4D PCB_LAYER_PRESENTATION::getLayerColor( int aLayer ) const
							 | 
						|
								{
							 | 
						|
								    if( m_boardFrame )
							 | 
						|
								    {
							 | 
						|
								        return m_boardFrame->GetColorSettings()->GetColor( aLayer );
							 | 
						|
								    }
							 | 
						|
								    else
							 | 
						|
								    {
							 | 
						|
								        SETTINGS_MANAGER&          mgr = Pgm().GetSettingsManager();
							 | 
						|
								        FOOTPRINT_EDITOR_SETTINGS* settings = mgr.GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>();
							 | 
						|
								        COLOR_SETTINGS*            current = mgr.GetColorSettings( settings->m_ColorTheme );
							 | 
						|
								
							 | 
						|
								        return current->GetColor( aLayer );
							 | 
						|
								    }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								wxString PCB_LAYER_PRESENTATION::getLayerName( int aLayer ) const
							 | 
						|
								{
							 | 
						|
								    if( m_boardFrame )
							 | 
						|
								        return m_boardFrame->GetBoard()->GetLayerName( ToLAYER_ID( aLayer ) );
							 | 
						|
								    else
							 | 
						|
								        return BOARD::GetStandardLayerName( ToLAYER_ID( aLayer ) );
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								LSEQ PCB_LAYER_PRESENTATION::getOrderedEnabledLayers() const
							 | 
						|
								{
							 | 
						|
								    return m_boardFrame->GetBoard()->GetEnabledLayers().UIOrder();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								wxString PCB_LAYER_PRESENTATION::getLayerPairName( const LAYER_PAIR& aPair ) const
							 | 
						|
								{
							 | 
						|
								    const wxString layerAName = getLayerName( aPair.GetLayerA() );
							 | 
						|
								    const wxString layerBName = getLayerName( aPair.GetLayerB() );
							 | 
						|
								
							 | 
						|
								    return layerAName + wxT( " / " ) + layerBName;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Display a PCB layers list in a dialog to select one layer from this list.
							 | 
						|
								 */
							 | 
						|
								class PCB_ONE_LAYER_SELECTOR : public DIALOG_LAYER_SELECTION_BASE
							 | 
						|
								{
							 | 
						|
								public:
							 | 
						|
								    PCB_ONE_LAYER_SELECTOR( PCB_BASE_FRAME* aParent, PCB_LAYER_ID aDefaultLayer,
							 | 
						|
								                            LSET aNotAllowedLayersMask, bool aHideCheckBoxes = false );
							 | 
						|
								    ~PCB_ONE_LAYER_SELECTOR();
							 | 
						|
								
							 | 
						|
								    int GetLayerSelection() { return m_layerSelected; }
							 | 
						|
								
							 | 
						|
								private:
							 | 
						|
								    // Event handlers
							 | 
						|
								    void OnLeftGridCellClick( wxGridEvent& aEvent ) override;
							 | 
						|
								    void OnRightGridCellClick( wxGridEvent& aEvent ) override;
							 | 
						|
								    void OnMouseMove( wxUpdateUIEvent& aEvent ) override;
							 | 
						|
								
							 | 
						|
								    // Will close the dialog on ESC key
							 | 
						|
								    void onCharHook( wxKeyEvent& event );
							 | 
						|
								
							 | 
						|
								    wxString getLayerHotKey( PCB_LAYER_ID aLayer ) const
							 | 
						|
								    {
							 | 
						|
								        int code = PCB_ACTIONS::LayerIDToAction( aLayer )->GetHotKey();
							 | 
						|
								        return AddHotkeyName( wxS( "" ), code, IS_COMMENT );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    void buildList();
							 | 
						|
								
							 | 
						|
								    PCB_LAYER_PRESENTATION    m_layerPresentation;
							 | 
						|
								    PCB_LAYER_ID              m_layerSelected;
							 | 
						|
								    LSET                      m_notAllowedLayersMask;
							 | 
						|
								    std::vector<PCB_LAYER_ID> m_layersIdLeftColumn;
							 | 
						|
								    std::vector<PCB_LAYER_ID> m_layersIdRightColumn;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								PCB_ONE_LAYER_SELECTOR::PCB_ONE_LAYER_SELECTOR( PCB_BASE_FRAME* aParent, PCB_LAYER_ID aDefaultLayer,
							 | 
						|
								                                                LSET aNotAllowedLayersMask, bool aHideCheckBoxes ) :
							 | 
						|
								        DIALOG_LAYER_SELECTION_BASE( aParent ), m_layerPresentation( aParent )
							 | 
						|
								{
							 | 
						|
								    m_useCalculatedSize = true;
							 | 
						|
								
							 | 
						|
								    m_layerSelected = aDefaultLayer;
							 | 
						|
								    m_notAllowedLayersMask = aNotAllowedLayersMask;
							 | 
						|
								
							 | 
						|
								    m_leftGridLayers->SetCellHighlightPenWidth( 0 );
							 | 
						|
								    m_rightGridLayers->SetCellHighlightPenWidth( 0 );
							 | 
						|
								    m_leftGridLayers->SetColFormatBool( SELECT_COLNUM );
							 | 
						|
								    m_rightGridLayers->SetColFormatBool( SELECT_COLNUM );
							 | 
						|
								
							 | 
						|
								    m_leftGridLayers->AppendCols( 1 );
							 | 
						|
								
							 | 
						|
								    buildList();
							 | 
						|
								
							 | 
						|
								    if( aHideCheckBoxes )
							 | 
						|
								    {
							 | 
						|
								        m_leftGridLayers->HideCol( SELECT_COLNUM );
							 | 
						|
								        m_rightGridLayers->HideCol( SELECT_COLNUM );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    Connect( wxEVT_CHAR_HOOK, wxKeyEventHandler( PCB_ONE_LAYER_SELECTOR::onCharHook ) );
							 | 
						|
								
							 | 
						|
								    Layout();
							 | 
						|
								    GetSizer()->SetSizeHints( this );
							 | 
						|
								    SetFocus();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								PCB_ONE_LAYER_SELECTOR::~PCB_ONE_LAYER_SELECTOR()
							 | 
						|
								{
							 | 
						|
								    Disconnect( wxEVT_CHAR_HOOK, wxKeyEventHandler( PCB_ONE_LAYER_SELECTOR::onCharHook ) );
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								void PCB_ONE_LAYER_SELECTOR::OnMouseMove( wxUpdateUIEvent& aEvent )
							 | 
						|
								{
							 | 
						|
								    /// We have to assign this in UpdateUI events because the wxGrid is not properly receiving
							 | 
						|
								    /// MouseMove events.  It seems to only get them on the edges.  So, for now we use this
							 | 
						|
								    /// workaround
							 | 
						|
								
							 | 
						|
								    wxPoint mouse_pos = KIPLATFORM::UI::GetMousePosition();
							 | 
						|
								    wxPoint left_pos = m_leftGridLayers->ScreenToClient( mouse_pos );
							 | 
						|
								    wxPoint right_pos = m_rightGridLayers->ScreenToClient( mouse_pos );
							 | 
						|
								
							 | 
						|
								    if( m_leftGridLayers->HitTest( left_pos ) == wxHT_WINDOW_INSIDE )
							 | 
						|
								    {
							 | 
						|
								        int row = m_leftGridLayers->YToRow( left_pos.y );
							 | 
						|
								
							 | 
						|
								        if( row != wxNOT_FOUND && row < static_cast<int>( m_layersIdLeftColumn.size() ) )
							 | 
						|
								        {
							 | 
						|
								            m_layerSelected = m_layersIdLeftColumn[row];
							 | 
						|
								            m_leftGridLayers->SelectBlock( row, LAYERNAME_COLNUM, row, LAYER_HK_COLUMN );
							 | 
						|
								            return;
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    if( m_rightGridLayers->HitTest( right_pos ) == wxHT_WINDOW_INSIDE )
							 | 
						|
								    {
							 | 
						|
								        int row = m_rightGridLayers->YToRow( right_pos.y );
							 | 
						|
								
							 | 
						|
								        if( row == wxNOT_FOUND || row >= static_cast<int>( m_layersIdRightColumn.size() ) )
							 | 
						|
								            return;
							 | 
						|
								
							 | 
						|
								        m_layerSelected = m_layersIdRightColumn[row];
							 | 
						|
								        m_rightGridLayers->SelectBlock( row, LAYERNAME_COLNUM, row, LAYERNAME_COLNUM );
							 | 
						|
								    }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								void PCB_ONE_LAYER_SELECTOR::onCharHook( wxKeyEvent& event )
							 | 
						|
								{
							 | 
						|
								    if( event.GetKeyCode() == WXK_ESCAPE )
							 | 
						|
								        Close();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								void PCB_ONE_LAYER_SELECTOR::buildList()
							 | 
						|
								{
							 | 
						|
								    wxColour bg = m_layerPresentation.getLayerColor( LAYER_PCB_BACKGROUND ).ToColour();
							 | 
						|
								    int      left_row = 0;
							 | 
						|
								    int      right_row = 0;
							 | 
						|
								    wxString layername;
							 | 
						|
								
							 | 
						|
								    for( PCB_LAYER_ID layerid : m_layerPresentation.getOrderedEnabledLayers() )
							 | 
						|
								    {
							 | 
						|
								        if( m_notAllowedLayersMask[layerid] )
							 | 
						|
								            continue;
							 | 
						|
								
							 | 
						|
								        wxColour fg = m_layerPresentation.getLayerColor( layerid ).ToColour();
							 | 
						|
								        wxColour color( wxColour::AlphaBlend( fg.Red(), bg.Red(), fg.Alpha() / 255.0 ),
							 | 
						|
								                        wxColour::AlphaBlend( fg.Green(), bg.Green(), fg.Alpha() / 255.0 ),
							 | 
						|
								                        wxColour::AlphaBlend( fg.Blue(), bg.Blue(), fg.Alpha() / 255.0 ) );
							 | 
						|
								
							 | 
						|
								        layername = wxT( " " ) + m_layerPresentation.getLayerName( layerid );
							 | 
						|
								
							 | 
						|
								        if( IsCopperLayer( layerid ) )
							 | 
						|
								        {
							 | 
						|
								            if( left_row )
							 | 
						|
								                m_leftGridLayers->AppendRows( 1 );
							 | 
						|
								
							 | 
						|
								            m_leftGridLayers->SetCellBackgroundColour( left_row, COLOR_COLNUM, color );
							 | 
						|
								            m_leftGridLayers->SetCellValue( left_row, LAYERNAME_COLNUM, layername );
							 | 
						|
								            m_leftGridLayers->SetCellValue( left_row, LAYER_HK_COLUMN, getLayerHotKey( layerid ) );
							 | 
						|
								
							 | 
						|
								            if( m_layerSelected == layerid )
							 | 
						|
								                m_leftGridLayers->SetCellValue( left_row, SELECT_COLNUM, wxT( "1" ) );
							 | 
						|
								
							 | 
						|
								            m_layersIdLeftColumn.push_back( layerid );
							 | 
						|
								            left_row++;
							 | 
						|
								        }
							 | 
						|
								        else
							 | 
						|
								        {
							 | 
						|
								            if( right_row )
							 | 
						|
								                m_rightGridLayers->AppendRows( 1 );
							 | 
						|
								
							 | 
						|
								            m_rightGridLayers->SetCellBackgroundColour( right_row, COLOR_COLNUM, color );
							 | 
						|
								            m_rightGridLayers->SetCellValue( right_row, LAYERNAME_COLNUM, layername );
							 | 
						|
								
							 | 
						|
								            if( m_layerSelected == layerid )
							 | 
						|
								                m_rightGridLayers->SetCellValue( right_row, SELECT_COLNUM, wxT( "1" ) );
							 | 
						|
								
							 | 
						|
								            m_layersIdRightColumn.push_back( layerid );
							 | 
						|
								            right_row++;
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    // Show only populated lists:
							 | 
						|
								    if( left_row <= 0 )
							 | 
						|
								        m_leftGridLayers->Show( false );
							 | 
						|
								
							 | 
						|
								    if( right_row <= 0 )
							 | 
						|
								        m_rightGridLayers->Show( false );
							 | 
						|
								
							 | 
						|
								    // Now fix min grid column size (it also sets a minimal size)
							 | 
						|
								    m_leftGridLayers->AutoSizeColumns();
							 | 
						|
								    m_rightGridLayers->AutoSizeColumns();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								void PCB_ONE_LAYER_SELECTOR::OnLeftGridCellClick( wxGridEvent& event )
							 | 
						|
								{
							 | 
						|
								    m_layerSelected = m_layersIdLeftColumn[event.GetRow()];
							 | 
						|
								
							 | 
						|
								    if( IsQuasiModal() )
							 | 
						|
								        EndQuasiModal( 1 );
							 | 
						|
								    else
							 | 
						|
								        EndDialog( 1 );
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								void PCB_ONE_LAYER_SELECTOR::OnRightGridCellClick( wxGridEvent& event )
							 | 
						|
								{
							 | 
						|
								    m_layerSelected = m_layersIdRightColumn[event.GetRow()];
							 | 
						|
								
							 | 
						|
								    if( IsQuasiModal() )
							 | 
						|
								        EndQuasiModal( 2 );
							 | 
						|
								    else
							 | 
						|
								        EndDialog( 2 );
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								PCB_LAYER_ID PCB_BASE_FRAME::SelectOneLayer( PCB_LAYER_ID aDefaultLayer, LSET aNotAllowedLayersMask,
							 | 
						|
								                                             wxPoint aDlgPosition )
							 | 
						|
								{
							 | 
						|
								    PCB_ONE_LAYER_SELECTOR dlg( this, aDefaultLayer, aNotAllowedLayersMask, true );
							 | 
						|
								
							 | 
						|
								    if( aDlgPosition != wxDefaultPosition )
							 | 
						|
								    {
							 | 
						|
								        wxSize dlgSize = dlg.GetSize();
							 | 
						|
								        aDlgPosition.x -= dlgSize.x / 2;
							 | 
						|
								        aDlgPosition.y -= dlgSize.y / 2;
							 | 
						|
								        dlg.SetPosition( aDlgPosition );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    if( dlg.ShowModal() != wxID_CANCEL )
							 | 
						|
								        return ToLAYER_ID( dlg.GetLayerSelection() );
							 | 
						|
								    else
							 | 
						|
								        return UNDEFINED_LAYER;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Class that manages the UI for the copper layer pair presets list
							 | 
						|
								 * based on an injected layer pair store.
							 | 
						|
								 */
							 | 
						|
								class COPPER_LAYERS_PAIR_PRESETS_UI
							 | 
						|
								{
							 | 
						|
								    enum class COLNUMS
							 | 
						|
								    {
							 | 
						|
								        ENABLED,
							 | 
						|
								        SWATCH,
							 | 
						|
								        LAYERNAMES,
							 | 
						|
								        USERNAME,
							 | 
						|
								    };
							 | 
						|
								
							 | 
						|
								public:
							 | 
						|
								    COPPER_LAYERS_PAIR_PRESETS_UI( WX_GRID& aGrid, PCB_LAYER_PRESENTATION& aPresentation,
							 | 
						|
								                                   LAYER_PAIR_SETTINGS& aLayerPairSettings ) :
							 | 
						|
								            m_layerPresentation( aPresentation ), m_grid( aGrid ),
							 | 
						|
								            m_layerPairSettings( aLayerPairSettings )
							 | 
						|
								    {
							 | 
						|
								        wxASSERT_MSG( m_grid.GetNumberRows() == 0, "Grid should be empty at controller start" );
							 | 
						|
								
							 | 
						|
								        configureGrid();
							 | 
						|
								        fillGridFromStore();
							 | 
						|
								
							 | 
						|
								        m_grid.Bind( wxEVT_GRID_CELL_CHANGED,
							 | 
						|
								                     [this]( wxGridEvent& aEvent )
							 | 
						|
								                     {
							 | 
						|
								                         const int col = aEvent.GetCol();
							 | 
						|
								                         const int row = aEvent.GetRow();
							 | 
						|
								                         if( col == (int) COLNUMS::USERNAME )
							 | 
						|
								                         {
							 | 
						|
								                             onUserNameChanged( row, m_grid.GetCellValue( row, col ) );
							 | 
						|
								                         }
							 | 
						|
								                         else if( col == (int) COLNUMS::ENABLED )
							 | 
						|
								                         {
							 | 
						|
								                             onEnableChanged( row, m_grid.GetCellValue( row, col ) == wxS( "1" ) );
							 | 
						|
								                         }
							 | 
						|
								                     } );
							 | 
						|
								
							 | 
						|
								        m_grid.Bind( wxEVT_GRID_CELL_LEFT_DCLICK,
							 | 
						|
								                     [&]( wxGridEvent& aEvent )
							 | 
						|
								                     {
							 | 
						|
								                         const int row = aEvent.GetRow();
							 | 
						|
								                         const int col = aEvent.GetCol();
							 | 
						|
								
							 | 
						|
								                         if( col == (int) COLNUMS::LAYERNAMES || col == (int) COLNUMS::SWATCH )
							 | 
						|
								                         {
							 | 
						|
								                             onPairActivated( row );
							 | 
						|
								                         }
							 | 
						|
								                     } );
							 | 
						|
								
							 | 
						|
								        m_autosizer =
							 | 
						|
								                std::make_unique<WX_GRID_AUTOSIZER>( m_grid,
							 | 
						|
								                                                     WX_GRID_AUTOSIZER::COL_MIN_WIDTHS{
							 | 
						|
								                                                             { (int) COLNUMS::LAYERNAMES, 72 },
							 | 
						|
								                                                             { (int) COLNUMS::USERNAME, 72 },
							 | 
						|
								                                                     },
							 | 
						|
								                                                     (int) COLNUMS::USERNAME );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    void OnLayerPairAdded( const LAYER_PAIR& aLayerPair )
							 | 
						|
								    {
							 | 
						|
								        LAYER_PAIR_INFO layerPairInfo{ aLayerPair, true, std::nullopt };
							 | 
						|
								
							 | 
						|
								        const bool added = m_layerPairSettings.AddLayerPair( layerPairInfo );
							 | 
						|
								
							 | 
						|
								        if( added )
							 | 
						|
								        {
							 | 
						|
								            m_grid.AppendRows( 1 );
							 | 
						|
								            fillRowFromLayerPair( m_grid.GetNumberRows() - 1, layerPairInfo );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    void OnDeleteSelectedLayerPairs()
							 | 
						|
								    {
							 | 
						|
								        int row = m_grid.GetGridCursorRow();
							 | 
						|
								
							 | 
						|
								        const LAYER_PAIR_INFO& layerPairInfo = m_layerPairSettings.GetLayerPairs()[row];
							 | 
						|
								        const bool removed = m_layerPairSettings.RemoveLayerPair( layerPairInfo.GetLayerPair() );
							 | 
						|
								
							 | 
						|
								        if( removed )
							 | 
						|
								        {
							 | 
						|
								            m_grid.DeleteRows( row );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								private:
							 | 
						|
								    void configureGrid()
							 | 
						|
								    {
							 | 
						|
								        m_grid.UseNativeColHeader( true );
							 | 
						|
								
							 | 
						|
								        m_grid.SetCellHighlightPenWidth( 0 );
							 | 
						|
								        m_grid.SetColFormatBool( (int) COLNUMS::ENABLED );
							 | 
						|
								
							 | 
						|
								        m_grid.SetSelectionMode( wxGrid::wxGridSelectionModes::wxGridSelectRows );
							 | 
						|
								
							 | 
						|
								        m_grid.AutoSizeColumn( (int) COLNUMS::USERNAME );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    void fillGridFromStore()
							 | 
						|
								    {
							 | 
						|
								        std::span<const LAYER_PAIR_INFO> storePairs = m_layerPairSettings.GetLayerPairs();
							 | 
						|
								
							 | 
						|
								        m_grid.AppendRows( storePairs.size() );
							 | 
						|
								
							 | 
						|
								        int row = 0;
							 | 
						|
								        for( const LAYER_PAIR_INFO& layerPairInfo : storePairs )
							 | 
						|
								        {
							 | 
						|
								            fillRowFromLayerPair( row, layerPairInfo );
							 | 
						|
								            row++;
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    void fillRowFromLayerPair( int aRow, const LAYER_PAIR_INFO& aLayerPairInfo )
							 | 
						|
								    {
							 | 
						|
								        wxASSERT_MSG( aRow < m_grid.GetNumberRows(), "Row index out of bounds" );
							 | 
						|
								
							 | 
						|
								        const LAYER_PAIR& layerPair = aLayerPairInfo.GetLayerPair();
							 | 
						|
								
							 | 
						|
								        const wxString layerNames = m_layerPresentation.getLayerPairName( layerPair );
							 | 
						|
								
							 | 
						|
								        m_grid.SetCellValue( aRow, (int) COLNUMS::LAYERNAMES, layerNames );
							 | 
						|
								
							 | 
						|
								        const std::optional<wxString> userName = aLayerPairInfo.GetName();
							 | 
						|
								        if( userName )
							 | 
						|
								        {
							 | 
						|
								            m_grid.SetCellValue( aRow, (int) COLNUMS::USERNAME, *userName );
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        m_grid.SetCellValue( aRow, (int) COLNUMS::ENABLED,
							 | 
						|
								                             aLayerPairInfo.IsEnabled() ? wxT( "1" ) : wxT( "0" ) );
							 | 
						|
								
							 | 
						|
								        // Set the color swatch
							 | 
						|
								        std::unique_ptr<wxBitmap>& swatch =
							 | 
						|
								                m_swatches.emplace_back( m_layerPresentation.CreateLayerPairIcon(
							 | 
						|
								                        layerPair.GetLayerA(), layerPair.GetLayerB(), KiIconScale( &m_grid ) ) );
							 | 
						|
								
							 | 
						|
								        m_grid.SetCellRenderer( aRow, (int) COLNUMS::SWATCH,
							 | 
						|
								                                new GRID_CELL_ICON_RENDERER( *swatch ) );
							 | 
						|
								
							 | 
						|
								        m_grid.SetReadOnly( aRow, (int) COLNUMS::SWATCH );
							 | 
						|
								        m_grid.SetReadOnly( aRow, (int) COLNUMS::LAYERNAMES );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    void onUserNameChanged( int aRow, const wxString& aNewValue )
							 | 
						|
								    {
							 | 
						|
								        LAYER_PAIR_INFO& changedPair = m_layerPairSettings.GetLayerPairs()[aRow];
							 | 
						|
								        changedPair.SetName( aNewValue );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    void onEnableChanged( int aRow, bool aNewValue )
							 | 
						|
								    {
							 | 
						|
								        LAYER_PAIR_INFO& changedPair = m_layerPairSettings.GetLayerPairs()[aRow];
							 | 
						|
								        changedPair.SetEnabled( aNewValue );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    void onPairActivated( int aRow )
							 | 
						|
								    {
							 | 
						|
								        const LAYER_PAIR_INFO& layerPairInfo = m_layerPairSettings.GetLayerPairs()[aRow];
							 | 
						|
								        const LAYER_PAIR&      layerPair = layerPairInfo.GetLayerPair();
							 | 
						|
								
							 | 
						|
								        m_layerPairSettings.SetCurrentLayerPair( layerPair );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    PCB_LAYER_PRESENTATION& m_layerPresentation;
							 | 
						|
								    WX_GRID&                m_grid;
							 | 
						|
								    LAYER_PAIR_SETTINGS&    m_layerPairSettings;
							 | 
						|
								
							 | 
						|
								    // Lifetime managment of the swatches
							 | 
						|
								    std::vector<std::unique_ptr<wxBitmap>> m_swatches;
							 | 
						|
								
							 | 
						|
								    std::unique_ptr<WX_GRID_AUTOSIZER> m_autosizer;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Class that manages the UI for the copper layer pair selection
							 | 
						|
								 * (left and right grids).
							 | 
						|
								 */
							 | 
						|
								class COPPER_LAYERS_PAIR_SELECTION_UI
							 | 
						|
								{
							 | 
						|
								    enum class CU_LAYER_COLNUMS
							 | 
						|
								    {
							 | 
						|
								        SELECT = 0,
							 | 
						|
								        COLOR = 1,
							 | 
						|
								        LAYERNAME = 2,
							 | 
						|
								    };
							 | 
						|
								
							 | 
						|
								public:
							 | 
						|
								    COPPER_LAYERS_PAIR_SELECTION_UI( wxGrid& aLeftGrid, wxGrid& aRightGrid,
							 | 
						|
								                                     PCB_LAYER_PRESENTATION& aPresentation,
							 | 
						|
								                                     LAYER_PAIR_SETTINGS&    aLayerPairSettings ) :
							 | 
						|
								            m_layerPresentation( aPresentation ), m_layerPairSettings( aLayerPairSettings ),
							 | 
						|
								            m_leftGrid( aLeftGrid ), m_rightGrid( aRightGrid )
							 | 
						|
								    {
							 | 
						|
								        configureGrid( m_leftGrid );
							 | 
						|
								        configureGrid( m_rightGrid );
							 | 
						|
								
							 | 
						|
								        for( const PCB_LAYER_ID& layerId : m_layerPresentation.getOrderedEnabledLayers() )
							 | 
						|
								        {
							 | 
						|
								            if( IsCopperLayer( layerId ) )
							 | 
						|
								            {
							 | 
						|
								                m_layersId.push_back( layerId );
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        fillLayerGrid( m_leftGrid );
							 | 
						|
								        fillLayerGrid( m_rightGrid );
							 | 
						|
								
							 | 
						|
								        m_leftGrid.Bind( wxEVT_GRID_CELL_LEFT_CLICK,
							 | 
						|
								                         [this]( wxGridEvent& aEvent )
							 | 
						|
								                         {
							 | 
						|
								                             onLeftGridRowSelected( aEvent.GetRow() );
							 | 
						|
								                         } );
							 | 
						|
								
							 | 
						|
								        m_rightGrid.Bind( wxEVT_GRID_CELL_LEFT_CLICK,
							 | 
						|
								                          [this]( wxGridEvent& aEvent )
							 | 
						|
								                          {
							 | 
						|
								                              onRightGridRowSelected( aEvent.GetRow() );
							 | 
						|
								                          } );
							 | 
						|
								
							 | 
						|
								        m_layerPairSettings.Bind( PCB_CURRENT_LAYER_PAIR_CHANGED,
							 | 
						|
								                                  [this]( wxCommandEvent& aEvent )
							 | 
						|
								                                  {
							 | 
						|
								                                      const LAYER_PAIR& newPair =
							 | 
						|
								                                              m_layerPairSettings.GetCurrentLayerPair();
							 | 
						|
								                                      setCurrentSelection( rowForLayer( newPair.GetLayerA() ),
							 | 
						|
								                                                           rowForLayer( newPair.GetLayerB() ) );
							 | 
						|
								                                  } );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								private:
							 | 
						|
								    void configureGrid( wxGrid& aGrid )
							 | 
						|
								    {
							 | 
						|
								        aGrid.SetCellHighlightPenWidth( 0 );
							 | 
						|
								        aGrid.SetColFormatBool( (int) CU_LAYER_COLNUMS::SELECT );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    void fillLayerGrid( wxGrid& aGrid )
							 | 
						|
								    {
							 | 
						|
								        const wxColour bg = m_layerPresentation.getLayerColor( LAYER_PCB_BACKGROUND ).ToColour();
							 | 
						|
								
							 | 
						|
								        aGrid.AppendRows( m_layersId.size() - 1 );
							 | 
						|
								
							 | 
						|
								        int row = 0;
							 | 
						|
								        for( const PCB_LAYER_ID& layerId : m_layersId )
							 | 
						|
								        {
							 | 
						|
								            const wxColour fg = m_layerPresentation.getLayerColor( layerId ).ToColour();
							 | 
						|
								            const wxColour color(
							 | 
						|
								                    wxColour::AlphaBlend( fg.Red(), bg.Red(), fg.Alpha() / 255.0 ),
							 | 
						|
								                    wxColour::AlphaBlend( fg.Green(), bg.Green(), fg.Alpha() / 255.0 ),
							 | 
						|
								                    wxColour::AlphaBlend( fg.Blue(), bg.Blue(), fg.Alpha() / 255.0 ) );
							 | 
						|
								
							 | 
						|
								            const wxString layerName = wxT( " " ) + m_layerPresentation.getLayerName( layerId );
							 | 
						|
								
							 | 
						|
								            aGrid.SetCellBackgroundColour( row, (int) CU_LAYER_COLNUMS::COLOR, color );
							 | 
						|
								            aGrid.SetCellValue( row, (int) CU_LAYER_COLNUMS::LAYERNAME, layerName );
							 | 
						|
								
							 | 
						|
								            row++;
							 | 
						|
								        };
							 | 
						|
								
							 | 
						|
								        // Now fix min grid layer name column size (it also sets a minimal size)
							 | 
						|
								        aGrid.AutoSizeColumn( (int) CU_LAYER_COLNUMS::LAYERNAME );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    PCB_LAYER_ID layerForRow( int aRow ) { return m_layersId.at( aRow ); }
							 | 
						|
								
							 | 
						|
								    int rowForLayer( PCB_LAYER_ID aLayerId )
							 | 
						|
								    {
							 | 
						|
								        for( unsigned i = 0; i < m_layersId.size(); ++i )
							 | 
						|
								        {
							 | 
						|
								            if( m_layersId[i] == aLayerId )
							 | 
						|
								            {
							 | 
						|
								                return i;
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        wxASSERT_MSG( false, wxString::Format( "Unknown layer in grid: %d", aLayerId ) );
							 | 
						|
								        return 0;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    void onLeftGridRowSelected( int aRow )
							 | 
						|
								    {
							 | 
						|
								        LAYER_PAIR newPair{
							 | 
						|
								            layerForRow( aRow ),
							 | 
						|
								            layerForRow( m_rightCurrRow ),
							 | 
						|
								        };
							 | 
						|
								        setCurrentSelection( aRow, m_rightCurrRow );
							 | 
						|
								        m_layerPairSettings.SetCurrentLayerPair( newPair );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    void onRightGridRowSelected( int aRow )
							 | 
						|
								    {
							 | 
						|
								        LAYER_PAIR newPair{
							 | 
						|
								            layerForRow( m_leftCurrRow ),
							 | 
						|
								            layerForRow( aRow ),
							 | 
						|
								        };
							 | 
						|
								        setCurrentSelection( m_leftCurrRow, aRow );
							 | 
						|
								        m_layerPairSettings.SetCurrentLayerPair( newPair );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    /**
							 | 
						|
								     * Set the current layer selection.
							 | 
						|
								     *
							 | 
						|
								     * The layer pair must be copper layers that the selector this class
							 | 
						|
								     * was constructed with knows about.
							 | 
						|
								     */
							 | 
						|
								    void setCurrentSelection( int aLeftRow, int aRightRow )
							 | 
						|
								    {
							 | 
						|
								        const auto selectGridRow = []( wxGrid& aGrid, int aRow, bool aSelect )
							 | 
						|
								        {
							 | 
						|
								            // At start, there is no old row
							 | 
						|
								            if( aRow < 0 )
							 | 
						|
								            {
							 | 
						|
								                return;
							 | 
						|
								            }
							 | 
						|
								            const wxString val = aSelect ? wxT( "1" ) : wxEmptyString;
							 | 
						|
								            aGrid.SetCellValue( aRow, (int) CU_LAYER_COLNUMS::SELECT, val );
							 | 
						|
								            aGrid.SetGridCursor( aRow, (int) CU_LAYER_COLNUMS::COLOR );
							 | 
						|
								        };
							 | 
						|
								
							 | 
						|
								        if( m_leftCurrRow != aLeftRow )
							 | 
						|
								        {
							 | 
						|
								            selectGridRow( m_leftGrid, m_leftCurrRow, false );
							 | 
						|
								            selectGridRow( m_leftGrid, aLeftRow, true );
							 | 
						|
								            m_leftCurrRow = aLeftRow;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        if( m_rightCurrRow != aRightRow )
							 | 
						|
								        {
							 | 
						|
								            selectGridRow( m_rightGrid, m_rightCurrRow, false );
							 | 
						|
								            selectGridRow( m_rightGrid, aRightRow, true );
							 | 
						|
								            m_rightCurrRow = aRightRow;
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    PCB_LAYER_PRESENTATION&   m_layerPresentation;
							 | 
						|
								    LAYER_PAIR_SETTINGS&      m_layerPairSettings;
							 | 
						|
								    std::vector<PCB_LAYER_ID> m_layersId;
							 | 
						|
								    wxGrid&                   m_leftGrid;
							 | 
						|
								    wxGrid&                   m_rightGrid;
							 | 
						|
								
							 | 
						|
								    int m_leftCurrRow = -1;
							 | 
						|
								    int m_rightCurrRow = -1;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Display a pair PCB copper layers list in a dialog to select a layer pair from these lists.
							 | 
						|
								 *
							 | 
						|
								 * This is a higher level class that mostly glues together other controller classes and UI
							 | 
						|
								 * elements.
							 | 
						|
								 */
							 | 
						|
								class SELECT_COPPER_LAYERS_PAIR_DIALOG : public DIALOG_COPPER_LAYER_PAIR_SELECTION_BASE
							 | 
						|
								{
							 | 
						|
								public:
							 | 
						|
								    SELECT_COPPER_LAYERS_PAIR_DIALOG( PCB_BASE_FRAME&      aParent,
							 | 
						|
								                                      LAYER_PAIR_SETTINGS& aBoardSettings ) :
							 | 
						|
								            DIALOG_COPPER_LAYER_PAIR_SELECTION_BASE( &aParent ),
							 | 
						|
								            m_boardPairSettings( aBoardSettings ), m_dialogPairSettings( aBoardSettings ),
							 | 
						|
								            m_layerPresentation( &aParent ),
							 | 
						|
								            m_pairSelectionController( *m_leftGridLayers, *m_rightGridLayers, m_layerPresentation,
							 | 
						|
								                                       m_dialogPairSettings ),
							 | 
						|
								            m_presetsGridController( *m_presetsGrid, m_layerPresentation, m_dialogPairSettings )
							 | 
						|
								    {
							 | 
						|
								        m_addToPresetsButton->SetBitmap( KiBitmapBundle( BITMAPS::right ) );
							 | 
						|
								        m_deleteRowButton->SetBitmap( KiBitmapBundle( BITMAPS::trash ) );
							 | 
						|
								
							 | 
						|
								        m_addToPresetsButton->Bind( wxEVT_BUTTON,
							 | 
						|
								                                    [this]( wxCommandEvent& aEvent )
							 | 
						|
								                                    {
							 | 
						|
								                                        const LAYER_PAIR newPair =
							 | 
						|
								                                                m_dialogPairSettings.GetCurrentLayerPair();
							 | 
						|
								                                        m_presetsGridController.OnLayerPairAdded( newPair );
							 | 
						|
								                                    } );
							 | 
						|
								
							 | 
						|
								        m_deleteRowButton->Bind( wxEVT_BUTTON,
							 | 
						|
								                                 [this]( wxCommandEvent& aEvent )
							 | 
						|
								                                 {
							 | 
						|
								                                     m_presetsGridController.OnDeleteSelectedLayerPairs();
							 | 
						|
								                                 } );
							 | 
						|
								
							 | 
						|
								        SetFocus();
							 | 
						|
								
							 | 
						|
								        GetSizer()->SetSizeHints( this );
							 | 
						|
								        Center();
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    bool TransferDataToWindow() override
							 | 
						|
								    {
							 | 
						|
								        m_presetsGrid->Freeze();
							 | 
						|
								        m_leftGridLayers->Freeze();
							 | 
						|
								        m_rightGridLayers->Freeze();
							 | 
						|
								
							 | 
						|
								        m_dialogPairSettings.SetCurrentLayerPair( m_boardPairSettings.GetCurrentLayerPair() );
							 | 
						|
								
							 | 
						|
								        m_rightGridLayers->Thaw();
							 | 
						|
								        m_leftGridLayers->Thaw();
							 | 
						|
								        m_presetsGrid->Thaw();
							 | 
						|
								        return true;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    bool TransferDataFromWindow() override
							 | 
						|
								    {
							 | 
						|
								        // Pull out the dialog's stored pairs
							 | 
						|
								        std::span<const LAYER_PAIR_INFO> storePairs = m_dialogPairSettings.GetLayerPairs();
							 | 
						|
								
							 | 
						|
								        m_boardPairSettings.SetLayerPairs( storePairs );
							 | 
						|
								        m_boardPairSettings.SetCurrentLayerPair( m_dialogPairSettings.GetCurrentLayerPair() );
							 | 
						|
								
							 | 
						|
								        return true;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								private:
							 | 
						|
								    // The BOARD's pair store to be updated
							 | 
						|
								    LAYER_PAIR_SETTINGS& m_boardPairSettings;
							 | 
						|
								    // A local copy while we modify it
							 | 
						|
								    LAYER_PAIR_SETTINGS m_dialogPairSettings;
							 | 
						|
								    // Information about the layer presentation (colors, etc)
							 | 
						|
								    PCB_LAYER_PRESENTATION m_layerPresentation;
							 | 
						|
								    // UI controllers
							 | 
						|
								    COPPER_LAYERS_PAIR_SELECTION_UI m_pairSelectionController;
							 | 
						|
								    COPPER_LAYERS_PAIR_PRESETS_UI   m_presetsGridController;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								int ROUTER_TOOL::SelectCopperLayerPair( const TOOL_EVENT& aEvent )
							 | 
						|
								{
							 | 
						|
								    LAYER_PAIR_SETTINGS* const boardSettings = frame()->GetLayerPairSettings();
							 | 
						|
								
							 | 
						|
								    if( !boardSettings )
							 | 
						|
								    {
							 | 
						|
								        // Should only be used for suitable frame types with layer pairs
							 | 
						|
								        wxASSERT_MSG( false, "Could not access layer pair settings" );
							 | 
						|
								        return 0;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    SELECT_COPPER_LAYERS_PAIR_DIALOG dlg( *frame(), *boardSettings );
							 | 
						|
								
							 | 
						|
								    if( dlg.ShowModal() == wxID_OK )
							 | 
						|
								    {
							 | 
						|
								        const LAYER_PAIR layerPair = boardSettings->GetCurrentLayerPair();
							 | 
						|
								
							 | 
						|
								        // select the same layer for both layers is allowed (normal in some boards)
							 | 
						|
								        // but could be a mistake. So display an info message
							 | 
						|
								        if( layerPair.GetLayerA() == layerPair.GetLayerB() )
							 | 
						|
								        {
							 | 
						|
								            DisplayInfoMessage( frame(), _( "Warning: top and bottom layers are same." ) );
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    return 0;
							 | 
						|
								}
							 |