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.
		
		
		
		
		
			
		
			
				
					
					
						
							3195 lines
						
					
					
						
							106 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							3195 lines
						
					
					
						
							106 KiB
						
					
					
				| /* | |
|  * This program source code file is part of KiCad, a free EDA CAD application. | |
|  * | |
|  * Copyright (C) 2020 Jon Evans <jon@craftyjon.com> | |
|  * Copyright (C) 2021-2022 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 <widgets/appearance_controls.h> | |
|  | |
| #include <bitmaps.h> | |
| #include <board.h> | |
| #include <board_design_settings.h> | |
| #include <eda_list_dialog.h> | |
| #include <string_utils.h> | |
| #include <footprint_edit_frame.h> | |
| #include <menus_helpers.h> | |
| #include <pcb_display_options.h> | |
| #include <pcb_edit_frame.h> | |
| #include <pcb_painter.h> | |
| #include <pcbnew_settings.h> | |
| #include <settings/color_settings.h> | |
| #include <tool/tool_manager.h> | |
| #include <tools/pcb_actions.h> | |
| #include <widgets/bitmap_button.h> | |
| #include <widgets/bitmap_toggle.h> | |
| #include <widgets/collapsible_pane.h> | |
| #include <widgets/color_swatch.h> | |
| #include <widgets/grid_bitmap_toggle.h> | |
| #include <widgets/grid_color_swatch_helpers.h> | |
| #include <widgets/grid_text_helpers.h> | |
| #include <widgets/indicator_icon.h> | |
| #include <widgets/infobar.h> | |
| #include <widgets/wx_grid.h> | |
| #include <wx/bmpbuttn.h> | |
| #include <wx/checkbox.h> | |
| #include <wx/hyperlink.h> | |
| #include <wx/radiobut.h> | |
| #include <wx/sizer.h> | |
| #include <wx/slider.h> | |
| #include <wx/statline.h> | |
| #include <wx/textdlg.h> | |
|  | |
| 
 | |
| NET_GRID_TABLE::NET_GRID_TABLE( PCB_BASE_FRAME* aFrame, wxColor aBackgroundColor ) : | |
|         wxGridTableBase(), | |
|         m_frame( aFrame ) | |
| { | |
|     m_defaultAttr = new wxGridCellAttr; | |
|     m_defaultAttr->SetBackgroundColour( aBackgroundColor ); | |
| 
 | |
|     m_labelAttr = new wxGridCellAttr; | |
|     m_labelAttr->SetRenderer( new GRID_CELL_ESCAPED_TEXT_RENDERER ); | |
|     m_labelAttr->SetBackgroundColour( aBackgroundColor ); | |
| } | |
| 
 | |
| 
 | |
| NET_GRID_TABLE::~NET_GRID_TABLE() | |
| { | |
|     m_defaultAttr->DecRef(); | |
|     m_labelAttr->DecRef(); | |
| } | |
| 
 | |
| 
 | |
| wxGridCellAttr* NET_GRID_TABLE::GetAttr( int aRow, int aCol, wxGridCellAttr::wxAttrKind ) | |
| { | |
|     switch( aCol ) | |
|     { | |
|     case COL_COLOR: | |
|         m_defaultAttr->IncRef(); | |
|         return m_defaultAttr; | |
| 
 | |
|     case COL_VISIBILITY: | |
|         m_defaultAttr->IncRef(); | |
|         return m_defaultAttr; | |
| 
 | |
|     case COL_LABEL: | |
|         m_labelAttr->IncRef(); | |
|         return m_labelAttr; | |
| 
 | |
|     default: | |
|         wxFAIL; | |
|         return nullptr; | |
|     } | |
| } | |
| 
 | |
| 
 | |
| wxString NET_GRID_TABLE::GetValue( int aRow, int aCol ) | |
| { | |
|     wxASSERT( static_cast<size_t>( aRow ) < m_nets.size() ); | |
| 
 | |
|     switch( aCol ) | |
|     { | |
|     case COL_COLOR: | |
|         return m_nets[aRow].color.ToWxString( wxC2S_CSS_SYNTAX ); | |
| 
 | |
|     case COL_VISIBILITY: | |
|         return m_nets[aRow].visible ? wxT( "1" ) : wxT( "0" ); | |
| 
 | |
|     case COL_LABEL: | |
|         return m_nets[aRow].name; | |
| 
 | |
|     default: | |
|         return wxEmptyString; | |
|     } | |
| } | |
| 
 | |
| 
 | |
| void NET_GRID_TABLE::SetValue( int aRow, int aCol, const wxString& aValue ) | |
| { | |
|     wxASSERT( static_cast<size_t>( aRow ) < m_nets.size() ); | |
| 
 | |
|     NET_GRID_ENTRY& net = m_nets[aRow]; | |
| 
 | |
|     switch( aCol ) | |
|     { | |
|     case COL_COLOR: | |
|         net.color.SetFromWxString( aValue ); | |
|         updateNetColor( net ); | |
|         break; | |
| 
 | |
|     case COL_VISIBILITY: | |
|         net.visible = ( aValue != wxT( "0" ) ); | |
|         updateNetVisibility( net ); | |
|         break; | |
| 
 | |
|     case COL_LABEL: | |
|         net.name = aValue; | |
|         break; | |
| 
 | |
|     default: | |
|         break; | |
|     } | |
| } | |
| 
 | |
| 
 | |
| wxString NET_GRID_TABLE::GetTypeName( int aRow, int aCol ) | |
| { | |
|     switch( aCol ) | |
|     { | |
|     case COL_COLOR:      return wxT( "COLOR4D" ); | |
|     case COL_VISIBILITY: return wxGRID_VALUE_BOOL; | |
|     case COL_LABEL:      return wxGRID_VALUE_STRING; | |
|     default:             return wxGRID_VALUE_STRING; | |
|     } | |
| } | |
| 
 | |
| 
 | |
| bool NET_GRID_TABLE::GetValueAsBool( int aRow, int aCol ) | |
| { | |
|     wxASSERT( static_cast<size_t>( aRow ) < m_nets.size() ); | |
|     wxASSERT( aCol == COL_VISIBILITY ); | |
| 
 | |
|     return m_nets[aRow].visible; | |
| } | |
| 
 | |
| 
 | |
| void NET_GRID_TABLE::SetValueAsBool( int aRow, int aCol, bool aValue ) | |
| { | |
|     wxASSERT( static_cast<size_t>( aRow ) < m_nets.size() ); | |
|     wxASSERT( aCol == COL_VISIBILITY ); | |
| 
 | |
|     m_nets[aRow].visible = aValue; | |
|     updateNetVisibility( m_nets[aRow] ); | |
| } | |
| 
 | |
| 
 | |
| void* NET_GRID_TABLE::GetValueAsCustom( int aRow, int aCol, const wxString& aTypeName ) | |
| { | |
|     wxASSERT( aCol == COL_COLOR ); | |
|     wxASSERT( aTypeName == wxT( "COLOR4D" ) ); | |
|     wxASSERT( static_cast<size_t>( aRow ) < m_nets.size() ); | |
| 
 | |
|     return ColorToVoid( m_nets[aRow].color ); | |
| } | |
| 
 | |
| 
 | |
| void NET_GRID_TABLE::SetValueAsCustom( int aRow, int aCol, const wxString& aTypeName, void* aValue ) | |
| { | |
|     wxASSERT( aCol == COL_COLOR ); | |
|     wxASSERT( aTypeName == wxT( "COLOR4D" ) ); | |
|     wxASSERT( static_cast<size_t>( aRow ) < m_nets.size() ); | |
| 
 | |
|     m_nets[aRow].color = VoidToColor( aValue ); | |
|     updateNetColor( m_nets[aRow] ); | |
| 
 | |
|     if( GetView() ) | |
|     { | |
|         wxGridTableMessage msg( this, wxGRIDTABLE_REQUEST_VIEW_GET_VALUES ); | |
|         GetView()->ProcessTableMessage( msg ); | |
|     } | |
| } | |
| 
 | |
| 
 | |
| NET_GRID_ENTRY& NET_GRID_TABLE::GetEntry( int aRow ) | |
| { | |
|     wxASSERT( static_cast<size_t>( aRow ) < m_nets.size() ); | |
|     return m_nets[aRow]; | |
| } | |
| 
 | |
| 
 | |
| int NET_GRID_TABLE::GetRowByNetcode( int aCode ) const | |
| { | |
|     auto it = std::find_if( m_nets.cbegin(), m_nets.cend(), | |
|             [aCode]( const NET_GRID_ENTRY& aEntry ) | |
|             { | |
|                 return aEntry.code == aCode; | |
|             } ); | |
| 
 | |
|     if( it == m_nets.cend() ) | |
|         return -1; | |
| 
 | |
|     return std::distance( m_nets.cbegin(), it ); | |
| } | |
| 
 | |
| 
 | |
| void NET_GRID_TABLE::Rebuild() | |
| { | |
|     BOARD*              board = m_frame->GetBoard(); | |
|     const NETNAMES_MAP& nets  = board->GetNetInfo().NetsByName(); | |
| 
 | |
|     KIGFX::PCB_RENDER_SETTINGS* rs = static_cast<KIGFX::PCB_RENDER_SETTINGS*>( | |
|             m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings() ); | |
| 
 | |
|     std::set<int>&                 hiddenNets = rs->GetHiddenNets(); | |
|     std::map<int, KIGFX::COLOR4D>& netColors  = rs->GetNetColorMap(); | |
| 
 | |
|     int deleted = m_nets.size(); | |
|     m_nets.clear(); | |
| 
 | |
|     if( GetView() ) | |
|     { | |
|         wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, deleted ); | |
|         GetView()->ProcessTableMessage( msg ); | |
|     } | |
| 
 | |
|     for( const std::pair<const wxString, NETINFO_ITEM*>& pair : nets ) | |
|     { | |
|         int netCode = pair.second->GetNetCode(); | |
| 
 | |
|         if( netCode > 0 && !pair.first.StartsWith( wxT( "unconnected-(" ) ) ) | |
|         { | |
|             COLOR4D color = netColors.count( netCode ) ? netColors.at( netCode ) : | |
|                             COLOR4D::UNSPECIFIED; | |
| 
 | |
|             bool visible = hiddenNets.count( netCode ) == 0; | |
| 
 | |
|             m_nets.emplace_back( NET_GRID_ENTRY( netCode, pair.first, color, visible ) ); | |
|         } | |
|     } | |
| 
 | |
|     // TODO(JE) move to ::Compare so we can re-sort easily | |
|     std::sort( m_nets.begin(), m_nets.end(), | |
|                []( const NET_GRID_ENTRY& a, const NET_GRID_ENTRY& b ) | |
|                { | |
|                  return a.name < b.name; | |
|                } ); | |
| 
 | |
|     if( GetView() ) | |
|     { | |
|         wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_nets.size() ); | |
|         GetView()->ProcessTableMessage( msg ); | |
|     } | |
| } | |
| 
 | |
| 
 | |
| void NET_GRID_TABLE::ShowAllNets() | |
| { | |
|     for( NET_GRID_ENTRY& net : m_nets ) | |
|     { | |
|         net.visible = true; | |
|         updateNetVisibility( net ); | |
|     } | |
| 
 | |
|     if( GetView() ) | |
|         GetView()->ForceRefresh(); | |
| } | |
| 
 | |
| 
 | |
| void NET_GRID_TABLE::HideOtherNets( const NET_GRID_ENTRY& aNet ) | |
| { | |
|     for( NET_GRID_ENTRY& net : m_nets ) | |
|     { | |
|         net.visible = ( net.code == aNet.code ); | |
|         updateNetVisibility( net ); | |
|     } | |
| 
 | |
|     if( GetView() ) | |
|         GetView()->ForceRefresh(); | |
| } | |
| 
 | |
| 
 | |
| void NET_GRID_TABLE::updateNetVisibility( const NET_GRID_ENTRY& aNet ) | |
| { | |
|     const TOOL_ACTION& action = aNet.visible ? PCB_ACTIONS::showNet : PCB_ACTIONS::hideNet; | |
|     m_frame->GetToolManager()->RunAction( action, true, static_cast<intptr_t>( aNet.code ) ); | |
| } | |
| 
 | |
| 
 | |
| void NET_GRID_TABLE::updateNetColor( const NET_GRID_ENTRY& aNet ) | |
| { | |
|     KIGFX::PCB_RENDER_SETTINGS* rs = static_cast<KIGFX::PCB_RENDER_SETTINGS*>( | |
|             m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings() ); | |
| 
 | |
|     std::map<int, KIGFX::COLOR4D>& netColors  = rs->GetNetColorMap(); | |
| 
 | |
|     if( aNet.color != COLOR4D::UNSPECIFIED ) | |
|         netColors[aNet.code] = aNet.color; | |
|     else | |
|         netColors.erase( aNet.code ); | |
| 
 | |
|     m_frame->GetCanvas()->GetView()->UpdateAllLayersColor(); | |
|     m_frame->GetCanvas()->RedrawRatsnest(); | |
|     m_frame->GetCanvas()->Refresh(); | |
| } | |
| 
 | |
| 
 | |
| /// Template for object appearance settings | |
| const APPEARANCE_CONTROLS::APPEARANCE_SETTING APPEARANCE_CONTROLS::s_objectSettings[] = { | |
| 
 | |
| #define RR  APPEARANCE_CONTROLS::APPEARANCE_SETTING   // Render Row abbreviation to reduce source width | |
|  | |
|     //     text                  id                        tooltip                   opacity slider | |
|     RR( _HKI( "Tracks" ),           LAYER_TRACKS,             _HKI( "Show tracks" ),       true ), | |
|     RR( _HKI( "Vias" ),             LAYER_VIAS,               _HKI( "Show all vias" ),     true ), | |
|     RR( _HKI( "Pads" ),             LAYER_PADS,               _HKI( "Show all pads" ),     true ), | |
|     RR( _HKI( "Zones" ),            LAYER_ZONES,              _HKI( "Show copper zones" ), true ), | |
|     RR( _HKI( "Images" ),           LAYER_DRAW_BITMAPS,       _HKI( "Show user images" ),  true ), | |
|     RR(), | |
|     RR( _HKI( "Footprints Front" ), LAYER_MOD_FR,             _HKI( "Show footprints that are on board's front" ) ), | |
|     RR( _HKI( "Footprints Back" ),  LAYER_MOD_BK,             _HKI( "Show footprints that are on board's back" ) ), | |
|     RR( _HKI( "Through-hole Pads" ),LAYER_PADS_TH,            _HKI( "Show through-hole pads" ) ), | |
|     RR( _HKI( "Values" ),           LAYER_MOD_VALUES,         _HKI( "Show footprint values" ) ), | |
|     RR( _HKI( "References" ),       LAYER_MOD_REFERENCES,     _HKI( "Show footprint references" ) ), | |
|     RR( _HKI( "Footprint Text" ),   LAYER_MOD_TEXT,           _HKI( "Show all footprint text" ) ), | |
|     RR( _HKI( "Hidden Text" ),      LAYER_MOD_TEXT_INVISIBLE, _HKI( "Show footprint text marked as invisible" ) ), | |
|     RR(), | |
|     RR(), | |
|     RR( _HKI( "Ratsnest" ),         LAYER_RATSNEST,           _HKI( "Show unconnected nets as a ratsnest") ), | |
|     RR( _HKI( "No-Connects" ),      LAYER_NO_CONNECTS,        _HKI( "Show a marker on pads which have no net connected" ) ), | |
|     RR( _HKI( "DRC Warnings" ),     LAYER_DRC_WARNING,        _HKI( "DRC violations with a Warning severity" ) ), | |
|     RR( _HKI( "DRC Errors" ),       LAYER_DRC_ERROR,          _HKI( "DRC violations with an Error severity" ) ), | |
|     RR( _HKI( "DRC Exclusions" ),   LAYER_DRC_EXCLUSION,      _HKI( "DRC violations which have been individually excluded" ) ), | |
|     RR( _HKI( "Anchors" ),          LAYER_ANCHOR,             _HKI( "Show footprint and text origins as a cross" ) ), | |
|     RR( _HKI( "Shadow on Locked Items" ), LAYER_LOCKED_ITEM_SHADOW, _HKI( "Show a shadow marker on locked items" ) ), | |
|     RR( _HKI( "Shadow on Conflicts" ), LAYER_CONFLICTS_SHADOW, | |
|                                        _HKI( "Show a shadow on overlapping courtyards while moving footprints" ) ), | |
|     RR( _HKI( "Drawing Sheet" ),    LAYER_DRAWINGSHEET,       _HKI( "Show drawing sheet borders and title block" ) ), | |
|     RR( _HKI( "Grid" ),             LAYER_GRID,               _HKI( "Show the (x,y) grid dots" ) ) | |
| }; | |
| 
 | |
| /// These GAL layers are shown in the Objects tab in the footprint editor | |
| static std::set<int> s_allowedInFpEditor = | |
|         { | |
|             LAYER_TRACKS, | |
|             LAYER_VIAS, | |
|             LAYER_PADS, | |
|             LAYER_ZONES, | |
|             LAYER_PADS_TH, | |
|             LAYER_MOD_VALUES, | |
|             LAYER_MOD_REFERENCES, | |
|             LAYER_MOD_TEXT, | |
|             LAYER_MOD_TEXT_INVISIBLE, | |
|             LAYER_DRAW_BITMAPS, | |
|             LAYER_GRID | |
|         }; | |
| 
 | |
| // These are the built-in layer presets that cannot be deleted | |
|  | |
| LAYER_PRESET APPEARANCE_CONTROLS::presetNoLayers( _HKI( "No Layers" ), LSET() ); | |
| 
 | |
| LAYER_PRESET APPEARANCE_CONTROLS::presetAllLayers( _HKI( "All Layers" ), LSET::AllLayersMask() ); | |
| 
 | |
| LAYER_PRESET APPEARANCE_CONTROLS::presetAllCopper( _HKI( "All Copper Layers" ), | |
|         LSET::AllCuMask().set( Edge_Cuts ) ); | |
| 
 | |
| LAYER_PRESET APPEARANCE_CONTROLS::presetInnerCopper( _HKI( "Inner Copper Layers" ), | |
|         LSET::InternalCuMask().set( Edge_Cuts ) ); | |
| 
 | |
| LAYER_PRESET APPEARANCE_CONTROLS::presetFront( _HKI( "Front Layers" ), | |
|         LSET::FrontMask().set( Edge_Cuts ) ); | |
| 
 | |
| LAYER_PRESET APPEARANCE_CONTROLS::presetFrontAssembly( _HKI( "Front Assembly View" ), | |
|         LSET::FrontAssembly().set( Edge_Cuts ), GAL_SET::DefaultVisible(), F_SilkS ); | |
| 
 | |
| LAYER_PRESET APPEARANCE_CONTROLS::presetBack( _HKI( "Back Layers" ), | |
|         LSET::BackMask().set( Edge_Cuts ) ); | |
| 
 | |
| LAYER_PRESET APPEARANCE_CONTROLS::presetBackAssembly( _HKI( "Back Assembly View" ), | |
|         LSET::BackAssembly().set( Edge_Cuts ), GAL_SET::DefaultVisible(), B_SilkS ); | |
| 
 | |
| 
 | |
| APPEARANCE_CONTROLS::APPEARANCE_CONTROLS( PCB_BASE_FRAME* aParent, wxWindow* aFocusOwner, | |
|                                           bool aFpEditorMode ) : | |
|         APPEARANCE_CONTROLS_BASE( aParent ), | |
|         m_frame( aParent ), | |
|         m_focusOwner( aFocusOwner ), | |
|         m_board( nullptr ), | |
|         m_isFpEditor( aFpEditorMode ), | |
|         m_currentPreset( nullptr ), | |
|         m_lastSelectedUserPreset( nullptr ), | |
|         m_layerContextMenu( nullptr ) | |
| { | |
|     int indicatorSize = ConvertDialogToPixels( wxSize( 6, 6 ) ).x; | |
|     m_iconProvider    = new ROW_ICON_PROVIDER( indicatorSize ); | |
|     int pointSize     = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ).GetPointSize(); | |
|     int screenHeight  = wxSystemSettings::GetMetric( wxSYS_SCREEN_Y ); | |
| 
 | |
|     m_layerPanelColour = m_panelLayers->GetBackgroundColour().ChangeLightness( 110 ); | |
|     SetBorders( true, false, false, false ); | |
| 
 | |
|     m_layersOuterSizer = new wxBoxSizer( wxVERTICAL ); | |
|     m_windowLayers->SetSizer( m_layersOuterSizer ); | |
|     m_windowLayers->SetScrollRate( 0, 5 ); | |
|     m_windowLayers->Bind( wxEVT_SET_FOCUS, &APPEARANCE_CONTROLS::OnSetFocus, this ); | |
| 
 | |
|     m_objectsOuterSizer = new wxBoxSizer( wxVERTICAL ); | |
|     m_windowObjects->SetSizer( m_objectsOuterSizer ); | |
|     m_windowObjects->SetScrollRate( 0, 5 ); | |
|     m_windowObjects->Bind( wxEVT_SET_FOCUS, &APPEARANCE_CONTROLS::OnSetFocus, this ); | |
| 
 | |
|     wxFont infoFont = KIUI::GetInfoFont( this ); | |
|     m_staticTextNets->SetFont( infoFont ); | |
|     m_staticTextNetClasses->SetFont( infoFont ); | |
|     m_panelLayers->SetFont( infoFont ); | |
|     m_windowLayers->SetFont( infoFont ); | |
|     m_windowObjects->SetFont( infoFont ); | |
|     m_presetsLabel->SetFont( infoFont ); | |
|     m_viewportsLabel->SetFont( infoFont ); | |
| 
 | |
|     createControls(); | |
| 
 | |
|     m_btnNetInspector->SetBitmap( KiBitmap( BITMAPS::list_nets_16 ) ); | |
|     m_btnNetInspector->SetPadding( 2 ); | |
| 
 | |
|     m_btnConfigureNetClasses->SetBitmap( KiBitmap( BITMAPS::options_generic_16 ) ); | |
|     m_btnConfigureNetClasses->SetPadding( 2 ); | |
| 
 | |
|     m_txtNetFilter->SetHint( _( "Filter nets" ) ); | |
| 
 | |
|     if( screenHeight <= 900 && pointSize >= indicatorSize ) | |
|         pointSize = pointSize * 8 / 10; | |
| 
 | |
|     m_pointSize = pointSize; | |
|     wxFont font = m_notebook->GetFont(); | |
| 
 | |
| #ifdef __WXMAC__ | |
|     font.SetPointSize( m_pointSize ); | |
|     m_notebook->SetFont( font ); | |
| #endif | |
|  | |
|     auto setHighContrastMode = | |
|             [&]( HIGH_CONTRAST_MODE aMode ) | |
|             { | |
|                 PCB_DISPLAY_OPTIONS opts   = m_frame->GetDisplayOptions(); | |
|                 opts.m_ContrastModeDisplay = aMode; | |
| 
 | |
|                 m_frame->SetDisplayOptions( opts ); | |
|                 passOnFocus(); | |
|             }; | |
| 
 | |
|     m_rbHighContrastNormal->Bind( wxEVT_RADIOBUTTON, | |
|             [=]( wxCommandEvent& aEvent ) | |
|             { | |
|                 setHighContrastMode( HIGH_CONTRAST_MODE::NORMAL ); | |
|             } ); | |
| 
 | |
|     m_rbHighContrastDim->Bind( wxEVT_RADIOBUTTON, | |
|             [=]( wxCommandEvent& aEvent ) | |
|             { | |
|                 setHighContrastMode( HIGH_CONTRAST_MODE::DIMMED ); | |
|             } ); | |
| 
 | |
|     m_rbHighContrastOff->Bind( wxEVT_RADIOBUTTON, | |
|             [=]( wxCommandEvent& aEvent ) | |
|             { | |
|                 setHighContrastMode( HIGH_CONTRAST_MODE::HIDDEN ); | |
|             } ); | |
| 
 | |
|     m_cbLayerPresets->Bind( wxEVT_CHOICE, &APPEARANCE_CONTROLS::onLayerPresetChanged, this ); | |
| 
 | |
|     m_btnNetInspector->Bind( wxEVT_BUTTON, | |
|             [&]( wxCommandEvent& aEvent ) | |
|             { | |
|                 m_frame->GetToolManager()->RunAction( PCB_ACTIONS::listNets, true ); | |
|                 passOnFocus(); | |
|             } ); | |
| 
 | |
|     m_btnConfigureNetClasses->Bind( wxEVT_BUTTON, | |
|             [&]( wxCommandEvent& aEvent ) | |
|             { | |
|                 // This panel should only be visible in the PCB_EDIT_FRAME anyway | |
|                 if( PCB_EDIT_FRAME* editframe = dynamic_cast<PCB_EDIT_FRAME*>( m_frame ) ) | |
|                     editframe->ShowBoardSetupDialog( _( "Net Classes" ) ); | |
| 
 | |
|                 passOnFocus(); | |
|             } ); | |
| 
 | |
|     m_cbFlipBoard->SetValue( m_frame->GetCanvas()->GetView()->IsMirroredX() ); | |
|     m_cbFlipBoard->Bind( wxEVT_CHECKBOX, | |
|             [&]( wxCommandEvent& aEvent ) | |
|             { | |
|                 m_frame->GetToolManager()->RunAction( PCB_ACTIONS::flipBoard, true ); | |
|             } ); | |
| 
 | |
|     m_toggleGridRenderer = new GRID_BITMAP_TOGGLE_RENDERER( KiBitmap( BITMAPS::visibility ), | |
|                                                             KiBitmap( BITMAPS::visibility_off ) ); | |
| 
 | |
|     m_netsGrid->RegisterDataType( wxT( "bool" ), m_toggleGridRenderer, new wxGridCellBoolEditor ); | |
| 
 | |
|     // TODO(JE) Update background color of swatch renderer when theme changes | |
|     m_netsGrid->RegisterDataType( wxT( "COLOR4D" ), | |
|                                   new GRID_CELL_COLOR_RENDERER( m_frame, SWATCH_SMALL ), | |
|                                   new GRID_CELL_COLOR_SELECTOR( m_frame, m_netsGrid ) ); | |
| 
 | |
|     m_netsTable = new NET_GRID_TABLE( m_frame, m_panelNets->GetBackgroundColour() ); | |
|     m_netsGrid->SetTable( m_netsTable, true ); | |
|     m_netsGrid->SetColLabelSize( 0 ); | |
| 
 | |
|     m_netsGrid->SetSelectionMode( wxGrid::wxGridSelectRows ); | |
|     m_netsGrid->SetSelectionForeground( m_netsGrid->GetDefaultCellTextColour() ); | |
|     m_netsGrid->SetSelectionBackground( m_panelNets->GetBackgroundColour() ); | |
| 
 | |
|     const int cellPadding      = 6; | |
| #ifdef __WXMAC__ | |
|     const int rowHeightPadding = 5; | |
| #else | |
|     const int rowHeightPadding = 3; | |
| #endif | |
|  | |
|     wxSize size = ConvertDialogToPixels( SWATCH_SIZE_SMALL_DU ); | |
|     m_netsGrid->SetColSize( NET_GRID_TABLE::COL_COLOR, size.x + cellPadding ); | |
| 
 | |
|     size = KiBitmap( BITMAPS::visibility ).GetSize(); | |
|     m_netsGrid->SetColSize( NET_GRID_TABLE::COL_VISIBILITY, size.x + cellPadding ); | |
| 
 | |
|     m_netsGrid->SetDefaultCellFont( font ); | |
|     m_netsGrid->SetDefaultRowSize( font.GetPixelSize().y + rowHeightPadding ); | |
| 
 | |
|     m_netsGrid->GetGridWindow()->Bind( wxEVT_MOTION, &APPEARANCE_CONTROLS::OnNetGridMouseEvent, | |
|                                        this ); | |
| 
 | |
|     // To handle middle click on color swatches | |
|     m_netsGrid->GetGridWindow()->Bind( wxEVT_MIDDLE_UP, &APPEARANCE_CONTROLS::OnNetGridMouseEvent, | |
|                                        this ); | |
| 
 | |
|     m_netsGrid->ShowScrollbars( wxSHOW_SB_NEVER, wxSHOW_SB_DEFAULT ); | |
|     m_netclassScrolledWindow->ShowScrollbars( wxSHOW_SB_NEVER, wxSHOW_SB_DEFAULT ); | |
| 
 | |
|     if( m_isFpEditor ) | |
|         m_notebook->RemovePage( 2 ); | |
| 
 | |
|     loadDefaultLayerPresets(); | |
|     rebuildObjects(); | |
|     OnBoardChanged(); | |
| 
 | |
|     // Grid visibility is loaded and set to the GAL before we are constructed | |
|     SetObjectVisible( LAYER_GRID, m_frame->IsGridVisible() ); | |
| 
 | |
|     Bind( wxEVT_COMMAND_MENU_SELECTED, &APPEARANCE_CONTROLS::OnLayerContextMenu, this, | |
|           ID_CHANGE_COLOR, ID_LAST_VALUE ); | |
| } | |
| 
 | |
| 
 | |
| APPEARANCE_CONTROLS::~APPEARANCE_CONTROLS() | |
| { | |
|     delete m_iconProvider; | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::createControls() | |
| { | |
|     int      hotkey; | |
|     wxString msg; | |
|     wxFont   infoFont = KIUI::GetInfoFont( this ); | |
| 
 | |
|     // Create layer display options | |
|     m_paneLayerDisplayOptions = new WX_COLLAPSIBLE_PANE( m_panelLayers, wxID_ANY, | |
|                                                          _( "Layer Display Options" ) ); | |
|     m_paneLayerDisplayOptions->Collapse(); | |
|     m_paneLayerDisplayOptions->SetBackgroundColour( m_notebook->GetThemeBackgroundColour() ); | |
| 
 | |
|     wxWindow* layerDisplayPane = m_paneLayerDisplayOptions->GetPane(); | |
| 
 | |
|     wxBoxSizer* layerDisplayOptionsSizer; | |
|     layerDisplayOptionsSizer = new wxBoxSizer( wxVERTICAL ); | |
| 
 | |
|     hotkey = PCB_ACTIONS::highContrastModeCycle.GetHotKey(); | |
| 
 | |
|     if( hotkey ) | |
|         msg = wxString::Format( _( "Inactive layers (%s):" ), KeyNameFromKeyCode( hotkey ) ); | |
|     else | |
|         msg = _( "Inactive layers:" ); | |
| 
 | |
|     m_inactiveLayersLabel = new wxStaticText( layerDisplayPane, wxID_ANY, msg ); | |
|     m_inactiveLayersLabel->SetFont( infoFont ); | |
|     m_inactiveLayersLabel->Wrap( -1 ); | |
|     layerDisplayOptionsSizer->Add( m_inactiveLayersLabel, 0, wxEXPAND | wxBOTTOM | wxLEFT, 2 ); | |
| 
 | |
|     wxBoxSizer* contrastModeSizer; | |
|     contrastModeSizer = new wxBoxSizer( wxHORIZONTAL ); | |
| 
 | |
|     m_rbHighContrastNormal = new wxRadioButton( layerDisplayPane, wxID_ANY, _( "Normal" ), | |
|                                                 wxDefaultPosition, wxDefaultSize, wxRB_GROUP ); | |
|     m_rbHighContrastNormal->SetFont( infoFont ); | |
|     m_rbHighContrastNormal->SetValue( true ); | |
|     m_rbHighContrastNormal->SetToolTip( _( "Inactive layers will be shown in full color" ) ); | |
| 
 | |
|     contrastModeSizer->Add( m_rbHighContrastNormal, 0, wxRIGHT, 5 ); | |
|     contrastModeSizer->AddStretchSpacer(); | |
| 
 | |
|     m_rbHighContrastDim = new wxRadioButton( layerDisplayPane, wxID_ANY, _( "Dim" ) ); | |
|     m_rbHighContrastDim->SetFont( infoFont ); | |
|     m_rbHighContrastDim->SetToolTip( _( "Inactive layers will be dimmed" ) ); | |
| 
 | |
|     contrastModeSizer->Add( m_rbHighContrastDim, 0, wxRIGHT, 5 ); | |
|     contrastModeSizer->AddStretchSpacer(); | |
| 
 | |
|     m_rbHighContrastOff = new wxRadioButton( layerDisplayPane, wxID_ANY, _( "Hide" ) ); | |
|     m_rbHighContrastOff->SetFont( infoFont ); | |
|     m_rbHighContrastOff->SetToolTip( _( "Inactive layers will be hidden" ) ); | |
| 
 | |
|     contrastModeSizer->Add( m_rbHighContrastOff, 0, 0, 5 ); | |
|     contrastModeSizer->AddStretchSpacer(); | |
| 
 | |
|     layerDisplayOptionsSizer->Add( contrastModeSizer, 0, wxEXPAND, 5 ); | |
| 
 | |
|     m_layerDisplaySeparator = new wxStaticLine( layerDisplayPane, wxID_ANY, wxDefaultPosition, | |
|                                                 wxDefaultSize, wxLI_HORIZONTAL ); | |
|     layerDisplayOptionsSizer->Add( m_layerDisplaySeparator, 0, wxEXPAND | wxTOP | wxBOTTOM, 5 ); | |
| 
 | |
|     m_cbFlipBoard = new wxCheckBox( layerDisplayPane, wxID_ANY, _( "Flip board view" ) ); | |
|     m_cbFlipBoard->SetFont( infoFont ); | |
|     layerDisplayOptionsSizer->Add( m_cbFlipBoard, 0, 0, 5 ); | |
| 
 | |
|     layerDisplayPane->SetSizer( layerDisplayOptionsSizer ); | |
|     layerDisplayPane->Layout(); | |
|     layerDisplayOptionsSizer->Fit( layerDisplayPane ); | |
| 
 | |
|     m_panelLayersSizer->Add( m_paneLayerDisplayOptions, 0, wxEXPAND | wxTOP, 5 ); | |
| 
 | |
|     m_paneLayerDisplayOptions->Bind( WX_COLLAPSIBLE_PANE_CHANGED, | |
|                                      [&]( wxCommandEvent& aEvent ) | |
|                                      { | |
|                                          Freeze(); | |
|                                          m_panelLayers->Fit(); | |
|                                          m_sizerOuter->Layout(); | |
|                                          Thaw(); | |
|                                      } ); | |
| 
 | |
|     // Create net display options | |
|  | |
|     m_paneNetDisplayOptions = new WX_COLLAPSIBLE_PANE( m_panelNetsAndClasses, wxID_ANY, | |
|                                                        _( "Net Display Options" ) ); | |
|     m_paneNetDisplayOptions->Collapse(); | |
|     m_paneNetDisplayOptions->SetBackgroundColour( m_notebook->GetThemeBackgroundColour() ); | |
| 
 | |
|     wxWindow* netDisplayPane = m_paneNetDisplayOptions->GetPane(); | |
|     wxBoxSizer* netDisplayOptionsSizer = new wxBoxSizer( wxVERTICAL ); | |
| 
 | |
|     //// Net color mode | |
|  | |
|     hotkey = PCB_ACTIONS::netColorModeCycle.GetHotKey(); | |
| 
 | |
|     if( hotkey ) | |
|         msg = wxString::Format( _( "Net colors (%s):" ), KeyNameFromKeyCode( hotkey ) ); | |
|     else | |
|         msg = _( "Net colors:" ); | |
| 
 | |
|     m_txtNetDisplayTitle = new wxStaticText( netDisplayPane, wxID_ANY, msg ); | |
|     m_txtNetDisplayTitle->SetFont( infoFont ); | |
|     m_txtNetDisplayTitle->Wrap( -1 ); | |
|     m_txtNetDisplayTitle->SetToolTip( _( "Choose when to show net and netclass colors" ) ); | |
| 
 | |
|     netDisplayOptionsSizer->Add( m_txtNetDisplayTitle, 0, wxEXPAND | wxBOTTOM | wxLEFT, 2 ); | |
| 
 | |
|     wxBoxSizer* netColorSizer = new wxBoxSizer( wxHORIZONTAL ); | |
| 
 | |
|     m_rbNetColorAll = new wxRadioButton( netDisplayPane, wxID_ANY, _( "All" ), wxDefaultPosition, | |
|                                          wxDefaultSize, wxRB_GROUP ); | |
|     m_rbNetColorAll->SetFont( infoFont ); | |
|     m_rbNetColorAll->SetToolTip( _( "Net and netclass colors are shown on all copper items" ) ); | |
| 
 | |
|     netColorSizer->Add( m_rbNetColorAll, 0, wxRIGHT, 5 ); | |
|     netColorSizer->AddStretchSpacer(); | |
| 
 | |
|     m_rbNetColorRatsnest = new wxRadioButton( netDisplayPane, wxID_ANY, _( "Ratsnest" ) ); | |
|     m_rbNetColorRatsnest->SetFont( infoFont ); | |
|     m_rbNetColorRatsnest->SetValue( true ); | |
|     m_rbNetColorRatsnest->SetToolTip( _( "Net and netclass colors are shown on the ratsnest only" ) ); | |
| 
 | |
|     netColorSizer->Add( m_rbNetColorRatsnest, 0, wxRIGHT, 5 ); | |
|     netColorSizer->AddStretchSpacer(); | |
| 
 | |
|     m_rbNetColorOff = new wxRadioButton( netDisplayPane, wxID_ANY, _( "None" ) ); | |
|     m_rbNetColorOff->SetFont( infoFont ); | |
|     m_rbNetColorOff->SetToolTip( _( "Net and netclass colors are not shown" ) ); | |
| 
 | |
|     netColorSizer->Add( m_rbNetColorOff, 0, 0, 5 ); | |
| 
 | |
|     netDisplayOptionsSizer->Add( netColorSizer, 0, wxEXPAND | wxBOTTOM, 5 ); | |
| 
 | |
|     //// Ratsnest display | |
|  | |
|     hotkey = PCB_ACTIONS::ratsnestModeCycle.GetHotKey(); | |
| 
 | |
|     if( hotkey ) | |
|         msg = wxString::Format( _( "Ratsnest display (%s):" ), KeyNameFromKeyCode( hotkey ) ); | |
|     else | |
|         msg = _( "Ratsnest display:" ); | |
| 
 | |
|     m_txtRatsnestVisibility = new wxStaticText( netDisplayPane, wxID_ANY, msg ); | |
|     m_txtRatsnestVisibility->SetFont( infoFont ); | |
|     m_txtRatsnestVisibility->Wrap( -1 ); | |
|     m_txtRatsnestVisibility->SetToolTip( _( "Choose which ratsnest lines to display" ) ); | |
| 
 | |
|     netDisplayOptionsSizer->Add( m_txtRatsnestVisibility, 0, wxEXPAND | wxBOTTOM | wxLEFT, 2 ); | |
| 
 | |
|     wxBoxSizer* ratsnestDisplayModeSizer = new wxBoxSizer( wxHORIZONTAL ); | |
| 
 | |
|     m_rbRatsnestAllLayers = new wxRadioButton( netDisplayPane, wxID_ANY, _( "All" ), | |
|                                                wxDefaultPosition, wxDefaultSize, wxRB_GROUP ); | |
|     m_rbRatsnestAllLayers->SetFont( infoFont ); | |
|     m_rbRatsnestAllLayers->SetValue( true ); | |
|     m_rbRatsnestAllLayers->SetToolTip( _( "Show ratsnest lines to items on all layers" ) ); | |
| 
 | |
|     ratsnestDisplayModeSizer->Add( m_rbRatsnestAllLayers, 0, wxRIGHT, 5 ); | |
|     ratsnestDisplayModeSizer->AddStretchSpacer(); | |
| 
 | |
|     m_rbRatsnestVisLayers = new wxRadioButton( netDisplayPane, wxID_ANY, _( "Visible layers" ) ); | |
|     m_rbRatsnestVisLayers->SetFont( infoFont ); | |
|     m_rbRatsnestVisLayers->SetToolTip( _( "Show ratsnest lines to items on visible layers" ) ); | |
| 
 | |
|     ratsnestDisplayModeSizer->Add( m_rbRatsnestVisLayers, 0, wxRIGHT, 5 ); | |
|     ratsnestDisplayModeSizer->AddStretchSpacer(); | |
| 
 | |
|     m_rbRatsnestNone = new wxRadioButton( netDisplayPane, wxID_ANY, _( "None" ) ); | |
|     m_rbRatsnestNone->SetFont( infoFont ); | |
|     m_rbRatsnestNone->SetToolTip( _( "Hide all ratsnest lines" ) ); | |
| 
 | |
|     ratsnestDisplayModeSizer->Add( m_rbRatsnestNone, 0, 0, 5 ); | |
| 
 | |
|     netDisplayOptionsSizer->Add( ratsnestDisplayModeSizer, 0, wxEXPAND | wxBOTTOM, 5 ); | |
| 
 | |
|     //// | |
|  | |
|     netDisplayPane->SetSizer( netDisplayOptionsSizer ); | |
|     netDisplayPane->Layout(); | |
|     netDisplayOptionsSizer->Fit( netDisplayPane ); | |
| 
 | |
|     m_netsTabOuterSizer->Add( m_paneNetDisplayOptions, 0, wxEXPAND | wxTOP, 5 ); | |
| 
 | |
|     m_paneNetDisplayOptions->Bind( WX_COLLAPSIBLE_PANE_CHANGED, | |
|                                    [&]( wxCommandEvent& aEvent ) | |
|                                    { | |
|                                        Freeze(); | |
|                                        m_panelNetsAndClasses->Fit(); | |
|                                        m_sizerOuter->Layout(); | |
|                                        passOnFocus(); | |
|                                        Thaw(); | |
|                                    } ); | |
| 
 | |
|     m_rbNetColorAll->Bind( wxEVT_RADIOBUTTON, &APPEARANCE_CONTROLS::onNetColorMode, this ); | |
|     m_rbNetColorOff->Bind( wxEVT_RADIOBUTTON, &APPEARANCE_CONTROLS::onNetColorMode, this ); | |
|     m_rbNetColorRatsnest->Bind( wxEVT_RADIOBUTTON, &APPEARANCE_CONTROLS::onNetColorMode, this ); | |
| 
 | |
|     m_rbRatsnestAllLayers->Bind( wxEVT_RADIOBUTTON, &APPEARANCE_CONTROLS::onRatsnestMode, this ); | |
|     m_rbRatsnestVisLayers->Bind( wxEVT_RADIOBUTTON, &APPEARANCE_CONTROLS::onRatsnestMode, this ); | |
|     m_rbRatsnestNone->Bind( wxEVT_RADIOBUTTON, &APPEARANCE_CONTROLS::onRatsnestMode, this ); | |
| } | |
| 
 | |
| 
 | |
| wxSize APPEARANCE_CONTROLS::GetBestSize() const | |
| { | |
|     wxSize size( 220, 480 ); | |
|     // TODO(JE) appropriate logic | |
|     return size; | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnNotebookPageChanged( wxNotebookEvent& aEvent ) | |
| { | |
|     // Work around wxMac issue where the notebook pages are blank | |
| #ifdef __WXMAC__ | |
|     int page = aEvent.GetSelection(); | |
| 
 | |
|     if( page >= 0 ) | |
|         m_notebook->ChangeSelection( static_cast<unsigned>( page ) ); | |
| #endif | |
|  | |
| #ifndef __WXMSW__ | |
|     // Because wxWidgets is broken and will send click events to children of the collapsible | |
|     // panes even if they are collapsed without this | |
|     Freeze(); | |
|     m_panelLayers->Fit(); | |
|     m_panelNetsAndClasses->Fit(); | |
|     m_sizerOuter->Layout(); | |
|     Thaw(); | |
| #endif | |
|  | |
|     Bind( wxEVT_IDLE, &APPEARANCE_CONTROLS::idleFocusHandler, this ); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::idleFocusHandler( wxIdleEvent& aEvent ) | |
| { | |
|     passOnFocus(); | |
|     Unbind( wxEVT_IDLE, &APPEARANCE_CONTROLS::idleFocusHandler, this ); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnSetFocus( wxFocusEvent& aEvent ) | |
| { | |
| #ifdef __WXMSW__ | |
|     // In wxMSW, buttons won't process events unless they have focus, so we'll let it take the | |
|     // focus and give it back to the parent in the button event handler. | |
|     if( wxBitmapButton* btn = dynamic_cast<wxBitmapButton*>( aEvent.GetEventObject() ) ) | |
|     { | |
|         wxCommandEvent evt( wxEVT_BUTTON ); | |
|         wxPostEvent( btn, evt ); | |
|     } | |
| #endif | |
|  | |
|     passOnFocus(); | |
|     aEvent.Skip(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnSize( wxSizeEvent& aEvent ) | |
| { | |
|     aEvent.Skip(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnNetGridClick( wxGridEvent& event ) | |
| { | |
|     int row = event.GetRow(); | |
|     int col = event.GetCol(); | |
| 
 | |
|     switch( col ) | |
|     { | |
|     case NET_GRID_TABLE::COL_VISIBILITY: | |
|         m_netsTable->SetValueAsBool( row, col, !m_netsTable->GetValueAsBool( row, col ) ); | |
|         m_netsGrid->ForceRefresh(); | |
|         break; | |
| 
 | |
|     default: | |
|         break; | |
|     } | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnNetGridDoubleClick( wxGridEvent& event ) | |
| { | |
|     int row = event.GetRow(); | |
|     int col = event.GetCol(); | |
| 
 | |
|     switch( col ) | |
|     { | |
|     case NET_GRID_TABLE::COL_COLOR: | |
|         m_netsGrid->GetCellEditor( row, col )->BeginEdit( row, col, m_netsGrid ); | |
|         break; | |
| 
 | |
|     default: | |
|         break; | |
|     } | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnNetGridRightClick( wxGridEvent& event ) | |
| { | |
|     m_netsGrid->SelectRow( event.GetRow() ); | |
| 
 | |
|     wxString netName = UnescapeString( m_netsGrid->GetCellValue( event.GetRow(), | |
|                                                                  NET_GRID_TABLE::COL_LABEL ) ); | |
|     wxMenu menu; | |
| 
 | |
|     menu.Append( new wxMenuItem( &menu, ID_SET_NET_COLOR, _( "Set Net Color" ), wxEmptyString, | |
|                                  wxITEM_NORMAL ) ); | |
|     menu.Append( new wxMenuItem( &menu, ID_HIGHLIGHT_NET, | |
|                                  wxString::Format( _( "Highlight %s" ), netName ), wxEmptyString, | |
|                                  wxITEM_NORMAL ) ); | |
|     menu.Append( new wxMenuItem( &menu, ID_SELECT_NET, | |
|                                  wxString::Format( _( "Select Tracks and Vias in %s" ), netName ), | |
|                                  wxEmptyString, wxITEM_NORMAL ) ); | |
|     menu.Append( new wxMenuItem( &menu, ID_DESELECT_NET, | |
|                                  wxString::Format( _( "Unselect Tracks and Vias in %s" ), netName ), | |
|                                  wxEmptyString, wxITEM_NORMAL ) ); | |
| 
 | |
|     menu.AppendSeparator(); | |
| 
 | |
|     menu.Append( new wxMenuItem( &menu, ID_SHOW_ALL_NETS, _( "Show All Nets" ), wxEmptyString, | |
|                                  wxITEM_NORMAL ) ); | |
|     menu.Append( new wxMenuItem( &menu, ID_HIDE_OTHER_NETS, _( "Hide All Other Nets" ), | |
|                                  wxEmptyString, wxITEM_NORMAL ) ); | |
| 
 | |
|     menu.Bind( wxEVT_COMMAND_MENU_SELECTED, &APPEARANCE_CONTROLS::onNetContextMenu, this ); | |
| 
 | |
|     PopupMenu( &menu ); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnNetGridMouseEvent( wxMouseEvent& aEvent ) | |
| { | |
|     wxPoint pos = m_netsGrid->CalcUnscrolledPosition( aEvent.GetPosition() ); | |
|     wxGridCellCoords cell = m_netsGrid->XYToCell( pos ); | |
| 
 | |
|     if( aEvent.Moving() || aEvent.Entering() ) | |
|     { | |
|         aEvent.Skip(); | |
| 
 | |
|         if( !cell ) | |
|         { | |
|             m_netsGrid->GetGridWindow()->UnsetToolTip(); | |
|             return; | |
|         } | |
| 
 | |
|         if( cell == m_hoveredCell ) | |
|             return; | |
| 
 | |
|         m_hoveredCell = cell; | |
| 
 | |
|         NET_GRID_ENTRY& net = m_netsTable->GetEntry( cell.GetRow() ); | |
| 
 | |
|         wxString name = net.name; | |
|         wxString showOrHide = net.visible ? _( "Click to hide ratsnest for %s" ) | |
|                                           : _( "Click to show ratsnest for %s" ); | |
|         wxString tip; | |
| 
 | |
|         if( cell.GetCol() == NET_GRID_TABLE::COL_VISIBILITY ) | |
|         { | |
|             tip.Printf( showOrHide, name ); | |
|         } | |
|         else if( cell.GetCol() == NET_GRID_TABLE::COL_COLOR ) | |
|         { | |
|             tip = _( "Double click (or middle click) to change color; " | |
|                      "right click for more actions" ); | |
|         } | |
| 
 | |
|         m_netsGrid->GetGridWindow()->SetToolTip( tip ); | |
|     } | |
|     else if( aEvent.Leaving() ) | |
|     { | |
|         m_netsGrid->UnsetToolTip(); | |
|         aEvent.Skip(); | |
|     } | |
|     else if( aEvent.Dragging() ) | |
|     { | |
|         // not allowed | |
|         CallAfter( [&]() | |
|                    { | |
|                        m_netsGrid->ClearSelection(); | |
|                    } ); | |
|     } | |
|     else if( aEvent.ButtonUp( wxMOUSE_BTN_MIDDLE ) && !!cell ) | |
|     { | |
|         int row = cell.GetRow(); | |
|         int col = cell.GetCol(); | |
| 
 | |
|         if(col == NET_GRID_TABLE::COL_COLOR ) | |
|             m_netsGrid->GetCellEditor( row, col )->BeginEdit( row, col, m_netsGrid ); | |
| 
 | |
|         aEvent.Skip(); | |
|     } | |
|     else | |
|     { | |
|         aEvent.Skip(); | |
|     } | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnLanguageChanged() | |
| { | |
|     m_notebook->SetPageText( 0, _( "Layers" ) ); | |
|     m_notebook->SetPageText( 1, _( "Objects" ) ); | |
|     m_notebook->SetPageText( 2, _( "Nets" ) ); | |
| 
 | |
|     Freeze(); | |
|     rebuildLayers(); | |
|     rebuildLayerContextMenu(); | |
|     rebuildLayerPresetsWidget(); | |
|     rebuildViewportsWidget(); | |
|     rebuildObjects(); | |
|     rebuildNets(); | |
| 
 | |
|     syncColorsAndVisibility(); | |
|     syncObjectSettings(); | |
|     syncLayerPresetSelection(); | |
| 
 | |
|     UpdateDisplayOptions(); | |
| 
 | |
|     Thaw(); | |
|     Refresh(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnBoardChanged() | |
| { | |
|     Freeze(); | |
|     rebuildLayers(); | |
|     rebuildLayerContextMenu(); | |
|     syncColorsAndVisibility(); | |
|     syncObjectSettings(); | |
|     rebuildNets(); | |
|     rebuildLayerPresetsWidget(); | |
|     syncLayerPresetSelection(); | |
|     rebuildViewportsWidget(); | |
| 
 | |
|     UpdateDisplayOptions(); | |
| 
 | |
|     m_board = m_frame->GetBoard(); | |
| 
 | |
|     if( m_board ) | |
|         m_board->AddListener( this ); | |
| 
 | |
|     Thaw(); | |
|     Refresh(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnBoardNetSettingsChanged( BOARD& aBoard ) | |
| { | |
|     handleBoardItemsChanged(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnNetVisibilityChanged( int aNetCode, bool aVisibility ) | |
| { | |
|     int row = m_netsTable->GetRowByNetcode( aNetCode ); | |
| 
 | |
|     if( row >= 0 ) | |
|     { | |
|         m_netsTable->SetValueAsBool( row, NET_GRID_TABLE::COL_VISIBILITY, aVisibility ); | |
|         m_netsGrid->ForceRefresh(); | |
|     } | |
| } | |
| 
 | |
| 
 | |
| bool APPEARANCE_CONTROLS::doesBoardItemNeedRebuild( BOARD_ITEM* aBoardItem ) | |
| { | |
|     return aBoardItem->Type() == PCB_NETINFO_T; | |
| } | |
| 
 | |
| 
 | |
| bool APPEARANCE_CONTROLS::doesBoardItemNeedRebuild( std::vector<BOARD_ITEM*>& aBoardItems ) | |
| { | |
|     bool rebuild = std::any_of( aBoardItems.begin(), aBoardItems.end(), | |
|                                 []( const BOARD_ITEM* a ) | |
|                                 { | |
|                                     return a->Type() == PCB_NETINFO_T; | |
|                                 } ); | |
| 
 | |
|     return rebuild; | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnBoardItemAdded( BOARD& aBoard, BOARD_ITEM* aItem ) | |
| { | |
|     if( doesBoardItemNeedRebuild( aItem ) ) | |
|         handleBoardItemsChanged(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnBoardItemsAdded( BOARD& aBoard, std::vector<BOARD_ITEM*>& aItems ) | |
| { | |
|     if( doesBoardItemNeedRebuild( aItems ) ) | |
|         handleBoardItemsChanged(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnBoardItemRemoved( BOARD& aBoard, BOARD_ITEM* aItem ) | |
| { | |
|     if( doesBoardItemNeedRebuild( aItem ) ) | |
|         handleBoardItemsChanged(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnBoardItemsRemoved( BOARD& aBoard, std::vector<BOARD_ITEM*>& aItems ) | |
| { | |
|     if( doesBoardItemNeedRebuild( aItems ) ) | |
|         handleBoardItemsChanged(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnBoardItemChanged( BOARD& aBoard, BOARD_ITEM* aItem ) | |
| { | |
|     if( doesBoardItemNeedRebuild( aItem ) ) | |
|         handleBoardItemsChanged(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnBoardItemsChanged( BOARD& aBoard, std::vector<BOARD_ITEM*>& aItems ) | |
| { | |
|     if( doesBoardItemNeedRebuild( aItems ) ) | |
|         handleBoardItemsChanged(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::handleBoardItemsChanged() | |
| { | |
|     Freeze(); | |
|     rebuildNets(); | |
|     Thaw(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnColorThemeChanged() | |
| { | |
|     syncColorsAndVisibility(); | |
|     syncObjectSettings(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnLayerChanged() | |
| { | |
|     for( const std::unique_ptr<APPEARANCE_SETTING>& setting : m_layerSettings ) | |
|     { | |
|         setting->ctl_panel->SetBackgroundColour( m_layerPanelColour ); | |
|         setting->ctl_indicator->SetIndicatorState( ROW_ICON_PROVIDER::STATE::OFF ); | |
|     } | |
| 
 | |
|     wxChar r, g, b; | |
| 
 | |
|     r = m_layerPanelColour.Red(); | |
|     g = m_layerPanelColour.Green(); | |
|     b = m_layerPanelColour.Blue(); | |
| 
 | |
|     if( r < 240 || g < 240 || b < 240 ) | |
|     { | |
|         r = wxChar( std::min( (int) r + 15, 255 ) ); | |
|         g = wxChar( std::min( (int) g + 15, 255 ) ); | |
|         b = wxChar( std::min( (int) b + 15, 255 ) ); | |
|     } | |
|     else | |
|     { | |
|         r = wxChar( std::max( (int) r - 15, 0 ) ); | |
|         g = wxChar( std::max( (int) g - 15, 0 ) ); | |
|         b = wxChar( std::max( (int) b - 15, 0 ) ); | |
|     } | |
| 
 | |
|     PCB_LAYER_ID current = m_frame->GetActiveLayer(); | |
| 
 | |
|     if( !m_layerSettingsMap.count( current ) ) | |
|     { | |
|         wxASSERT( m_layerSettingsMap.count( F_Cu ) ); | |
|         current = F_Cu; | |
|     } | |
| 
 | |
|     APPEARANCE_SETTING* newSetting = m_layerSettingsMap[ current ]; | |
| 
 | |
|     newSetting->ctl_panel->SetBackgroundColour( wxColour( r, g, b ) ); | |
|     newSetting->ctl_indicator->SetIndicatorState( ROW_ICON_PROVIDER::STATE::ON ); | |
| 
 | |
|     Refresh(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::SetLayerVisible( int aLayer, bool isVisible ) | |
| { | |
|     LSET         visible = getVisibleLayers(); | |
|     PCB_LAYER_ID layer   = ToLAYER_ID( aLayer ); | |
| 
 | |
|     if( visible.test( layer ) == isVisible ) | |
|         return; | |
| 
 | |
|     visible.set( layer, isVisible ); | |
|     setVisibleLayers( visible ); | |
| 
 | |
|     m_frame->GetCanvas()->GetView()->SetLayerVisible( layer, isVisible ); | |
| 
 | |
|     syncColorsAndVisibility(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::SetObjectVisible( GAL_LAYER_ID aLayer, bool isVisible ) | |
| { | |
|     if( m_objectSettingsMap.count( aLayer ) ) | |
|     { | |
|         APPEARANCE_SETTING* setting = m_objectSettingsMap.at( aLayer ); | |
|         setting->ctl_visibility->SetValue( isVisible ); | |
|     } | |
| 
 | |
|     m_frame->GetBoard()->SetElementVisibility( aLayer, isVisible ); | |
| 
 | |
|     m_frame->GetCanvas()->GetView()->SetLayerVisible( aLayer, isVisible ); | |
|     m_frame->GetCanvas()->Refresh(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::setVisibleLayers( LSET aLayers ) | |
| { | |
|     if( m_isFpEditor ) | |
|     { | |
|         KIGFX::VIEW* view = m_frame->GetCanvas()->GetView(); | |
| 
 | |
|         for( PCB_LAYER_ID layer : LSET::AllLayersMask().Seq() ) | |
|             view->SetLayerVisible( layer, aLayers.Contains( layer ) ); | |
|     } | |
|     else | |
|     { | |
|         m_frame->GetBoard()->SetVisibleLayers( aLayers ); | |
|     } | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::setVisibleObjects( GAL_SET aLayers ) | |
| { | |
|     if( m_isFpEditor ) | |
|     { | |
|         KIGFX::VIEW* view = m_frame->GetCanvas()->GetView(); | |
| 
 | |
|         for( size_t i = 0; i < GAL_LAYER_INDEX( LAYER_ZONE_START ); i++ ) | |
|             view->SetLayerVisible( GAL_LAYER_ID_START + GAL_LAYER_ID( i ), aLayers.test( i ) ); | |
|     } | |
|     else | |
|     { | |
|         // Ratsnest visibility is controlled by the ratsnest option, and not by the preset | |
|         if( m_frame->IsType( FRAME_PCB_EDITOR ) ) | |
|             aLayers.set( LAYER_RATSNEST, m_frame->GetPcbNewSettings()->m_Display.m_ShowGlobalRatsnest ); | |
| 
 | |
|         m_frame->GetBoard()->SetVisibleElements( aLayers ); | |
|     } | |
| } | |
| 
 | |
| 
 | |
| LSET APPEARANCE_CONTROLS::getVisibleLayers() | |
| { | |
|     if( m_isFpEditor ) | |
|     { | |
|         KIGFX::VIEW* view = m_frame->GetCanvas()->GetView(); | |
|         LSET set; | |
| 
 | |
|         for( PCB_LAYER_ID layer : LSET::AllLayersMask().Seq() ) | |
|             set.set( layer, view->IsLayerVisible( layer ) ); | |
| 
 | |
|         return set; | |
|     } | |
|     else | |
|     { | |
|         return m_frame->GetBoard()->GetVisibleLayers(); | |
|     } | |
| } | |
| 
 | |
| 
 | |
| GAL_SET APPEARANCE_CONTROLS::getVisibleObjects() | |
| { | |
|     if( m_isFpEditor ) | |
|     { | |
|         KIGFX::VIEW* view = m_frame->GetCanvas()->GetView(); | |
|         GAL_SET set; | |
|         set.reset(); | |
| 
 | |
|         for( size_t i = 0; i < set.size(); i++ ) | |
|             set.set( i, view->IsLayerVisible( GAL_LAYER_ID_START + GAL_LAYER_ID( i ) ) ); | |
| 
 | |
|         return set; | |
|     } | |
|     else | |
|     { | |
|         return m_frame->GetBoard()->GetVisibleElements(); | |
|     } | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::UpdateDisplayOptions() | |
| { | |
|     const PCB_DISPLAY_OPTIONS& options = m_frame->GetDisplayOptions(); | |
| 
 | |
|     switch( options.m_ContrastModeDisplay ) | |
|     { | |
|     case HIGH_CONTRAST_MODE::NORMAL: m_rbHighContrastNormal->SetValue( true ); break; | |
|     case HIGH_CONTRAST_MODE::DIMMED: m_rbHighContrastDim->SetValue( true );    break; | |
|     case HIGH_CONTRAST_MODE::HIDDEN: m_rbHighContrastOff->SetValue( true );    break; | |
|     } | |
| 
 | |
|     switch( options.m_NetColorMode ) | |
|     { | |
|     case NET_COLOR_MODE::ALL:      m_rbNetColorAll->SetValue( true );      break; | |
|     case NET_COLOR_MODE::RATSNEST: m_rbNetColorRatsnest->SetValue( true ); break; | |
|     case NET_COLOR_MODE::OFF:      m_rbNetColorOff->SetValue( true );      break; | |
|     } | |
| 
 | |
|     m_cbFlipBoard->SetValue( m_frame->GetCanvas()->GetView()->IsMirroredX() ); | |
| 
 | |
|     if( !m_isFpEditor ) | |
|     { | |
|         PCBNEW_SETTINGS* cfg = m_frame->GetPcbNewSettings(); | |
| 
 | |
|         if( !cfg->m_Display.m_ShowGlobalRatsnest ) | |
|             m_rbRatsnestNone->SetValue( true ); | |
|         else if( cfg->m_Display.m_RatsnestMode == RATSNEST_MODE::ALL ) | |
|             m_rbRatsnestAllLayers->SetValue( true ); | |
|         else | |
|             m_rbRatsnestVisLayers->SetValue( true ); | |
| 
 | |
|         wxASSERT( m_objectSettingsMap.count( LAYER_RATSNEST ) ); | |
|         APPEARANCE_SETTING* ratsnest = m_objectSettingsMap.at( LAYER_RATSNEST ); | |
|         ratsnest->ctl_visibility->SetValue( cfg->m_Display.m_ShowGlobalRatsnest ); | |
|     } | |
| } | |
| 
 | |
| 
 | |
| std::vector<LAYER_PRESET> APPEARANCE_CONTROLS::GetUserLayerPresets() const | |
| { | |
|     std::vector<LAYER_PRESET> ret; | |
| 
 | |
|     for( const std::pair<const wxString, LAYER_PRESET>& pair : m_layerPresets ) | |
|     { | |
|         if( !pair.second.readOnly ) | |
|             ret.emplace_back( pair.second ); | |
|     } | |
| 
 | |
|     return ret; | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::SetUserLayerPresets( std::vector<LAYER_PRESET>& aPresetList ) | |
| { | |
|     // Reset to defaults | |
|     loadDefaultLayerPresets(); | |
| 
 | |
|     for( const LAYER_PRESET& preset : aPresetList ) | |
|     { | |
|         if( m_layerPresets.count( preset.name ) ) | |
|             continue; | |
| 
 | |
|         m_layerPresets[preset.name] = preset; | |
| 
 | |
|         m_presetMRU.Add( preset.name ); | |
|     } | |
| 
 | |
|     rebuildLayerPresetsWidget(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::loadDefaultLayerPresets() | |
| { | |
|     m_layerPresets.clear(); | |
|     m_presetMRU.clear(); | |
| 
 | |
|     // Load the read-only defaults | |
|     for( const LAYER_PRESET& preset : { presetAllLayers, presetAllCopper, presetInnerCopper, | |
|                                         presetFront, presetFrontAssembly, presetBack, | |
|                                         presetBackAssembly } ) | |
|     { | |
|         m_layerPresets[preset.name]          = preset; | |
|         m_layerPresets[preset.name].readOnly = true; | |
| 
 | |
|         m_presetMRU.Add( preset.name ); | |
|     } | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::ApplyLayerPreset( const wxString& aPresetName ) | |
| { | |
|     updateLayerPresetSelection( aPresetName ); | |
| 
 | |
|     wxCommandEvent dummy; | |
|     onLayerPresetChanged( dummy ); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::ApplyLayerPreset( const LAYER_PRESET& aPreset ) | |
| { | |
|     if( m_layerPresets.count( aPreset.name ) ) | |
|         m_currentPreset = &m_layerPresets[aPreset.name]; | |
|     else | |
|         m_currentPreset = nullptr; | |
| 
 | |
|     m_lastSelectedUserPreset = ( m_currentPreset && !m_currentPreset->readOnly ) ? m_currentPreset | |
|                                                                                  : nullptr; | |
| 
 | |
|     updateLayerPresetSelection( aPreset.name ); | |
|     doApplyLayerPreset( aPreset ); | |
| } | |
| 
 | |
| 
 | |
| std::vector<VIEWPORT> APPEARANCE_CONTROLS::GetUserViewports() const | |
| { | |
|     std::vector<VIEWPORT> ret; | |
| 
 | |
|     for( const std::pair<const wxString, VIEWPORT>& pair : m_viewports ) | |
|         ret.emplace_back( pair.second ); | |
| 
 | |
|     return ret; | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::SetUserViewports( std::vector<VIEWPORT>& aViewportList ) | |
| { | |
|     m_viewports.clear(); | |
| 
 | |
|     for( const VIEWPORT& viewport : aViewportList ) | |
|     { | |
|         if( m_viewports.count( viewport.name ) ) | |
|             continue; | |
| 
 | |
|         m_viewports[viewport.name] = viewport; | |
| 
 | |
|         m_viewportMRU.Add( viewport.name ); | |
|     } | |
| 
 | |
|     rebuildViewportsWidget(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::ApplyViewport( const wxString& aViewportName ) | |
| { | |
|     updateViewportSelection( aViewportName ); | |
| 
 | |
|     wxCommandEvent dummy; | |
|     onViewportChanged( dummy ); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::ApplyViewport( const VIEWPORT& aViewport ) | |
| { | |
|     updateViewportSelection( aViewport.name ); | |
|     doApplyViewport( aViewport ); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::rebuildLayers() | |
| { | |
|     BOARD* board = m_frame->GetBoard(); | |
|     LSET enabled = board->GetEnabledLayers(); | |
|     LSET visible = getVisibleLayers(); | |
| 
 | |
|     COLOR_SETTINGS* theme    = m_frame->GetColorSettings(); | |
|     COLOR4D         bgColor  = theme->GetColor( LAYER_PCB_BACKGROUND ); | |
|     bool            readOnly = theme->IsReadOnly(); | |
| 
 | |
| #ifdef __WXMAC__ | |
|     wxSizerItem* m_windowLayersSizerItem = m_panelLayersSizer->GetItem( m_windowLayers ); | |
|     m_windowLayersSizerItem->SetFlag( m_windowLayersSizerItem->GetFlag() & ~wxTOP ); | |
| #endif | |
|  | |
|     auto appendLayer = | |
|             [&]( std::unique_ptr<APPEARANCE_SETTING>& aSetting ) | |
|             { | |
|                 int layer = aSetting->id; | |
| 
 | |
|                 wxPanel*    panel = new wxPanel( m_windowLayers, layer ); | |
|                 wxBoxSizer* sizer = new wxBoxSizer( wxHORIZONTAL ); | |
|                 panel->SetSizer( sizer ); | |
| 
 | |
|                 panel->SetBackgroundColour( m_layerPanelColour ); | |
| 
 | |
|                 aSetting->visible = visible[layer]; | |
| 
 | |
|                 // TODO(JE) consider restyling this indicator | |
|                 INDICATOR_ICON* indicator = new INDICATOR_ICON( panel, *m_iconProvider, | |
|                                                                 ROW_ICON_PROVIDER::STATE::OFF, | |
|                                                                 layer ); | |
| 
 | |
|                 COLOR_SWATCH* swatch = new COLOR_SWATCH( panel, COLOR4D::UNSPECIFIED, layer, | |
|                                                          bgColor, theme->GetColor( layer ), | |
|                                                          SWATCH_SMALL ); | |
|                 swatch->SetToolTip( _( "Double click or middle click for color change, " | |
|                                        "right click for menu" ) ); | |
| 
 | |
|                 BITMAP_TOGGLE* btn_visible = new BITMAP_TOGGLE( panel, layer, | |
|                                                                 KiBitmap( BITMAPS::visibility ), | |
|                                                                 KiBitmap( BITMAPS::visibility_off ), | |
|                                                                 aSetting->visible ); | |
|                 btn_visible->SetToolTip( _( "Show or hide this layer" ) ); | |
| 
 | |
|                 wxStaticText* label = new wxStaticText( panel, layer, aSetting->label ); | |
|                 label->Wrap( -1 ); | |
|                 label->SetToolTip( aSetting->tooltip ); | |
| 
 | |
|                 sizer->AddSpacer( 1 ); | |
|                 sizer->Add( indicator, 0, wxALIGN_CENTER_VERTICAL | wxTOP, 2 ); | |
|                 sizer->AddSpacer( 5 ); | |
|                 sizer->Add( swatch, 0, wxALIGN_CENTER_VERTICAL | wxTOP, 2 ); | |
|                 sizer->AddSpacer( 6 ); | |
|                 sizer->Add( btn_visible, 0, wxALIGN_CENTER_VERTICAL | wxTOP, 2 ); | |
|                 sizer->AddSpacer( 5 ); | |
|                 sizer->Add( label, 1, wxALIGN_CENTER_VERTICAL | wxTOP, 2 ); | |
| 
 | |
|                 m_layersOuterSizer->Add( panel, 0, wxEXPAND, 0 ); | |
| 
 | |
|                 aSetting->ctl_panel      = panel; | |
|                 aSetting->ctl_indicator  = indicator; | |
|                 aSetting->ctl_visibility = btn_visible; | |
|                 aSetting->ctl_color      = swatch; | |
|                 aSetting->ctl_text       = label; | |
| 
 | |
|                 panel->Bind( wxEVT_LEFT_DOWN, &APPEARANCE_CONTROLS::onLayerLeftClick, this ); | |
|                 indicator->Bind( wxEVT_LEFT_DOWN, &APPEARANCE_CONTROLS::onLayerLeftClick, this ); | |
|                 swatch->Bind( wxEVT_LEFT_DOWN, &APPEARANCE_CONTROLS::onLayerLeftClick, this ); | |
|                 label->Bind( wxEVT_LEFT_DOWN, &APPEARANCE_CONTROLS::onLayerLeftClick, this ); | |
| 
 | |
|                 btn_visible->Bind( TOGGLE_CHANGED, | |
|                         [&]( wxCommandEvent& aEvent ) | |
|                         { | |
|                             wxObject* btn = aEvent.GetEventObject(); | |
|                             int       layerId = static_cast<wxWindow*>( btn )->GetId(); | |
| 
 | |
|                             onLayerVisibilityToggled( static_cast<PCB_LAYER_ID>( layerId ) ); | |
|                         } ); | |
| 
 | |
|                 swatch->Bind( COLOR_SWATCH_CHANGED, &APPEARANCE_CONTROLS::OnColorSwatchChanged, | |
|                               this ); | |
|                 swatch->SetReadOnlyCallback( std::bind( &APPEARANCE_CONTROLS::onReadOnlySwatch, | |
|                                                         this ) ); | |
|                 swatch->SetReadOnly( readOnly ); | |
| 
 | |
|                 panel->Bind( wxEVT_RIGHT_DOWN, &APPEARANCE_CONTROLS::rightClickHandler, this ); | |
|                 indicator->Bind( wxEVT_RIGHT_DOWN, &APPEARANCE_CONTROLS::rightClickHandler, this ); | |
|                 swatch->Bind( wxEVT_RIGHT_DOWN, &APPEARANCE_CONTROLS::rightClickHandler, this ); | |
|                 btn_visible->Bind( wxEVT_RIGHT_DOWN, &APPEARANCE_CONTROLS::rightClickHandler, this ); | |
|                 label->Bind( wxEVT_RIGHT_DOWN, &APPEARANCE_CONTROLS::rightClickHandler, this ); | |
|             }; | |
| 
 | |
|     auto updateLayer = | |
|             [&]( std::unique_ptr<APPEARANCE_SETTING>& aSetting ) | |
|             { | |
|                 int layer = aSetting->id; | |
|                 aSetting->visible = visible[layer]; | |
|                 aSetting->ctl_panel->Show(); | |
|                 aSetting->ctl_panel->SetId( layer ); | |
|                 aSetting->ctl_indicator->SetWindowID( layer ); | |
|                 aSetting->ctl_color->SetWindowID( layer ); | |
|                 aSetting->ctl_color->SetSwatchColor( theme->GetColor( layer ), false ); | |
|                 aSetting->ctl_visibility->SetWindowID( layer ); | |
|                 aSetting->ctl_text->SetLabelText( aSetting->label ); | |
|                 aSetting->ctl_text->SetId( layer ); | |
|                 aSetting->ctl_text->SetToolTip( aSetting->tooltip ); | |
|             }; | |
| 
 | |
|     // technical layers are shown in this order: | |
|     // Because they are static, wxGetTranslation must be explicitly | |
|     // called for tooltips. | |
|     static const struct { | |
|         PCB_LAYER_ID layerId; | |
|         wxString     tooltip; | |
|     } non_cu_seq[] = { | |
|         { F_Adhes,          _HKI( "Adhesive on board's front" ) }, | |
|         { B_Adhes,          _HKI( "Adhesive on board's back" ) }, | |
|         { F_Paste,          _HKI( "Solder paste on board's front" ) }, | |
|         { B_Paste,          _HKI( "Solder paste on board's back" ) }, | |
|         { F_SilkS,          _HKI( "Silkscreen on board's front" ) }, | |
|         { B_SilkS,          _HKI( "Silkscreen on board's back" ) }, | |
|         { F_Mask,           _HKI( "Solder mask on board's front" ) }, | |
|         { B_Mask,           _HKI( "Solder mask on board's back" ) }, | |
|         { Dwgs_User,        _HKI( "Explanatory drawings" ) }, | |
|         { Cmts_User,        _HKI( "Explanatory comments" ) }, | |
|         { Eco1_User,        _HKI( "User defined meaning" ) }, | |
|         { Eco2_User,        _HKI( "User defined meaning" ) }, | |
|         { Edge_Cuts,        _HKI( "Board's perimeter definition" ) }, | |
|         { Margin,           _HKI( "Board's edge setback outline" ) }, | |
|         { F_CrtYd,          _HKI( "Footprint courtyards on board's front" ) }, | |
|         { B_CrtYd,          _HKI( "Footprint courtyards on board's back" ) }, | |
|         { F_Fab,            _HKI( "Footprint assembly on board's front" ) }, | |
|         { B_Fab,            _HKI( "Footprint assembly on board's back" ) }, | |
|         { User_1,           _HKI( "User defined layer 1" ) }, | |
|         { User_2,           _HKI( "User defined layer 2" ) }, | |
|         { User_3,           _HKI( "User defined layer 3" ) }, | |
|         { User_4,           _HKI( "User defined layer 4" ) }, | |
|         { User_5,           _HKI( "User defined layer 5" ) }, | |
|         { User_6,           _HKI( "User defined layer 6" ) }, | |
|         { User_7,           _HKI( "User defined layer 7" ) }, | |
|         { User_8,           _HKI( "User defined layer 8" ) }, | |
|         { User_9,           _HKI( "User defined layer 9" ) }, | |
|     }; | |
| 
 | |
|     // There is a spacer added to the end of the list that we need to remove and re-add | |
|     // after possibly adding additional layers | |
|     if( m_layersOuterSizer->GetItemCount() > 0 ) | |
|     { | |
|         m_layersOuterSizer->Detach( m_layersOuterSizer->GetItemCount() - 1 ); | |
|     } | |
|     // Otherwise, this is the first time we are updating the control, so we need to attach | |
|     // the handler | |
|     else | |
|     { | |
|         // Add right click handling to show the context menu when clicking to the free area in | |
|         // m_windowLayers (below the layer items) | |
|         m_windowLayers->Bind( wxEVT_RIGHT_DOWN, &APPEARANCE_CONTROLS::rightClickHandler, this ); | |
|     } | |
| 
 | |
|     std::size_t total_layers = enabled.CuStack().size(); | |
| 
 | |
|     for( const auto& entry : non_cu_seq ) | |
|     { | |
|         if( enabled[entry.layerId] ) | |
|             total_layers++; | |
|     } | |
| 
 | |
|     // Adds layers to the panel until we have enough to hold our total count | |
|     while( total_layers > m_layerSettings.size() ) | |
|         m_layerSettings.push_back( std::make_unique<APPEARANCE_SETTING>() ); | |
| 
 | |
|     // We never delete layers from the panel, only hide them.  This saves us | |
|     // having to recreate the (possibly) later with minimal overhead | |
|     for( std::size_t ii = total_layers; ii < m_layerSettings.size(); ++ii ) | |
|     { | |
|         if( m_layerSettings[ii]->ctl_panel ) | |
|             m_layerSettings[ii]->ctl_panel->Show( false ); | |
|     } | |
| 
 | |
|     auto layer_it = m_layerSettings.begin(); | |
| 
 | |
|     // show all coppers first, with front on top, back on bottom, then technical layers | |
|     for( LSEQ cu_stack = enabled.CuStack(); cu_stack; ++cu_stack, ++layer_it ) | |
|     { | |
|         PCB_LAYER_ID layer = *cu_stack; | |
|         wxString dsc; | |
| 
 | |
|         switch( layer ) | |
|         { | |
|             case F_Cu: dsc = _( "Front copper layer" ); break; | |
|             case B_Cu: dsc = _( "Back copper layer" );  break; | |
|             default:   dsc = _( "Inner copper layer" ); break; | |
|         } | |
| 
 | |
|         std::unique_ptr<APPEARANCE_SETTING>& setting = *layer_it; | |
| 
 | |
|         setting->label = board->GetLayerName( layer ); | |
|         setting->id = layer; | |
|         setting->tooltip = dsc; | |
| 
 | |
|         if( setting->ctl_panel == nullptr ) | |
|             appendLayer( setting ); | |
|         else | |
|             updateLayer( setting ); | |
| 
 | |
|         m_layerSettingsMap[layer] = setting.get(); | |
| 
 | |
|         if( m_isFpEditor && LSET::ForbiddenFootprintLayers().test( layer ) ) | |
|         { | |
|             setting->ctl_text->Disable(); | |
|             setting->ctl_color->SetToolTip( wxEmptyString ); | |
|         } | |
|     } | |
| 
 | |
|     for( const auto& entry : non_cu_seq ) | |
|     { | |
|         PCB_LAYER_ID layer = entry.layerId; | |
| 
 | |
|         if( !enabled[layer] ) | |
|             continue; | |
| 
 | |
|         std::unique_ptr<APPEARANCE_SETTING>& setting = *layer_it; | |
| 
 | |
|         setting->label = board->GetLayerName( layer ); | |
|         setting->id = layer; | |
|         // Because non_cu_seq is created static, we must explicitly call wxGetTranslation for | |
|         // texts which are internationalized | |
|         setting->tooltip = wxGetTranslation( entry.tooltip ); | |
| 
 | |
|         if( setting->ctl_panel == nullptr ) | |
|             appendLayer( setting ); | |
|         else | |
|             updateLayer( setting ); | |
| 
 | |
|         m_layerSettingsMap[layer] = setting.get(); | |
| 
 | |
|         if( m_isFpEditor && LSET::ForbiddenFootprintLayers().test( layer ) ) | |
|         { | |
|             setting->ctl_text->Disable(); | |
|             setting->ctl_color->SetToolTip( wxEmptyString ); | |
|         } | |
| 
 | |
|         ++layer_it; | |
|     } | |
| 
 | |
|     m_layersOuterSizer->AddSpacer( 10 ); | |
|     m_windowLayers->SetBackgroundColour( m_layerPanelColour ); | |
|     m_windowLayers->Layout(); | |
| 
 | |
|     m_paneLayerDisplayOptions->SetLabel( _( "Layer Display Options" ) ); | |
| 
 | |
|     int hotkey = PCB_ACTIONS::highContrastModeCycle.GetHotKey(); | |
|     wxString msg; | |
| 
 | |
|     if( hotkey ) | |
|         msg = wxString::Format( _( "Inactive layers (%s):" ), KeyNameFromKeyCode( hotkey ) ); | |
|     else | |
|         msg = _( "Inactive layers:" ); | |
| 
 | |
|     m_inactiveLayersLabel->SetLabel( msg ); | |
| 
 | |
|     m_rbHighContrastNormal->SetLabel( _( "Normal" ) ); | |
|     m_rbHighContrastNormal->SetToolTip( _( "Inactive layers will be shown in full color" ) ); | |
| 
 | |
|     m_rbHighContrastDim->SetLabel( _( "Dim" ) ); | |
|     m_rbHighContrastDim->SetToolTip( _( "Inactive layers will be dimmed" ) ); | |
| 
 | |
|     m_rbHighContrastOff->SetLabel( _( "Hide" ) ); | |
|     m_rbHighContrastOff->SetToolTip( _( "Inactive layers will be hidden" ) ); | |
| 
 | |
|     m_cbFlipBoard->SetLabel( _( "Flip board view" ) ); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::rebuildLayerContextMenu() | |
| { | |
|     delete m_layerContextMenu; | |
|     m_layerContextMenu = new wxMenu; | |
| 
 | |
|     AddMenuItem( m_layerContextMenu, ID_SHOW_ALL_COPPER_LAYERS, _( "Show All Copper Layers" ), | |
|                  KiBitmap( BITMAPS::show_all_copper_layers ) ); | |
|     AddMenuItem( m_layerContextMenu, ID_HIDE_ALL_COPPER_LAYERS, _( "Hide All Copper Layers" ), | |
|                  KiBitmap( BITMAPS::show_no_copper_layers ) ); | |
| 
 | |
|     m_layerContextMenu->AppendSeparator(); | |
| 
 | |
|     AddMenuItem( m_layerContextMenu, ID_HIDE_ALL_BUT_ACTIVE, _( "Hide All Layers But Active" ), | |
|                  KiBitmap( BITMAPS::select_w_layer ) ); | |
| 
 | |
|     m_layerContextMenu->AppendSeparator(); | |
| 
 | |
|     AddMenuItem( m_layerContextMenu, ID_SHOW_ALL_NON_COPPER, _( "Show All Non Copper Layers" ), | |
|                  KiBitmap( BITMAPS::show_no_copper_layers ) ); | |
| 
 | |
|     AddMenuItem( m_layerContextMenu, ID_HIDE_ALL_NON_COPPER, _( "Hide All Non Copper Layers" ), | |
|                  KiBitmap( BITMAPS::show_all_copper_layers ) ); | |
| 
 | |
|     m_layerContextMenu->AppendSeparator(); | |
| 
 | |
|     AddMenuItem( m_layerContextMenu, ID_PRESET_ALL_LAYERS, _( "Show All Layers" ), | |
|                  KiBitmap( BITMAPS::show_all_layers ) ); | |
| 
 | |
|     AddMenuItem( m_layerContextMenu, ID_PRESET_NO_LAYERS, _( "Hide All Layers" ), | |
|                  KiBitmap( BITMAPS::show_no_layers ) ); | |
| 
 | |
|     m_layerContextMenu->AppendSeparator(); | |
| 
 | |
|     AddMenuItem( m_layerContextMenu, ID_PRESET_FRONT_ASSEMBLY, _( "Show Only Front Assembly Layers" ), | |
|                  KiBitmap( BITMAPS::show_front_assembly_layers ) ); | |
| 
 | |
|     AddMenuItem( m_layerContextMenu, ID_PRESET_FRONT, _( "Show Only Front Layers" ), | |
|                  KiBitmap( BITMAPS::show_all_front_layers ) ); | |
| 
 | |
|     // Only show the internal layer option if internal layers are enabled | |
|     if( m_frame->GetBoard()->GetCopperLayerCount() > 2 ) | |
|     { | |
|         AddMenuItem( m_layerContextMenu, ID_PRESET_INNER_COPPER, _( "Show Only Inner Layers" ), | |
|                      KiBitmap( BITMAPS::show_all_copper_layers ) ); | |
|     } | |
| 
 | |
|     AddMenuItem( m_layerContextMenu, ID_PRESET_BACK, _( "Show Only Back Layers" ), | |
|                  KiBitmap( BITMAPS::show_all_back_layers ) ); | |
| 
 | |
|     AddMenuItem( m_layerContextMenu, ID_PRESET_BACK_ASSEMBLY, _( "Show Only Back Assembly Layers" ), | |
|                  KiBitmap( BITMAPS::show_back_assembly_layers ) ); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnLayerContextMenu( wxCommandEvent& aEvent ) | |
| { | |
|     BOARD* board   = m_frame->GetBoard(); | |
|     LSET   visible = getVisibleLayers(); | |
| 
 | |
|     PCB_LAYER_ID current = m_frame->GetActiveLayer(); | |
| 
 | |
|     switch( aEvent.GetId() ) | |
|     { | |
|     case ID_PRESET_NO_LAYERS: | |
|         ApplyLayerPreset( presetNoLayers ); | |
|         return; | |
| 
 | |
|     case ID_PRESET_ALL_LAYERS: | |
|         ApplyLayerPreset( presetAllLayers ); | |
|         return; | |
| 
 | |
|     case ID_SHOW_ALL_COPPER_LAYERS: | |
|     { | |
|         visible |= presetAllCopper.layers; | |
|         setVisibleLayers( visible ); | |
|         break; | |
|     } | |
| 
 | |
|     case ID_HIDE_ALL_BUT_ACTIVE: | |
|         ApplyLayerPreset( presetNoLayers ); | |
|         SetLayerVisible( current, true ); | |
|         break; | |
| 
 | |
|     case ID_HIDE_ALL_COPPER_LAYERS: | |
|     { | |
|         visible &= ~presetAllCopper.layers; | |
| 
 | |
|         if( !visible.test( current ) && visible.count() > 0 ) | |
|             m_frame->SetActiveLayer( *visible.Seq().begin() ); | |
| 
 | |
|         setVisibleLayers( visible ); | |
|         break; | |
|     } | |
| 
 | |
|     case ID_HIDE_ALL_NON_COPPER: | |
|     { | |
|         visible &= presetAllCopper.layers; | |
| 
 | |
|         if( !visible.test( current ) && visible.count() > 0 ) | |
|             m_frame->SetActiveLayer( *visible.Seq().begin() ); | |
| 
 | |
|         setVisibleLayers( visible ); | |
|         break; | |
|     } | |
| 
 | |
|     case ID_SHOW_ALL_NON_COPPER: | |
|     { | |
|         visible |= ~presetAllCopper.layers; | |
| 
 | |
|         setVisibleLayers( visible ); | |
|         break; | |
|     } | |
| 
 | |
|     case ID_PRESET_FRONT_ASSEMBLY: | |
|         ApplyLayerPreset( presetFrontAssembly ); | |
|         return; | |
| 
 | |
|     case ID_PRESET_FRONT: | |
|         ApplyLayerPreset( presetFront ); | |
|         return; | |
| 
 | |
|     case ID_PRESET_INNER_COPPER: | |
|         ApplyLayerPreset( presetInnerCopper ); | |
|         return; | |
| 
 | |
|     case ID_PRESET_BACK: | |
|         ApplyLayerPreset( presetBack ); | |
|         return; | |
| 
 | |
|     case ID_PRESET_BACK_ASSEMBLY: | |
|         ApplyLayerPreset( presetBackAssembly ); | |
|         return; | |
|     } | |
| 
 | |
|     syncLayerPresetSelection(); | |
|     syncColorsAndVisibility(); | |
| 
 | |
|     if( !m_isFpEditor ) | |
|         m_frame->GetCanvas()->SyncLayersVisibility( board ); | |
| 
 | |
|     m_frame->GetCanvas()->Refresh(); | |
| } | |
| 
 | |
| 
 | |
| int APPEARANCE_CONTROLS::GetTabIndex() const | |
| { | |
|     return m_notebook->GetSelection(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::SetTabIndex( int aTab ) | |
| { | |
|     size_t max = m_notebook->GetPageCount(); | |
| 
 | |
|     if( aTab >= 0 && static_cast<size_t>( aTab ) < max ) | |
|         m_notebook->SetSelection( aTab ); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::syncColorsAndVisibility() | |
| { | |
|     COLOR_SETTINGS* theme    = m_frame->GetColorSettings(); | |
|     bool            readOnly = theme->IsReadOnly(); | |
|     LSET            visible  = getVisibleLayers(); | |
|     GAL_SET         objects  = getVisibleObjects(); | |
| 
 | |
|     Freeze(); | |
| 
 | |
|     for( std::unique_ptr<APPEARANCE_SETTING>& setting : m_layerSettings ) | |
|     { | |
|         int layer = setting->id; | |
| 
 | |
|         if( setting->ctl_visibility ) | |
|             setting->ctl_visibility->SetValue( visible[layer] ); | |
| 
 | |
|         if( setting->ctl_color ) | |
|         { | |
|             const COLOR4D& color = theme->GetColor( layer ); | |
|             setting->ctl_color->SetSwatchColor( color, false ); | |
|             setting->ctl_color->SetReadOnly( readOnly ); | |
|         } | |
|     } | |
| 
 | |
|     for( std::unique_ptr<APPEARANCE_SETTING>& setting : m_objectSettings ) | |
|     { | |
|         GAL_LAYER_ID layer = static_cast<GAL_LAYER_ID>( setting->id ); | |
| 
 | |
|         if( setting->ctl_visibility ) | |
|             setting->ctl_visibility->SetValue( objects.Contains( layer ) ); | |
| 
 | |
|         if( setting->ctl_color ) | |
|         { | |
|             const COLOR4D& color = theme->GetColor( layer ); | |
|             setting->ctl_color->SetSwatchColor( color, false ); | |
|             setting->ctl_color->SetReadOnly( readOnly ); | |
|         } | |
|     } | |
| 
 | |
|     // Update indicators and panel background colors | |
|     OnLayerChanged(); | |
| 
 | |
|     Thaw(); | |
| 
 | |
|     m_windowLayers->Refresh(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::onLayerLeftClick( wxMouseEvent& aEvent ) | |
| { | |
|     auto eventSource = static_cast<wxWindow*>( aEvent.GetEventObject() ); | |
| 
 | |
|     PCB_LAYER_ID layer = ToLAYER_ID( eventSource->GetId() ); | |
| 
 | |
|     if( m_isFpEditor && LSET::ForbiddenFootprintLayers().test( layer ) ) | |
|         return; | |
| 
 | |
|     m_frame->SetActiveLayer( layer ); | |
|     passOnFocus(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::rightClickHandler( wxMouseEvent& aEvent ) | |
| { | |
|     wxASSERT( m_layerContextMenu ); | |
|     PopupMenu( m_layerContextMenu ); | |
|     passOnFocus(); | |
| }; | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::onLayerVisibilityToggled( PCB_LAYER_ID aLayer ) | |
| { | |
|     LSET visibleLayers = getVisibleLayers(); | |
| 
 | |
|     visibleLayers.set( aLayer, !visibleLayers.test( aLayer ) ); | |
|     setVisibleLayers( visibleLayers ); | |
|     m_frame->GetCanvas()->GetView()->SetLayerVisible( aLayer, visibleLayers.test( aLayer ) ); | |
| 
 | |
|     syncLayerPresetSelection(); | |
|     m_frame->GetCanvas()->Refresh(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::onObjectVisibilityChanged( GAL_LAYER_ID aLayer, bool isVisible, | |
|                                                      bool isFinal ) | |
| { | |
|     // Special-case controls | |
|     switch( aLayer ) | |
|     { | |
|     case LAYER_RATSNEST: | |
|     { | |
|         // don't touch the layers. ratsnest is enabled on per-item basis. | |
|         m_frame->GetCanvas()->GetView()->MarkTargetDirty( KIGFX::TARGET_NONCACHED ); | |
|         m_frame->GetCanvas()->GetView()->SetLayerVisible( aLayer, true ); | |
| 
 | |
|         if( m_frame->IsType( FRAME_PCB_EDITOR ) ) | |
|         { | |
|             m_frame->GetPcbNewSettings()->m_Display.m_ShowGlobalRatsnest = isVisible; | |
|             m_frame->GetBoard()->SetElementVisibility( aLayer, isVisible ); | |
|             m_frame->GetCanvas()->RedrawRatsnest(); | |
|         } | |
| 
 | |
|         break; | |
|     } | |
| 
 | |
|     case LAYER_GRID: | |
|         m_frame->SetGridVisibility( isVisible ); | |
|         m_frame->GetCanvas()->Refresh(); | |
|         syncLayerPresetSelection(); | |
|         break; | |
| 
 | |
|     case LAYER_MOD_TEXT: | |
|         // Because Footprint Text is a meta-control that also can disable values/references, | |
|         // drag them along here so that the user is less likely to be confused. | |
|         if( isFinal ) | |
|         { | |
|             // Should only trigger when you actually click the Footprint Text button | |
|             // Otherwise it goes into infinite recursive loop with the following case section | |
|             onObjectVisibilityChanged( LAYER_MOD_REFERENCES, isVisible, false ); | |
|             onObjectVisibilityChanged( LAYER_MOD_VALUES, isVisible, false ); | |
|             m_objectSettingsMap[LAYER_MOD_REFERENCES]->ctl_visibility->SetValue( isVisible ); | |
|             m_objectSettingsMap[LAYER_MOD_VALUES]->ctl_visibility->SetValue( isVisible ); | |
|         } | |
|         break; | |
| 
 | |
|     case LAYER_MOD_REFERENCES: | |
|     case LAYER_MOD_VALUES: | |
|         // In case that user changes Footprint Value/References when the Footprint Text | |
|         // meta-control is disabled, we should put it back on. | |
|         if( isVisible ) | |
|         { | |
|             onObjectVisibilityChanged( LAYER_MOD_TEXT, isVisible, false ); | |
|             m_objectSettingsMap[LAYER_MOD_TEXT]->ctl_visibility->SetValue( isVisible ); | |
|         } | |
|         break; | |
| 
 | |
|     default: | |
|         break; | |
|     } | |
| 
 | |
|     GAL_SET visible = getVisibleObjects(); | |
| 
 | |
|     if( visible.Contains( aLayer ) != isVisible ) | |
|     { | |
|         visible.set( aLayer, isVisible ); | |
|         setVisibleObjects( visible ); | |
|         m_frame->GetCanvas()->GetView()->SetLayerVisible( aLayer, isVisible ); | |
|         syncLayerPresetSelection(); | |
|     } | |
| 
 | |
|     if( isFinal ) | |
|     { | |
|         m_frame->GetCanvas()->Refresh(); | |
|         passOnFocus(); | |
|     } | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::rebuildObjects() | |
| { | |
|     COLOR_SETTINGS* theme   = m_frame->GetColorSettings(); | |
|     COLOR4D         bgColor = theme->GetColor( LAYER_PCB_BACKGROUND ); | |
|     GAL_SET         visible = getVisibleObjects(); | |
|     int             swatchWidth = m_windowObjects->ConvertDialogToPixels( wxSize( 8, 0 ) ).x; | |
|     int             labelWidth = 0; | |
| 
 | |
|     m_objectSettings.clear(); | |
|     m_objectsOuterSizer->Clear( true ); | |
|     m_objectsOuterSizer->AddSpacer( 5 ); | |
| 
 | |
|     auto appendObject = | |
|             [&]( const std::unique_ptr<APPEARANCE_SETTING>& aSetting ) | |
|             { | |
|                 wxBoxSizer* sizer = new wxBoxSizer( wxHORIZONTAL ); | |
|                 int         layer = aSetting->id; | |
| 
 | |
|                 aSetting->visible = visible.Contains( ToGalLayer( layer ) ); | |
|                 COLOR4D color     = theme->GetColor( layer ); | |
|                 COLOR4D defColor  = theme->GetDefaultColor( layer ); | |
| 
 | |
|                 if( color != COLOR4D::UNSPECIFIED ) | |
|                 { | |
|                     COLOR_SWATCH* swatch = new COLOR_SWATCH( m_windowObjects, color, layer, | |
|                                                              bgColor, defColor, SWATCH_SMALL ); | |
|                     swatch->SetToolTip( _( "Left double click or middle click for color change, " | |
|                                            "right click for menu" ) ); | |
| 
 | |
|                     sizer->Add( swatch, 0,  wxALIGN_CENTER_VERTICAL, 0 ); | |
|                     aSetting->ctl_color = swatch; | |
| 
 | |
|                     swatch->Bind( COLOR_SWATCH_CHANGED, | |
|                                   &APPEARANCE_CONTROLS::OnColorSwatchChanged, this ); | |
| 
 | |
|                     swatch->SetReadOnlyCallback( std::bind( &APPEARANCE_CONTROLS::onReadOnlySwatch, | |
|                                                             this ) ); | |
|                 } | |
|                 else | |
|                 { | |
|                     sizer->AddSpacer( swatchWidth  ); | |
|                 } | |
| 
 | |
|                 BITMAP_TOGGLE* btn_visible = new BITMAP_TOGGLE( m_windowObjects, layer, | |
|                                                                 KiBitmap( BITMAPS::visibility ), | |
|                                                                 KiBitmap( BITMAPS::visibility_off ), | |
|                                                                 aSetting->visible ); | |
| 
 | |
|                 wxString tip; | |
|                 tip.Printf( _( "Show or hide %s" ), aSetting->label.Lower() ); | |
|                 btn_visible->SetToolTip( tip ); | |
| 
 | |
|                 aSetting->ctl_visibility = btn_visible; | |
| 
 | |
|                 sizer->AddSpacer( 5 ); | |
| 
 | |
|                 btn_visible->Bind( TOGGLE_CHANGED, | |
|                         [&]( wxCommandEvent& aEvent ) | |
|                         { | |
|                             int id = static_cast<wxWindow*>( aEvent.GetEventObject() )->GetId(); | |
|                             bool isVisible = aEvent.GetInt(); | |
|                             onObjectVisibilityChanged( ToGalLayer( id ), isVisible, true ); | |
|                         } ); | |
| 
 | |
|                 wxStaticText* label = new wxStaticText( m_windowObjects, layer, aSetting->label ); | |
|                 label->Wrap( -1 ); | |
|                 label->SetToolTip( aSetting->tooltip ); | |
| 
 | |
|                 if( aSetting->can_control_opacity ) | |
|                 { | |
|                     label->SetMinSize( wxSize( labelWidth, -1 ) ); | |
| #ifdef __WXMAC__ | |
|                     sizer->Add( btn_visible, 0, wxALIGN_CENTER_VERTICAL | wxBOTTOM, 10 ); | |
|                     sizer->AddSpacer( 5 ); | |
|                     sizer->Add( label, 0, wxALIGN_CENTER_VERTICAL | wxBOTTOM, 10 ); | |
| #else | |
|                     sizer->Add( btn_visible, 0, wxALIGN_CENTER_VERTICAL, 0 ); | |
|                     sizer->AddSpacer( 5 ); | |
|                     sizer->Add( label, 0, wxALIGN_CENTER_VERTICAL, 0 ); | |
| #endif | |
|  | |
|                     wxSlider* slider = new wxSlider( m_windowObjects, wxID_ANY, 100, 0, 100, | |
|                                                      wxDefaultPosition, wxDefaultSize, | |
|                                                      wxSL_HORIZONTAL ); | |
| #ifdef __WXMAC__ | |
|                     slider->SetMinSize( wxSize( 80, 16 ) ); | |
| #else | |
|                     slider->SetMinSize( wxSize( 80, -1 ) ); | |
| #endif | |
|  | |
|                     tip.Printf( _( "Set opacity of %s" ), aSetting->label.Lower() ); | |
|                     slider->SetToolTip( tip ); | |
| 
 | |
|                     sizer->Add( slider, 1, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 5 ); | |
|                     aSetting->ctl_opacity = slider; | |
| 
 | |
|                     auto opacitySliderHandler = | |
|                             [=]( wxCommandEvent& aEvent ) | |
|                             { | |
|                                 wxSlider* ctrl = static_cast<wxSlider*>( aEvent.GetEventObject() ); | |
|                                 int value = ctrl->GetValue(); | |
|                                 onObjectOpacitySlider( layer, value / 100.0f ); | |
|                             }; | |
| 
 | |
|                     slider->Bind( wxEVT_SCROLL_CHANGED, opacitySliderHandler ); | |
|                     slider->Bind( wxEVT_SCROLL_THUMBTRACK, opacitySliderHandler ); | |
|                     slider->Bind( wxEVT_SET_FOCUS, &APPEARANCE_CONTROLS::OnSetFocus, this ); | |
|                 } | |
|                 else | |
|                 { | |
|                     sizer->Add( btn_visible, 0, wxALIGN_CENTER_VERTICAL, 0 ); | |
|                     sizer->AddSpacer( 5 ); | |
|                     sizer->Add( label, 0, wxALIGN_CENTER_VERTICAL, 0 ); | |
|                 } | |
| 
 | |
|                 aSetting->ctl_text = label; | |
|                 m_objectsOuterSizer->Add( sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, 5 ); | |
| 
 | |
|                 if( !aSetting->can_control_opacity ) | |
|                     m_objectsOuterSizer->AddSpacer( 2 ); | |
|             }; | |
| 
 | |
|     for( const APPEARANCE_SETTING& s_setting : s_objectSettings ) | |
|     { | |
|         if( m_isFpEditor && !s_allowedInFpEditor.count( s_setting.id ) ) | |
|             continue; | |
| 
 | |
|         if( !s_setting.spacer ) | |
|         { | |
|             m_objectSettings.emplace_back( std::make_unique<APPEARANCE_SETTING>( s_setting ) ); | |
| 
 | |
|             std::unique_ptr<APPEARANCE_SETTING>& setting = m_objectSettings.back(); | |
| 
 | |
|             // Because s_render_rows is created static, we must explicitly call wxGetTranslation | |
|             // for texts which are internationalized (tool tips and item names) | |
|             setting->tooltip = wxGetTranslation( s_setting.tooltip ); | |
|             setting->label   = wxGetTranslation( s_setting.label ); | |
| 
 | |
|             if( setting->can_control_opacity ) | |
|             { | |
|                 int width = m_windowObjects->GetTextExtent( setting->label ).x + 5; | |
|                 labelWidth = std::max( labelWidth, width ); | |
|             } | |
| 
 | |
|             m_objectSettingsMap[ToGalLayer( setting->id )] = setting.get(); | |
|         } | |
|     } | |
| 
 | |
|     for( const std::unique_ptr<APPEARANCE_SETTING>& setting : m_objectSettings ) | |
|     { | |
|         if( setting->spacer ) | |
|             m_objectsOuterSizer->AddSpacer( m_pointSize ); | |
|         else | |
|             appendObject( setting ); | |
|     } | |
| 
 | |
|     m_objectsOuterSizer->Layout(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::syncObjectSettings() | |
| { | |
|     GAL_SET visible = getVisibleObjects(); | |
| 
 | |
|     const PCB_DISPLAY_OPTIONS& opts = m_frame->GetDisplayOptions(); | |
| 
 | |
|     for( std::unique_ptr<APPEARANCE_SETTING>& setting : m_objectSettings ) | |
|     { | |
|         if( setting->spacer ) | |
|             continue; | |
| 
 | |
|         GAL_LAYER_ID layer = ToGalLayer( setting->id ); | |
| 
 | |
|         if( setting->ctl_visibility ) | |
|             setting->ctl_visibility->SetValue( visible.Contains( layer ) ); | |
| 
 | |
|         if( setting->ctl_color ) | |
|         { | |
|             COLOR4D color = m_frame->GetColorSettings()->GetColor( setting->id ); | |
|             setting->ctl_color->SetSwatchColor( color, false ); | |
|         } | |
|     } | |
| 
 | |
|     wxASSERT( m_objectSettingsMap.count( LAYER_TRACKS ) | |
|               && m_objectSettingsMap.count( LAYER_VIAS ) | |
|               && m_objectSettingsMap.count( LAYER_PADS ) | |
|               && m_objectSettingsMap.count( LAYER_ZONES ) | |
|               && m_objectSettingsMap.count( LAYER_DRAW_BITMAPS ) ); | |
| 
 | |
|     m_objectSettingsMap[LAYER_TRACKS]->ctl_opacity->SetValue( opts.m_TrackOpacity * 100 ); | |
|     m_objectSettingsMap[LAYER_VIAS]->ctl_opacity->SetValue( opts.m_ViaOpacity * 100 ); | |
|     m_objectSettingsMap[LAYER_PADS]->ctl_opacity->SetValue( opts.m_PadOpacity * 100 ); | |
|     m_objectSettingsMap[LAYER_ZONES]->ctl_opacity->SetValue( opts.m_ZoneOpacity * 100 ); | |
|     m_objectSettingsMap[LAYER_DRAW_BITMAPS]->ctl_opacity->SetValue( opts.m_ImageOpacity * 100 ); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::rebuildNets() | |
| { | |
|     BOARD*          board   = m_frame->GetBoard(); | |
|     COLOR_SETTINGS* theme   = m_frame->GetColorSettings(); | |
|     COLOR4D         bgColor = theme->GetColor( LAYER_PCB_BACKGROUND ); | |
| 
 | |
|     // If the board isn't fully loaded, we can't yet rebuild | |
|     if( !board->GetProject() ) | |
|         return; | |
| 
 | |
|     m_staticTextNets->SetLabel( _( "Nets" ) ); | |
|     m_staticTextNetClasses->SetLabel( _( "Net Classes" ) ); | |
| 
 | |
|     KIGFX::PCB_RENDER_SETTINGS* rs = static_cast<KIGFX::PCB_RENDER_SETTINGS*>( | |
|             m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings() ); | |
| 
 | |
|     std::map<wxString, KIGFX::COLOR4D>& netclassColors = rs->GetNetclassColorMap(); | |
| 
 | |
|     m_netclassOuterSizer->Clear( true ); | |
| 
 | |
|     auto appendNetclass = | |
|             [&]( int aId, const std::shared_ptr<NETCLASS>& aClass, bool isDefaultClass = false ) | |
|             { | |
|                 wxString name = aClass->GetName(); | |
| 
 | |
|                 m_netclassSettings.emplace_back( std::make_unique<APPEARANCE_SETTING>() ); | |
|                 APPEARANCE_SETTING* setting = m_netclassSettings.back().get(); | |
|                 m_netclassSettingsMap[name] = setting; | |
| 
 | |
|                 setting->ctl_panel = new wxPanel( m_netclassScrolledWindow, aId ); | |
|                 wxBoxSizer* sizer = new wxBoxSizer( wxHORIZONTAL ); | |
|                 setting->ctl_panel->SetSizer( sizer ); | |
|                 COLOR4D color = netclassColors.count( name ) ? netclassColors.at( name ) : | |
|                                                                 COLOR4D::UNSPECIFIED; | |
| 
 | |
|                 setting->ctl_color = new COLOR_SWATCH( setting->ctl_panel, color, aId, bgColor, | |
|                                                         COLOR4D::UNSPECIFIED, SWATCH_SMALL ); | |
|                 setting->ctl_color->SetToolTip( _( "Left double click or middle click for color " | |
|                                                    "change, right click for menu" ) ); | |
| 
 | |
|                 setting->ctl_color->Bind( COLOR_SWATCH_CHANGED, | |
|                                           &APPEARANCE_CONTROLS::onNetclassColorChanged, this ); | |
| 
 | |
|                 // Default netclass can't have an override color | |
|                 if( isDefaultClass ) | |
|                     setting->ctl_color->Hide(); | |
| 
 | |
|                 setting->ctl_visibility = new BITMAP_TOGGLE( setting->ctl_panel, aId, | |
|                                                              KiBitmap( BITMAPS::visibility ), | |
|                                                              KiBitmap( BITMAPS::visibility_off ), | |
|                                                              true ); | |
| 
 | |
|                 wxString tip; | |
|                 tip.Printf( _( "Show or hide ratsnest for nets in %s" ), name ); | |
|                 setting->ctl_visibility->SetToolTip( tip ); | |
| 
 | |
|                 setting->ctl_text = new wxStaticText( setting->ctl_panel, aId, name ); | |
|                 setting->ctl_text->Wrap( -1 ); | |
| 
 | |
|                 int flags = wxALIGN_CENTER_VERTICAL; | |
| 
 | |
|                 sizer->Add( setting->ctl_color,      0, flags | wxRESERVE_SPACE_EVEN_IF_HIDDEN, 5 ); | |
|                 sizer->AddSpacer( 7 ); | |
|                 sizer->Add( setting->ctl_visibility, 0, flags,                                  5 ); | |
|                 sizer->AddSpacer( 3 ); | |
|                 sizer->Add( setting->ctl_text,       1, flags,                                  5 ); | |
| 
 | |
|                 m_netclassOuterSizer->Add( setting->ctl_panel, 0, wxEXPAND, 5 ); | |
|                 m_netclassOuterSizer->AddSpacer( 2 ); | |
| 
 | |
|                 setting->ctl_visibility->Bind( TOGGLE_CHANGED, | |
|                                                &APPEARANCE_CONTROLS::onNetclassVisibilityChanged, | |
|                                                this ); | |
| 
 | |
|                 auto menuHandler = | |
|                         [&, name, isDefaultClass]( wxMouseEvent& aEvent ) | |
|                         { | |
|                             m_contextMenuNetclass = name; | |
|                             wxString escapedName = UnescapeString( name ); | |
| 
 | |
|                             wxMenu menu; | |
| 
 | |
|                             if( !isDefaultClass) | |
|                             { | |
|                                 menu.Append( new wxMenuItem( &menu, ID_SET_NET_COLOR, | |
|                                              _( "Set Netclass Color" ), wxEmptyString, | |
|                                              wxITEM_NORMAL ) ); | |
|                             } | |
| 
 | |
|                             menu.Append( new wxMenuItem( &menu, ID_HIGHLIGHT_NET, | |
|                                          wxString::Format( _( "Highlight Nets in %s" ), | |
|                                                            escapedName ), | |
|                                          wxEmptyString, wxITEM_NORMAL ) ); | |
|                             menu.Append( new wxMenuItem( &menu, ID_SELECT_NET, | |
|                                          wxString::Format( _( "Select Tracks and Vias in %s" ), | |
|                                                            escapedName ), | |
|                                          wxEmptyString, wxITEM_NORMAL ) ); | |
|                             menu.Append( new wxMenuItem( &menu, ID_DESELECT_NET, | |
|                                          wxString::Format( _( "Unselect Tracks and Vias in %s" ), | |
|                                                            escapedName ), | |
|                                          wxEmptyString, wxITEM_NORMAL ) ); | |
| 
 | |
|                             menu.AppendSeparator(); | |
| 
 | |
|                             menu.Append( new wxMenuItem( &menu, ID_SHOW_ALL_NETS, | |
|                                          _( "Show All Netclasses" ), wxEmptyString, | |
|                                          wxITEM_NORMAL ) ); | |
|                             menu.Append( new wxMenuItem( &menu, ID_HIDE_OTHER_NETS, | |
|                                          _( "Hide All Other Netclasses" ), wxEmptyString, | |
|                                          wxITEM_NORMAL ) ); | |
| 
 | |
|                             menu.Bind( wxEVT_COMMAND_MENU_SELECTED, | |
|                                        &APPEARANCE_CONTROLS::onNetclassContextMenu, this ); | |
| 
 | |
|                             PopupMenu( &menu ); | |
|                         }; | |
| 
 | |
|                 setting->ctl_panel->Bind( wxEVT_RIGHT_DOWN, menuHandler ); | |
|                 setting->ctl_visibility->Bind( wxEVT_RIGHT_DOWN, menuHandler ); | |
|                 setting->ctl_color->Bind( wxEVT_RIGHT_DOWN, menuHandler ); | |
|                 setting->ctl_text->Bind( wxEVT_RIGHT_DOWN, menuHandler ); | |
|             }; | |
| 
 | |
|     std::shared_ptr<NET_SETTINGS>& netSettings = board->GetDesignSettings().m_NetSettings; | |
| 
 | |
|     std::vector<wxString> names; | |
| 
 | |
|     for( const auto& [ name, netclass ] : netSettings->m_NetClasses ) | |
|         names.emplace_back( name ); | |
| 
 | |
|     std::sort( names.begin(), names.end() ); | |
| 
 | |
|     m_netclassIdMap.clear(); | |
| 
 | |
|     int idx = wxID_HIGHEST; | |
| 
 | |
|     m_netclassIdMap[idx] = netSettings->m_DefaultNetClass->GetName(); | |
|     appendNetclass( idx++, netSettings->m_DefaultNetClass, true ); | |
| 
 | |
|     for( const wxString& name : names ) | |
|     { | |
|         m_netclassIdMap[idx] = name; | |
|         appendNetclass( idx++, netSettings->m_NetClasses.at( name ) ); | |
|     } | |
| 
 | |
|     int      hotkey; | |
|     wxString msg; | |
| 
 | |
|     m_paneNetDisplayOptions->SetLabel( _( "Net Display Options" ) ); | |
| 
 | |
|     hotkey = PCB_ACTIONS::netColorModeCycle.GetHotKey(); | |
| 
 | |
|     if( hotkey ) | |
|         msg = wxString::Format( _( "Net colors (%s):" ), KeyNameFromKeyCode( hotkey ) ); | |
|     else | |
|         msg = _( "Net colors:" ); | |
| 
 | |
|     m_txtNetDisplayTitle->SetLabel( msg ); | |
|     m_txtNetDisplayTitle->SetToolTip( _( "Choose when to show net and netclass colors" ) ); | |
| 
 | |
|     m_rbNetColorAll->SetLabel( _( "All" ) ); | |
|     m_rbNetColorAll->SetToolTip( _( "Net and netclass colors are shown on all copper items" ) ); | |
| 
 | |
|     m_rbNetColorRatsnest->SetLabel( _( "Ratsnest" ) ); | |
|     m_rbNetColorRatsnest->SetToolTip( _( "Net and netclass colors are shown on the ratsnest only" ) ); | |
| 
 | |
|     m_rbNetColorOff->SetLabel( _( "None" ) ); | |
|     m_rbNetColorOff->SetToolTip( _( "Net and netclass colors are not shown" ) ); | |
| 
 | |
|     hotkey = PCB_ACTIONS::ratsnestModeCycle.GetHotKey(); | |
| 
 | |
|     if( hotkey ) | |
|         msg = wxString::Format( _( "Ratsnest display (%s):" ), KeyNameFromKeyCode( hotkey ) ); | |
|     else | |
|         msg = _( "Ratsnest display:" ); | |
| 
 | |
|     m_txtRatsnestVisibility->SetLabel( msg ); | |
|     m_txtRatsnestVisibility->SetToolTip( _( "Choose which ratsnest lines to display" ) ); | |
| 
 | |
|     m_rbRatsnestAllLayers->SetLabel( _( "All" ) ); | |
|     m_rbRatsnestAllLayers->SetToolTip( _( "Show ratsnest lines to items on all layers" ) ); | |
| 
 | |
|     m_rbRatsnestVisLayers->SetLabel( _( "Visible layers" ) ); | |
|     m_rbRatsnestVisLayers->SetToolTip( _( "Show ratsnest lines to items on visible layers" ) ); | |
| 
 | |
|     m_rbRatsnestNone->SetLabel( _( "None" ) ); | |
|     m_rbRatsnestNone->SetToolTip( _( "Hide all ratsnest lines" ) ); | |
| 
 | |
|     m_netclassOuterSizer->Layout(); | |
| 
 | |
|     m_netsTable->Rebuild(); | |
|     m_panelNets->GetSizer()->Layout(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::rebuildLayerPresetsWidget() | |
| { | |
|     m_presetsLabel->SetLabel( _( "Presets (Ctrl+Tab):" ) ); | |
| 
 | |
|     m_cbLayerPresets->Clear(); | |
| 
 | |
|     // Build the layers preset list. | |
|     // By default, the presetAllLayers will be selected | |
|     int idx = 0; | |
|     int default_idx = 0; | |
| 
 | |
|     for( std::pair<const wxString, LAYER_PRESET>& pair : m_layerPresets ) | |
|     { | |
|         m_cbLayerPresets->Append( wxGetTranslation( pair.first ), | |
|                                   static_cast<void*>( &pair.second ) ); | |
| 
 | |
|         if( pair.first == presetAllLayers.name ) | |
|             default_idx = idx; | |
| 
 | |
|         idx++; | |
|     } | |
| 
 | |
|     m_cbLayerPresets->Append( wxT( "---" ) ); | |
|     m_cbLayerPresets->Append( _( "Save preset..." ) ); | |
|     m_cbLayerPresets->Append( _( "Delete preset..." ) ); | |
| 
 | |
|     // At least the built-in presets should always be present | |
|     wxASSERT( !m_layerPresets.empty() ); | |
| 
 | |
|     // Default preset: all layers | |
|     m_cbLayerPresets->SetSelection( default_idx ); | |
|     m_currentPreset = &m_layerPresets[presetAllLayers.name]; | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::syncLayerPresetSelection() | |
| { | |
|     LSET    visibleLayers  = getVisibleLayers(); | |
|     GAL_SET visibleObjects = getVisibleObjects(); | |
| 
 | |
|     auto it = std::find_if( m_layerPresets.begin(), m_layerPresets.end(), | |
|                             [&]( const std::pair<const wxString, LAYER_PRESET>& aPair ) | |
|                             { | |
|                                 return ( aPair.second.layers == visibleLayers | |
|                                          && aPair.second.renderLayers == visibleObjects ); | |
|                             } ); | |
| 
 | |
|     if( it != m_layerPresets.end() ) | |
|     { | |
|         // Select the right m_cbLayersPresets item. | |
|         // but these items are translated if they are predefined items. | |
|         bool do_translate = it->second.readOnly; | |
|         wxString text = do_translate ? wxGetTranslation( it->first ) : it->first; | |
| 
 | |
|         m_cbLayerPresets->SetStringSelection( text ); | |
|     } | |
|     else | |
|         m_cbLayerPresets->SetSelection( m_cbLayerPresets->GetCount() - 3 ); // separator | |
|  | |
|     m_currentPreset = static_cast<LAYER_PRESET*>( | |
|             m_cbLayerPresets->GetClientData( m_cbLayerPresets->GetSelection() ) ); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::updateLayerPresetSelection( const wxString& aName ) | |
| { | |
|     // look at m_layerPresets to know if aName is a read only preset, or a user preset. | |
|     // Read only presets have translated names in UI, so we have to use | |
|     // a translated name in UI selection. | |
|     // But for a user preset name we should search for aName (not translated) | |
|     wxString ui_label = aName; | |
| 
 | |
|     for( std::pair<const wxString, LAYER_PRESET>& pair : m_layerPresets ) | |
|     { | |
|         if( pair.first != aName ) | |
|             continue; | |
| 
 | |
|         if( pair.second.readOnly == true ) | |
|             ui_label = wxGetTranslation( aName ); | |
| 
 | |
|         break; | |
|     } | |
| 
 | |
|     int idx = m_cbLayerPresets->FindString( ui_label ); | |
| 
 | |
|     if( idx >= 0 && m_cbLayerPresets->GetSelection() != idx ) | |
|     { | |
|         m_cbLayerPresets->SetSelection( idx ); | |
|         m_currentPreset = static_cast<LAYER_PRESET*>( m_cbLayerPresets->GetClientData( idx ) ); | |
|     } | |
|     else if( idx < 0 ) | |
|     { | |
|         m_cbLayerPresets->SetSelection( m_cbLayerPresets->GetCount() - 3 ); // separator | |
|     } | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::onLayerPresetChanged( wxCommandEvent& aEvent ) | |
| { | |
|     int count = m_cbLayerPresets->GetCount(); | |
|     int index = m_cbLayerPresets->GetSelection(); | |
| 
 | |
|     auto resetSelection = | |
|             [&]() | |
|             { | |
|                 if( m_currentPreset ) | |
|                     m_cbLayerPresets->SetStringSelection( m_currentPreset->name ); | |
|                 else | |
|                     m_cbLayerPresets->SetSelection( m_cbLayerPresets->GetCount() - 3 ); | |
|             }; | |
| 
 | |
|     if( index == count - 3 ) | |
|     { | |
|         // Separator: reject the selection | |
|         resetSelection(); | |
|         return; | |
|     } | |
|     else if( index == count - 2 ) | |
|     { | |
|         // Save current state to new preset | |
|         wxString name; | |
| 
 | |
|         if( m_lastSelectedUserPreset ) | |
|             name = m_lastSelectedUserPreset->name; | |
| 
 | |
|         wxTextEntryDialog dlg( this, _( "Layer preset name:" ), _( "Save Layer Preset" ), name ); | |
| 
 | |
|         if( dlg.ShowModal() != wxID_OK ) | |
|         { | |
|             resetSelection(); | |
|             return; | |
|         } | |
| 
 | |
|         name = dlg.GetValue(); | |
|         bool exists = m_layerPresets.count( name ); | |
| 
 | |
|         if( !exists ) | |
|         { | |
|             m_layerPresets[name] = LAYER_PRESET( name, getVisibleLayers(), | |
|                                                  getVisibleObjects(), UNSELECTED_LAYER ); | |
|         } | |
| 
 | |
|         LAYER_PRESET* preset = &m_layerPresets[name]; | |
|         m_currentPreset      = preset; | |
| 
 | |
|         if( !exists ) | |
|         { | |
|             index = m_cbLayerPresets->Insert( name, index - 1, static_cast<void*>( preset ) ); | |
|         } | |
|         else | |
|         { | |
|             preset->layers       = getVisibleLayers(); | |
|             preset->renderLayers = getVisibleObjects(); | |
| 
 | |
|             index = m_cbLayerPresets->FindString( name ); | |
|             m_presetMRU.Remove( name ); | |
|         } | |
| 
 | |
|         m_cbLayerPresets->SetSelection( index ); | |
|         m_presetMRU.Insert( name, 0 ); | |
| 
 | |
|         return; | |
|     } | |
|     else if( index == count - 1 ) | |
|     { | |
|         // Delete a preset | |
|         wxArrayString headers; | |
|         std::vector<wxArrayString> items; | |
| 
 | |
|         headers.Add( _( "Presets" ) ); | |
| 
 | |
|         for( std::pair<const wxString, LAYER_PRESET>& pair : m_layerPresets ) | |
|         { | |
|             if( !pair.second.readOnly ) | |
|             { | |
|                 wxArrayString item; | |
|                 item.Add( pair.first ); | |
|                 items.emplace_back( item ); | |
|             } | |
|         } | |
| 
 | |
|         EDA_LIST_DIALOG dlg( m_frame, _( "Delete Preset" ), headers, items ); | |
|         dlg.SetListLabel( _( "Select preset:" ) ); | |
| 
 | |
|         if( dlg.ShowModal() == wxID_OK ) | |
|         { | |
|             wxString presetName = dlg.GetTextSelection(); | |
|             int idx = m_cbLayerPresets->FindString( presetName ); | |
| 
 | |
|             if( idx != wxNOT_FOUND ) | |
|             { | |
|                 m_layerPresets.erase( presetName ); | |
| 
 | |
|                 m_cbLayerPresets->Delete( idx ); | |
|                 m_currentPreset = nullptr; | |
| 
 | |
|                 m_presetMRU.Remove( presetName ); | |
|             } | |
|         } | |
| 
 | |
|         resetSelection(); | |
|         return; | |
|     } | |
| 
 | |
|     LAYER_PRESET* preset = static_cast<LAYER_PRESET*>( m_cbLayerPresets->GetClientData( index ) ); | |
|     m_currentPreset      = preset; | |
| 
 | |
|     m_lastSelectedUserPreset = ( !preset || preset->readOnly ) ? nullptr : preset; | |
| 
 | |
|     if( preset ) | |
|         doApplyLayerPreset( *preset ); | |
| 
 | |
|     if( !m_currentPreset->name.IsEmpty() ) | |
|     { | |
|         m_presetMRU.Remove( m_currentPreset->name ); | |
|         m_presetMRU.Insert( m_currentPreset->name, 0 ); | |
|     } | |
| 
 | |
|     passOnFocus(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::doApplyLayerPreset( const LAYER_PRESET& aPreset ) | |
| { | |
|     BOARD* board = m_frame->GetBoard(); | |
| 
 | |
|     setVisibleLayers( aPreset.layers ); | |
|     setVisibleObjects( aPreset.renderLayers ); | |
| 
 | |
|     // If the preset doesn't have an explicit active layer to restore, we can at least | |
|     // force the active layer to be something in the preset's layer set | |
|     PCB_LAYER_ID activeLayer = UNSELECTED_LAYER; | |
| 
 | |
|     if( aPreset.activeLayer != UNSELECTED_LAYER ) | |
|         activeLayer = aPreset.activeLayer; | |
|     else if( aPreset.layers.any() && !aPreset.layers.test( m_frame->GetActiveLayer() ) ) | |
|         activeLayer = *aPreset.layers.Seq().begin(); | |
| 
 | |
|     LSET boardLayers = board->GetLayerSet(); | |
| 
 | |
|     if( activeLayer != UNSELECTED_LAYER && boardLayers.Contains( activeLayer ) ) | |
|         m_frame->SetActiveLayer( activeLayer ); | |
| 
 | |
|     if( !m_isFpEditor ) | |
|         m_frame->GetCanvas()->SyncLayersVisibility( board ); | |
| 
 | |
|     m_frame->GetCanvas()->Refresh(); | |
| 
 | |
|     syncColorsAndVisibility(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::rebuildViewportsWidget() | |
| { | |
|     m_viewportsLabel->SetLabel( _( "Viewports (Alt+Tab):" ) ); | |
| 
 | |
|     m_cbViewports->Clear(); | |
| 
 | |
|     for( std::pair<const wxString, VIEWPORT>& pair : m_viewports ) | |
|         m_cbViewports->Append( pair.first, static_cast<void*>( &pair.second ) ); | |
| 
 | |
|     m_cbViewports->Append( wxT( "---" ) ); | |
|     m_cbViewports->Append( _( "Save viewport..." ) ); | |
|     m_cbViewports->Append( _( "Delete viewport..." ) ); | |
| 
 | |
|     m_cbViewports->SetSelection( m_cbViewports->GetCount() - 3 ); | |
|     m_lastSelectedViewport = nullptr; | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::updateViewportSelection( const wxString& aName ) | |
| { | |
|     int idx = m_cbViewports->FindString( aName ); | |
| 
 | |
|     if( idx >= 0 && m_cbViewports->GetSelection() != idx ) | |
|     { | |
|         m_cbViewports->SetSelection( idx ); | |
|         m_lastSelectedViewport = static_cast<VIEWPORT*>( m_cbViewports->GetClientData( idx ) ); | |
|     } | |
|     else if( idx < 0 ) | |
|     { | |
|         m_cbViewports->SetSelection( m_cbViewports->GetCount() - 3 ); // separator | |
|         m_lastSelectedViewport = nullptr; | |
|     } | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::onViewportChanged( wxCommandEvent& aEvent ) | |
| { | |
|     int count = m_cbViewports->GetCount(); | |
|     int index = m_cbViewports->GetSelection(); | |
| 
 | |
|     if( index >= 0 && index < count - 3 ) | |
|     { | |
|         VIEWPORT* viewport = static_cast<VIEWPORT*>( m_cbViewports->GetClientData( index ) ); | |
| 
 | |
|         wxCHECK( viewport, /* void */ ); | |
| 
 | |
