mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 18:23:15 +02:00
Using a boolean argument just leads to a lot of trailing booleans in the function calls and is not user friendly. Instead, introduce PostAction() to send an action that runs after the coroutine (equivalent to passing false or the default argument), and leave RunAction as the immediate execution function.
3282 lines
110 KiB
C++
3282 lines
110 KiB
C++
/*
|
|
* 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-2023 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 <pad.h>
|
|
#include <pcb_track.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 <project.h>
|
|
#include <project/project_local_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/wx_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/wx_infobar.h>
|
|
#include <widgets/wx_grid.h>
|
|
#include <dialogs/eda_view_switcher.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 )
|
|
{
|
|
wxGridCellAttr* attr = nullptr;
|
|
|
|
switch( aCol )
|
|
{
|
|
case COL_COLOR: attr = m_defaultAttr; break;
|
|
case COL_VISIBILITY: attr = m_defaultAttr; break;
|
|
case COL_LABEL: attr = m_labelAttr; break;
|
|
default: wxFAIL;
|
|
}
|
|
|
|
if( attr )
|
|
attr->IncRef();
|
|
|
|
return attr;
|
|
}
|
|
|
|
|
|
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.ToCSSString();
|
|
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] );
|
|
}
|
|
|
|
|
|
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::showNetInRatsnest
|
|
: PCB_ACTIONS::hideNetInRatsnest;
|
|
|
|
m_frame->GetToolManager()->RunAction( action, aNet.code );
|
|
}
|
|
|
|
|
|
void NET_GRID_TABLE::updateNetColor( const NET_GRID_ENTRY& aNet )
|
|
{
|
|
KIGFX::RENDER_SETTINGS* rs = m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings();
|
|
KIGFX::PCB_RENDER_SETTINGS* renderSettings = static_cast<KIGFX::PCB_RENDER_SETTINGS*>( rs );
|
|
|
|
std::map<int, KIGFX::COLOR4D>& netColors = renderSettings->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( "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( "Locked Item Shadow" ), LAYER_LOCKED_ITEM_SHADOW, _HKI( "Show a shadow marker on locked items" ) ),
|
|
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 )
|
|
{
|
|
DPI_SCALING dpi( nullptr, m_frame );
|
|
|
|
int indicatorSize = ConvertDialogToPixels( wxSize( 6, 6 ) ).x / dpi.GetContentScaleFactor();
|
|
int screenHeight = wxSystemSettings::GetMetric( wxSYS_SCREEN_Y );
|
|
m_iconProvider = new ROW_ICON_PROVIDER( indicatorSize );
|
|
m_pointSize = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ).GetPointSize();
|
|
|
|
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 );
|
|
|
|
m_cbLayerPresets->SetToolTip( wxString::Format( _( "Save and restore layer visibility combinations.\n"
|
|
"Use %s+Tab to activate selector.\n"
|
|
"Successive Tabs while holding %s down will "
|
|
"cycle through presets in the popup." ),
|
|
KeyNameFromKeyCode( PRESET_SWITCH_KEY ),
|
|
KeyNameFromKeyCode( PRESET_SWITCH_KEY ) ) );
|
|
|
|
m_cbViewports->SetToolTip( wxString::Format( _( "Save and restore view location and zoom.\n"
|
|
"Use %s+Tab to activate selector.\n"
|
|
"Successive Tabs while holding %s down will "
|
|
"cycle through viewports in the popup." ),
|
|
KeyNameFromKeyCode( VIEWPORT_SWITCH_KEY ),
|
|
KeyNameFromKeyCode( VIEWPORT_SWITCH_KEY ) ) );
|
|
|
|
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 && m_pointSize >= indicatorSize )
|
|
m_pointSize = m_pointSize * 8 / 10;
|
|
|
|
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 );
|
|
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 );
|
|
} );
|
|
|
|
m_toggleGridRenderer = new GRID_BITMAP_TOGGLE_RENDERER( KiBitmap( BITMAPS::visibility ),
|
|
KiBitmap( BITMAPS::visibility_off ) );
|
|
|
|
m_netsGrid->RegisterDataType( wxT( "bool" ), m_toggleGridRenderer, new wxGridCellBoolEditor );
|
|
|
|
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 );
|
|
|
|
PCBNEW_SETTINGS* settings = m_frame->GetPcbNewSettings();
|
|
|
|
if( settings->m_AuiPanels.appearance_expand_layer_display )
|
|
m_paneLayerDisplayOptions->Expand();
|
|
|
|
if( settings->m_AuiPanels.appearance_expand_net_display )
|
|
m_paneNetDisplayOptions->Expand();
|
|
|
|
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()
|
|
{
|
|
PCBNEW_SETTINGS* settings = m_frame->GetPcbNewSettings();
|
|
|
|
settings->m_AuiPanels.appearance_expand_layer_display = m_paneLayerDisplayOptions->IsExpanded();
|
|
settings->m_AuiPanels.appearance_expand_net_display = m_paneNetDisplayOptions->IsExpanded();
|
|
|
|
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, 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 | wxBOTTOM, 3 );
|
|
|
|
m_cbFlipBoard = new wxCheckBox( layerDisplayPane, wxID_ANY, _( "Flip board view" ) );
|
|
m_cbFlipBoard->SetFont( infoFont );
|
|
layerDisplayOptionsSizer->Add( m_cbFlipBoard, 0, wxTOP | wxBOTTOM, 5 );
|
|
|
|
layerDisplayPane->SetSizer( layerDisplayOptionsSizer );
|
|
layerDisplayPane->Layout();
|
|
layerDisplayOptionsSizer->Fit( layerDisplayPane );
|
|
|
|
m_panelLayersSizer->Add( m_paneLayerDisplayOptions, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 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
|
|
{
|
|
DPI_SCALING dpi( nullptr, m_frame );
|
|
wxSize size( 220 * dpi.GetScaleFactor(), 480 * dpi.GetScaleFactor() );
|
|
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_CLEAR_NET_COLOR, _( "Clear Net Color" ), wxEmptyString,
|
|
wxITEM_NORMAL ) );
|
|
|
|
menu.AppendSeparator();
|
|
|
|
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" ) );
|
|
|
|
if( m_notebook->GetPageCount() >= 3 )
|
|
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::OnDarkModeToggle()
|
|
{
|
|
// This is essentially a list of hacks because DarkMode isn't yet implemented inside
|
|
// wxWidgets.
|
|
//
|
|
// The individual wxPanels, COLOR_SWATCHes and GRID_CELL_COLOR_RENDERERs should really be
|
|
// overriding some virtual method or responding to some wxWidgets event so that the parent
|
|
// doesn't have to know what it contains. But, that's not where we are, so... :shrug:
|
|
|
|
m_layerPanelColour = m_panelLayers->GetBackgroundColour().ChangeLightness( 110 );
|
|
|
|
m_windowLayers->SetBackgroundColour( m_layerPanelColour );
|
|
|
|
for( wxSizerItem* child : m_layersOuterSizer->GetChildren() )
|
|
{
|
|
if( child && child->GetWindow() )
|
|
child->GetWindow()->SetBackgroundColour( m_layerPanelColour );
|
|
}
|
|
|
|
// Easier than calling OnDarkModeToggle on all the GRID_CELL_COLOR_RENDERERs:
|
|
m_netsGrid->RegisterDataType( wxT( "COLOR4D" ),
|
|
new GRID_CELL_COLOR_RENDERER( m_frame, SWATCH_SMALL ),
|
|
new GRID_CELL_COLOR_SELECTOR( m_frame, m_netsGrid ) );
|
|
|
|
for( const std::pair<const wxString, APPEARANCE_SETTING*>& pair : m_netclassSettingsMap )
|
|
{
|
|
if( pair.second->ctl_color )
|
|
pair.second->ctl_color->OnDarkModeToggle();
|
|
}
|
|
|
|
OnLayerChanged(); // Update selected highlighting
|
|
}
|
|
|
|
|
|
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 = m_layerPanelColour.Red();
|
|
wxChar g = m_layerPanelColour.Green();
|
|
wxChar 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 )
|
|
{
|
|
KIGFX::VIEW* view = m_frame->GetCanvas()->GetView();
|
|
|
|
if( m_isFpEditor )
|
|
{
|
|
for( PCB_LAYER_ID layer : LSET::AllLayersMask().Seq() )
|
|
view->SetLayerVisible( layer, aLayers.Contains( layer ) );
|
|
}
|
|
else
|
|
{
|
|
m_frame->GetBoard()->SetVisibleLayers( aLayers );
|
|
|
|
// Note: KIGFX::REPAINT isn't enough for things that go from invisible to visible as
|
|
// they won't be found in the view layer's itemset for repainting.
|
|
view->UpdateAllItemsConditionally( KIGFX::ALL,
|
|
[]( KIGFX::VIEW_ITEM* aItem ) -> bool
|
|
{
|
|
// Items rendered to composite layers (such as LAYER_PAD_TH) must be redrawn
|
|
// whether they're optionally flashed or not (as the layer being hidden/shown
|
|
// might be the last layer the item is visible on).
|
|
return dynamic_cast<PCB_VIA*>( aItem ) || dynamic_cast<PAD*>( aItem );
|
|
} );
|
|
}
|
|
}
|
|
|
|
|
|
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();
|
|
|
|
// The new preset. We keep the visibility state of objects:
|
|
LAYER_PRESET preset;
|
|
preset.renderLayers = getVisibleObjects();
|
|
|
|
switch( aEvent.GetId() )
|
|
{
|
|
case ID_PRESET_NO_LAYERS:
|
|
preset.layers = presetNoLayers.layers;
|
|
ApplyLayerPreset( preset );
|
|
return;
|
|
|
|
case ID_PRESET_ALL_LAYERS:
|
|
preset.layers = presetAllLayers.layers;
|
|
ApplyLayerPreset( preset );
|
|
return;
|
|
|
|
case ID_SHOW_ALL_COPPER_LAYERS:
|
|
visible |= presetAllCopper.layers;
|
|
setVisibleLayers( visible );
|
|
break;
|
|
|
|
case ID_HIDE_ALL_BUT_ACTIVE:
|
|
preset.layers = presetNoLayers.layers | LSET( current );
|
|
ApplyLayerPreset( preset );
|
|
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:
|
|
preset.layers = presetFrontAssembly.layers;
|
|
ApplyLayerPreset( preset );
|
|
return;
|
|
|
|
case ID_PRESET_FRONT:
|
|
preset.layers = presetFront.layers;
|
|
ApplyLayerPreset( preset );
|
|
return;
|
|
|
|
case ID_PRESET_INNER_COPPER:
|
|
preset.layers = presetInnerCopper.layers;
|
|
ApplyLayerPreset( preset );
|
|
return;
|
|
|
|
case ID_PRESET_BACK:
|
|
preset.layers = presetBack.layers;
|
|
ApplyLayerPreset( preset );
|
|
return;
|
|
|
|
case ID_PRESET_BACK_ASSEMBLY:
|
|
preset.layers = presetBackAssembly.layers;
|
|
ApplyLayerPreset( preset );
|
|
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 )
|
|
{
|
|
wxWindow* 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->OnDisplayOptionsChanged();
|
|
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::buildNetClassMenu( wxMenu& aMenu, bool isDefaultClass,
|
|
const wxString& aName )
|
|
{
|
|
if( !isDefaultClass)
|
|
{
|
|
aMenu.Append( new wxMenuItem( &aMenu, ID_SET_NET_COLOR, _( "Set Netclass Color" ),
|
|
wxEmptyString, wxITEM_NORMAL ) );
|
|
}
|
|
|
|
wxString name = UnescapeString( aName );
|
|
|
|
aMenu.Append( new wxMenuItem( &aMenu, ID_HIGHLIGHT_NET,
|
|
wxString::Format( _( "Highlight Nets in %s" ), name ),
|
|
wxEmptyString, wxITEM_NORMAL ) );
|
|
aMenu.Append( new wxMenuItem( &aMenu, ID_SELECT_NET,
|
|
wxString::Format( _( "Select Tracks and Vias in %s" ), name ),
|
|
wxEmptyString, wxITEM_NORMAL ) );
|
|
aMenu.Append( new wxMenuItem( &aMenu, ID_DESELECT_NET,
|
|
wxString::Format( _( "Unselect Tracks and Vias in %s" ), name ),
|
|
wxEmptyString, wxITEM_NORMAL ) );
|
|
|
|
aMenu.AppendSeparator();
|
|
|
|
aMenu.Append( new wxMenuItem( &aMenu, ID_SHOW_ALL_NETS, _( "Show All Netclasses" ),
|
|
wxEmptyString, wxITEM_NORMAL ) );
|
|
aMenu.Append( new wxMenuItem( &aMenu, ID_HIDE_OTHER_NETS, _( "Hide All Other Netclasses" ),
|
|
wxEmptyString, wxITEM_NORMAL ) );
|
|
|
|
aMenu.Bind( wxEVT_COMMAND_MENU_SELECTED, &APPEARANCE_CONTROLS::onNetclassContextMenu, this );
|
|
|
|
}
|
|
|
|
|
|
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();
|
|
const std::set<wxString>& hiddenClasses = m_frame->Prj().GetLocalSettings().m_HiddenNetclasses;
|
|
|
|
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 ),
|
|
!hiddenClasses.count( name ) );
|
|
|
|
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 )
|
|
{
|
|
wxMenu menu;
|
|
buildNetClassMenu( menu, isDefaultClass, name );
|
|
|
|
m_contextMenuNetclass = name;
|
|
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_viewportsLabel->SetLabel( wxString::Format( _( "Presets (%s+Tab):" ),
|
|
KeyNameFromKeyCode( PRESET_SWITCH_KEY ) ) );
|
|
|
|
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 )
|
|
{
|
|
// Change board layers visibility, but do not change objects visibility
|
|
LAYER_PRESET curr_layers_choice = *preset;
|
|
curr_layers_choice.renderLayers = getVisibleObjects();
|
|
doApplyLayerPreset( curr_layers_choice );
|
|
}
|
|
|
|
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( wxString::Format( _( "Viewports (%s+Tab):" ),
|
|
KeyNameFromKeyCode( VIEWPORT_SWITCH_KEY ) ) );
|
|
|
|
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 && idx < (int)m_cbViewports->GetCount() - 3 /* separator */ )
|
|
{
|
|
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
|
|
{
|
|
m_viewports[name].rect = m_frame->GetCanvas()->GetView()->GetViewport();
|
|
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 ) );
|
|
|
|
if( layer == F_Cu )
|
|
view->UpdateLayerColor( LAYER_PAD_FR );
|
|
else if( layer == B_Cu )
|
|
view->UpdateLayerColor( LAYER_PAD_BK );
|
|
|
|
// 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_CLEAR_NET_COLOR:
|
|
m_netsGrid->SetCellValue( row, NET_GRID_TABLE::COL_COLOR, wxS( "rgba(0,0,0,0)" ) );
|
|
break;
|
|
|
|
case ID_HIGHLIGHT_NET:
|
|
m_frame->GetToolManager()->RunAction( PCB_ACTIONS::highlightNet, net.code );
|
|
m_frame->GetCanvas()->Refresh();
|
|
break;
|
|
|
|
case ID_SELECT_NET:
|
|
m_frame->GetToolManager()->RunAction( PCB_ACTIONS::selectNet, net.code );
|
|
m_frame->GetCanvas()->Refresh();
|
|
break;
|
|
|
|
case ID_DESELECT_NET:
|
|
m_frame->GetToolManager()->RunAction( PCB_ACTIONS::deselectNet, 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::showNetInRatsnest
|
|
: PCB_ACTIONS::hideNetInRatsnest,
|
|
net->GetNetCode() );
|
|
|
|
int row = m_netsTable->GetRowByNetcode( net->GetNetCode() );
|
|
|
|
if( row >= 0 )
|
|
m_netsTable->SetValueAsBool( row, NET_GRID_TABLE::COL_VISIBILITY, aShow );
|
|
}
|
|
}
|
|
|
|
PROJECT_LOCAL_SETTINGS& localSettings = m_frame->Prj().GetLocalSettings();
|
|
|
|
if( !aShow )
|
|
localSettings.m_HiddenNetclasses.insert( aClassName );
|
|
else
|
|
localSettings.m_HiddenNetclasses.erase( aClassName );
|
|
|
|
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 netclassName = netclassNameFromEvent( aEvent );
|
|
|
|
netclassColors[netclassName] = 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;
|
|
}
|
|
|
|
if( PCB_EDIT_FRAME* editframe = dynamic_cast<PCB_EDIT_FRAME*>( m_frame ) )
|
|
{
|
|
editframe->SetElementVisibility( LAYER_RATSNEST, cfg->m_Display.m_ShowGlobalRatsnest );
|
|
editframe->OnDisplayOptionsChanged();
|
|
editframe->GetCanvas()->RedrawRatsnest();
|
|
editframe->GetCanvas()->Refresh();
|
|
}
|
|
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 = nullptr;
|
|
|
|
auto it = m_netclassSettingsMap.find( m_contextMenuNetclass );
|
|
|
|
if( it != m_netclassSettingsMap.end() )
|
|
setting = it->second;
|
|
|
|
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, 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();
|
|
}
|