|         doApplyViewport( *viewport ); | |
| 
 | |
|         if( !viewport->name.IsEmpty() ) | |
|         { | |
|             m_viewportMRU.Remove( viewport->name ); | |
|             m_viewportMRU.Insert( viewport->name, 0 ); | |
|         } | |
|     } | |
|     else if( index == count - 2 ) | |
|     { | |
|         // Save current state to new preset | |
|         wxString name; | |
| 
 | |
|         wxTextEntryDialog dlg( this, _( "Viewport name:" ), _( "Save Viewport" ), name ); | |
| 
 | |
|         if( dlg.ShowModal() != wxID_OK ) | |
|         { | |
|             if( m_lastSelectedViewport ) | |
|                 m_cbViewports->SetStringSelection( m_lastSelectedViewport->name ); | |
|             else | |
|                 m_cbViewports->SetSelection( m_cbViewports->GetCount() - 3 ); | |
| 
 | |
|             return; | |
|         } | |
| 
 | |
|         name = dlg.GetValue(); | |
|         bool exists = m_viewports.count( name ); | |
| 
 | |
|         if( !exists ) | |
|         { | |
|             m_viewports[name] = VIEWPORT( name, m_frame->GetCanvas()->GetView()->GetViewport() ); | |
| 
 | |
|             index = m_cbViewports->Insert( name, index-1, static_cast<void*>( &m_viewports[name] ) ); | |
|         } | |
|         else | |
|         { | |
|             index = m_cbViewports->FindString( name ); | |
|             m_viewportMRU.Remove( name ); | |
|         } | |
| 
 | |
|         m_cbViewports->SetSelection( index ); | |
|         m_viewportMRU.Insert( name, 0 ); | |
| 
 | |
|         return; | |
|     } | |
|     else if( index == count - 1 ) | |
|     { | |
|         // Delete an existing viewport | |
|         wxArrayString headers; | |
|         std::vector<wxArrayString> items; | |
| 
 | |
|         headers.Add( _( "Viewports" ) ); | |
| 
 | |
|         for( std::pair<const wxString, VIEWPORT>& pair : m_viewports ) | |
|         { | |
|             wxArrayString item; | |
|             item.Add( pair.first ); | |
|             items.emplace_back( item ); | |
|         } | |
| 
 | |
|         EDA_LIST_DIALOG dlg( m_frame, _( "Delete Viewport" ), headers, items ); | |
|         dlg.SetListLabel( _( "Select viewport:" ) ); | |
| 
 | |
|         if( dlg.ShowModal() == wxID_OK ) | |
|         { | |
|             wxString viewportName = dlg.GetTextSelection(); | |
|             int idx = m_cbViewports->FindString( viewportName ); | |
| 
 | |
|             if( idx != wxNOT_FOUND ) | |
|             { | |
|                 m_viewports.erase( viewportName ); | |
|                 m_cbViewports->Delete( idx ); | |
|                 m_viewportMRU.Remove( viewportName ); | |
|             } | |
|         } | |
| 
 | |
|         if( m_lastSelectedViewport ) | |
|             m_cbViewports->SetStringSelection( m_lastSelectedViewport->name ); | |
|         else | |
|             m_cbViewports->SetSelection( m_cbViewports->GetCount() - 3 ); | |
| 
 | |
|         return; | |
|     } | |
| 
 | |
|     passOnFocus(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::doApplyViewport( const VIEWPORT& aViewport ) | |
| { | |
|     m_frame->GetCanvas()->GetView()->SetViewport( aViewport.rect ); | |
|     m_frame->GetCanvas()->Refresh(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::OnColorSwatchChanged( wxCommandEvent& aEvent ) | |
| { | |
|     COLOR_SWATCH* swatch   = static_cast<COLOR_SWATCH*>( aEvent.GetEventObject() ); | |
|     COLOR4D       newColor = swatch->GetSwatchColor(); | |
|     int           layer    = swatch->GetId(); | |
| 
 | |
|     COLOR_SETTINGS* cs = m_frame->GetColorSettings(); | |
|     cs->SetColor( layer, newColor ); | |
| 
 | |
|     m_frame->GetCanvas()->UpdateColors(); | |
| 
 | |
|     KIGFX::VIEW* view = m_frame->GetCanvas()->GetView(); | |
|     view->UpdateLayerColor( layer ); | |
|     view->UpdateLayerColor( GetNetnameLayer( layer ) ); | |
| 
 | |
|     if( IsCopperLayer( layer ) ) | |
|         view->UpdateLayerColor( ZONE_LAYER_FOR( layer ) ); | |
| 
 | |
|     // Update the bitmap of the layer box | |
|     if( m_frame->IsType( FRAME_PCB_EDITOR ) ) | |
|         static_cast<PCB_EDIT_FRAME*>( m_frame )->ReCreateLayerBox( false ); | |
| 
 | |
|     m_frame->GetCanvas()->Refresh(); | |
| 
 | |
|     if( layer == LAYER_PCB_BACKGROUND ) | |
|         m_frame->SetDrawBgColor( newColor ); | |
| 
 | |
|     passOnFocus(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::onObjectOpacitySlider( int aLayer, float aOpacity ) | |
| { | |
|     PCB_DISPLAY_OPTIONS options = m_frame->GetDisplayOptions(); | |
| 
 | |
|     switch( aLayer ) | |
|     { | |
|     case static_cast<int>( LAYER_TRACKS ):       options.m_TrackOpacity   = aOpacity; break; | |
|     case static_cast<int>( LAYER_VIAS ):         options.m_ViaOpacity     = aOpacity; break; | |
|     case static_cast<int>( LAYER_PADS ):         options.m_PadOpacity     = aOpacity; break; | |
|     case static_cast<int>( LAYER_ZONES ):        options.m_ZoneOpacity    = aOpacity; break; | |
|     case static_cast<int>( LAYER_DRAW_BITMAPS ): options.m_ImageOpacity   = aOpacity; break; | |
|     default: return; | |
|     } | |
| 
 | |
|     m_frame->SetDisplayOptions( options ); | |
|     passOnFocus(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::onNetContextMenu( wxCommandEvent& aEvent ) | |
| { | |
|     wxASSERT( m_netsGrid->GetSelectedRows().size() == 1 ); | |
| 
 | |
|     int row = m_netsGrid->GetSelectedRows()[0]; | |
|     NET_GRID_ENTRY& net = m_netsTable->GetEntry( row ); | |
| 
 | |
|     m_netsGrid->ClearSelection(); | |
| 
 | |
|     switch( aEvent.GetId() ) | |
|     { | |
|     case ID_SET_NET_COLOR: | |
|     { | |
|         wxGridCellEditor* editor = m_netsGrid->GetCellEditor( row, NET_GRID_TABLE::COL_COLOR ); | |
|         editor->BeginEdit( row, NET_GRID_TABLE::COL_COLOR, m_netsGrid ); | |
|         break; | |
|     } | |
| 
 | |
|     case ID_HIGHLIGHT_NET: | |
|     { | |
|         m_frame->GetToolManager()->RunAction( PCB_ACTIONS::highlightNet, true, | |
|                                               static_cast<intptr_t>( net.code ) ); | |
|         m_frame->GetCanvas()->Refresh(); | |
|         break; | |
|     } | |
| 
 | |
|     case ID_SELECT_NET: | |
|     { | |
|         m_frame->GetToolManager()->RunAction( PCB_ACTIONS::selectNet, true, | |
|                                               static_cast<intptr_t>( net.code ) ); | |
|         m_frame->GetCanvas()->Refresh(); | |
|         break; | |
|     } | |
| 
 | |
|     case ID_DESELECT_NET: | |
|     { | |
|         m_frame->GetToolManager()->RunAction( PCB_ACTIONS::deselectNet, true, | |
|                                               static_cast<intptr_t>( net.code ) ); | |
|         m_frame->GetCanvas()->Refresh(); | |
|         break; | |
|     } | |
| 
 | |
|     case ID_SHOW_ALL_NETS: | |
|         m_netsTable->ShowAllNets(); | |
|         break; | |
| 
 | |
|     case ID_HIDE_OTHER_NETS: | |
|         m_netsTable->HideOtherNets( net ); | |
|         break; | |
| 
 | |
|     default: | |
|         break; | |
|     } | |
| 
 | |
|     passOnFocus(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::onNetclassVisibilityChanged( wxCommandEvent& aEvent ) | |
| { | |
|     wxString className = netclassNameFromEvent( aEvent ); | |
|     bool     show      = aEvent.GetInt(); | |
|     showNetclass( className, show ); | |
|     passOnFocus(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::showNetclass( const wxString& aClassName, bool aShow ) | |
| { | |
|     for( NETINFO_ITEM* net : m_frame->GetBoard()->GetNetInfo() ) | |
|     { | |
|         if( net->GetNetClass()->GetName() == aClassName ) | |
|         { | |
|             m_frame->GetToolManager()->RunAction( aShow ? PCB_ACTIONS::showNet | |
|                                                         : PCB_ACTIONS::hideNet, | |
|                                                   true, | |
|                                                   static_cast<intptr_t>( net->GetNetCode() ) ); | |
| 
 | |
|             int row = m_netsTable->GetRowByNetcode( net->GetNetCode() ); | |
| 
 | |
|             if( row >= 0 ) | |
|                 m_netsTable->SetValueAsBool( row, NET_GRID_TABLE::COL_VISIBILITY, aShow ); | |
|         } | |
|     } | |
| 
 | |
|     m_netsGrid->ForceRefresh(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::onNetclassColorChanged( wxCommandEvent& aEvent ) | |
| { | |
|     KIGFX::PCB_RENDER_SETTINGS* rs = static_cast<KIGFX::PCB_RENDER_SETTINGS*>( | |
|             m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings() ); | |
| 
 | |
|     std::map<wxString, KIGFX::COLOR4D>& netclassColors = rs->GetNetclassColorMap(); | |
| 
 | |
|     COLOR_SWATCH* swatch    = static_cast<COLOR_SWATCH*>( aEvent.GetEventObject() ); | |
|     wxString      className = netclassNameFromEvent( aEvent ); | |
| 
 | |
|     netclassColors[className] = swatch->GetSwatchColor(); | |
| 
 | |
|     m_frame->GetCanvas()->GetView()->UpdateAllLayersColor(); | |
|     m_frame->GetCanvas()->RedrawRatsnest(); | |
|     m_frame->GetCanvas()->Refresh(); | |
| } | |
| 
 | |
| 
 | |
| wxString APPEARANCE_CONTROLS::netclassNameFromEvent( wxEvent& aEvent ) | |
| { | |
|     COLOR_SWATCH* s = static_cast<COLOR_SWATCH*>( aEvent.GetEventObject() ); | |
|     int classId = s->GetId(); | |
| 
 | |
|     wxASSERT( m_netclassIdMap.count( classId ) ); | |
|     return m_netclassIdMap.at( classId ); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::onNetColorMode( wxCommandEvent& aEvent ) | |
| { | |
|     PCB_DISPLAY_OPTIONS options = m_frame->GetDisplayOptions(); | |
| 
 | |
|     if( m_rbNetColorAll->GetValue() ) | |
|         options.m_NetColorMode = NET_COLOR_MODE::ALL; | |
|     else if( m_rbNetColorRatsnest->GetValue() ) | |
|         options.m_NetColorMode = NET_COLOR_MODE::RATSNEST; | |
|     else | |
|         options.m_NetColorMode = NET_COLOR_MODE::OFF; | |
| 
 | |
|     m_frame->SetDisplayOptions( options ); | |
|     m_frame->GetCanvas()->GetView()->UpdateAllLayersColor(); | |
|     passOnFocus(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::onRatsnestMode( wxCommandEvent& aEvent ) | |
| { | |
|     PCBNEW_SETTINGS* cfg = m_frame->GetPcbNewSettings(); | |
| 
 | |
|     if( m_rbRatsnestAllLayers->GetValue() ) | |
|     { | |
|         cfg->m_Display.m_ShowGlobalRatsnest = true; | |
|         cfg->m_Display.m_RatsnestMode = RATSNEST_MODE::ALL; | |
|     } | |
|     else if( m_rbRatsnestVisLayers->GetValue() ) | |
|     { | |
|         cfg->m_Display.m_ShowGlobalRatsnest = true; | |
|         cfg->m_Display.m_RatsnestMode = RATSNEST_MODE::VISIBLE; | |
|     } | |
|     else | |
|     { | |
|         cfg->m_Display.m_ShowGlobalRatsnest = false; | |
|     } | |
| 
 | |
|     m_frame->GetCanvas()->RedrawRatsnest(); | |
|     passOnFocus(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::onNetclassContextMenu( wxCommandEvent& aEvent ) | |
| { | |
|     KIGFX::VIEW* view = m_frame->GetCanvas()->GetView(); | |
|     KIGFX::PCB_RENDER_SETTINGS* rs = | |
|             static_cast<KIGFX::PCB_RENDER_SETTINGS*>( view->GetPainter()->GetSettings() ); | |
| 
 | |
|     BOARD*                         board            = m_frame->GetBoard(); | |
|     std::shared_ptr<NET_SETTINGS>& netSettings      = board->GetDesignSettings().m_NetSettings; | |
| 
 | |
| 
 | |
|     APPEARANCE_SETTING* setting = m_netclassSettingsMap.count( m_contextMenuNetclass ) ? | |
|                                   m_netclassSettingsMap.at( m_contextMenuNetclass ) : nullptr; | |
| 
 | |
|     auto runOnNetsOfClass = | |
|             [&]( const wxString& netClassName, std::function<void( NETINFO_ITEM* )> aFunction ) | |
|             { | |
|                 for( NETINFO_ITEM* net : board->GetNetInfo() ) | |
|                 { | |
|                     if( net->GetNetClass()->GetName() == netClassName ) | |
|                         aFunction( net ); | |
|                 } | |
|             }; | |
| 
 | |
|     switch( aEvent.GetId() ) | |
|     { | |
|         case ID_SET_NET_COLOR: | |
|         { | |
|             if( setting ) | |
|             { | |
|                 setting->ctl_color->GetNewSwatchColor(); | |
| 
 | |
|                 COLOR4D color = setting->ctl_color->GetSwatchColor(); | |
| 
 | |
|                 std::map<wxString, KIGFX::COLOR4D>& netclassColors = rs->GetNetclassColorMap(); | |
| 
 | |
|                 if( color != COLOR4D::UNSPECIFIED ) | |
|                     netclassColors[m_contextMenuNetclass] = color; | |
|                 else | |
|                     netclassColors.erase( m_contextMenuNetclass ); | |
| 
 | |
|                 view->UpdateAllLayersColor(); | |
|             } | |
| 
 | |
|             break; | |
|         } | |
| 
 | |
|         case ID_HIGHLIGHT_NET: | |
|         { | |
|             if( !m_contextMenuNetclass.IsEmpty() ) | |
|             { | |
|                 runOnNetsOfClass( m_contextMenuNetclass, | |
|                         [&]( NETINFO_ITEM* aItem ) | |
|                         { | |
|                             static bool first = true; | |
|                             int code = aItem->GetNetCode(); | |
| 
 | |
|                             if( first ) | |
|                             { | |
|                                 board->SetHighLightNet( code ); | |
|                                 rs->SetHighlight( true, code ); | |
|                                 first = false; | |
|                             } | |
|                             else | |
|                             { | |
|                                 board->SetHighLightNet( code, true ); | |
|                                 rs->SetHighlight( true, code, true ); | |
|                             } | |
|                         } ); | |
| 
 | |
|                 view->UpdateAllLayersColor(); | |
|                 board->HighLightON(); | |
|             } | |
|             break; | |
|         } | |
| 
 | |
|         case ID_SELECT_NET: | |
|         case ID_DESELECT_NET: | |
|         { | |
|             if( !m_contextMenuNetclass.IsEmpty() ) | |
|             { | |
|                 TOOL_MANAGER* toolMgr = m_frame->GetToolManager(); | |
|                 TOOL_ACTION&  action = aEvent.GetId() == ID_SELECT_NET ? PCB_ACTIONS::selectNet | |
|                                                                        : PCB_ACTIONS::deselectNet; | |
| 
 | |
|                 runOnNetsOfClass( m_contextMenuNetclass, | |
|                         [&]( NETINFO_ITEM* aItem ) | |
|                         { | |
|                             toolMgr->RunAction( action, true, | |
|                                                 static_cast<intptr_t>( aItem->GetNetCode() ) ); | |
|                         } ); | |
|             } | |
|             break; | |
|         } | |
| 
 | |
|         case ID_SHOW_ALL_NETS: | |
|         { | |
|             showNetclass( NETCLASS::Default ); | |
|             wxASSERT( m_netclassSettingsMap.count( NETCLASS::Default ) ); | |
|             m_netclassSettingsMap.at( NETCLASS::Default )->ctl_visibility->SetValue( true ); | |
| 
 | |
|             for( const auto& [ name, netclass ] : netSettings->m_NetClasses ) | |
|             { | |
|                 showNetclass( name ); | |
| 
 | |
|                 if( m_netclassSettingsMap.count( name ) ) | |
|                     m_netclassSettingsMap.at( name )->ctl_visibility->SetValue( true ); | |
|             } | |
| 
 | |
|             break; | |
|         } | |
| 
 | |
|         case ID_HIDE_OTHER_NETS: | |
|         { | |
|             bool showDefault = m_contextMenuNetclass == NETCLASS::Default; | |
|             showNetclass( NETCLASS::Default, showDefault ); | |
|             wxASSERT( m_netclassSettingsMap.count( NETCLASS::Default ) ); | |
|             m_netclassSettingsMap.at( NETCLASS::Default )->ctl_visibility->SetValue( showDefault ); | |
| 
 | |
|             for( const auto& [ name, netclass ] : netSettings->m_NetClasses ) | |
|             { | |
|                 bool show = ( name == m_contextMenuNetclass ); | |
| 
 | |
|                 showNetclass( name, show ); | |
| 
 | |
|                 if( m_netclassSettingsMap.count( name ) ) | |
|                     m_netclassSettingsMap.at( name )->ctl_visibility->SetValue( show ); | |
|             } | |
| 
 | |
|             break; | |
|         } | |
| 
 | |
|         default: | |
|             break; | |
|     } | |
| 
 | |
|     m_frame->GetCanvas()->RedrawRatsnest(); | |
|     m_frame->GetCanvas()->Refresh(); | |
| 
 | |
|     m_contextMenuNetclass.clear(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::passOnFocus() | |
| { | |
|     m_focusOwner->SetFocus(); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::onReadOnlySwatch() | |
| { | |
|     WX_INFOBAR* infobar = m_frame->GetInfoBar(); | |
| 
 | |
|     wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Open Preferences" ), | |
|                                                    wxEmptyString ); | |
| 
 | |
|     button->Bind( wxEVT_COMMAND_HYPERLINK, std::function<void( wxHyperlinkEvent& aEvent )>( | |
|             [&]( wxHyperlinkEvent& aEvent ) | |
|             { | |
|                  wxCommandEvent dummy; | |
|                  m_frame->OnPreferences( dummy ); | |
|             } ) ); | |
| 
 | |
|     infobar->RemoveAllButtons(); | |
|     infobar->AddButton( button ); | |
|     infobar->AddCloseButton(); | |
| 
 | |
|     infobar->ShowMessageFor( _( "The current color theme is read-only.  Create a new theme in " | |
|                                 "Preferences to enable color editing." ), | |
|                              10000, wxICON_INFORMATION ); | |
| } | |
| 
 | |
| 
 | |
| void APPEARANCE_CONTROLS::RefreshCollapsiblePanes() | |
| { | |
|     m_paneLayerDisplayOptions->Refresh(); | |
| }
 |