/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 1992-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 2 * 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, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for KiROUND #include #include LSET DIALOG_PLOT::s_lastLayerSet; LSET DIALOG_PLOT::s_lastAllLayersSet; LSEQ DIALOG_PLOT::s_lastAllLayersOrder; /** * A helper wxWidgets control client data object to store layer IDs. */ class PCB_LAYER_ID_CLIENT_DATA : public wxClientData { public: PCB_LAYER_ID_CLIENT_DATA() : m_id( UNDEFINED_LAYER ) { } PCB_LAYER_ID_CLIENT_DATA( PCB_LAYER_ID aId ) : m_id( aId ) { } void SetData( PCB_LAYER_ID aId ) { m_id = aId; } PCB_LAYER_ID Layer() const { return m_id; } private: PCB_LAYER_ID m_id; }; PCB_LAYER_ID_CLIENT_DATA* getLayerClientData( const wxRearrangeList* aList, int aIdx ) { return static_cast( aList->GetClientObject( aIdx ) ); } DIALOG_PLOT::DIALOG_PLOT( PCB_EDIT_FRAME* aParent ) : DIALOG_PLOT_BASE( aParent ), m_parent( aParent ), m_defaultPenSize( aParent, m_hpglPenLabel, m_hpglPenCtrl, m_hpglPenUnits ), m_trackWidthCorrection( aParent, m_widthAdjustLabel, m_widthAdjustCtrl, m_widthAdjustUnits ) { BOARD* board = m_parent->GetBoard(); SetName( DLG_WINDOW_NAME ); m_plotOpts = aParent->GetPlotSettings(); m_DRCWarningTemplate = m_DRCExclusionsWarning->GetLabel(); m_messagesPanel->SetFileName( Prj().GetProjectPath() + wxT( "report.txt" ) ); int order = 0; LSET plotOnAllLayersSelection = m_plotOpts.GetPlotOnAllLayersSelection(); wxArrayInt plotAllLayersOrder; wxArrayString plotAllLayersChoicesStrings; std::vector layersIdChoiceList; int textWidth = 0; for( LSEQ seq = board->GetEnabledLayers().SeqStackupForPlotting(); seq; ++seq ) { PCB_LAYER_ID id = *seq; wxString layerName = board->GetLayerName( id ); // wxCOL_WIDTH_AUTOSIZE doesn't work on all platforms, so we calculate width here textWidth = std::max( textWidth, KIUI::GetTextSize( layerName, m_layerCheckListBox ).x ); plotAllLayersChoicesStrings.Add( layerName ); layersIdChoiceList.push_back( id ); size_t size = plotOnAllLayersSelection.size(); if( ( static_cast( id ) <= size ) && plotOnAllLayersSelection.test( id ) ) plotAllLayersOrder.push_back( order ); else plotAllLayersOrder.push_back( ~order ); order += 1; } int checkColSize = 22; int layerColSize = textWidth + 15; #ifdef __WXMAC__ // TODO: something in wxWidgets 3.1.x pads checkbox columns with extra space. (It used to // also be that the width of the column would get set too wide (to 30), but that's patched in // our local wxWidgets fork.) checkColSize += 30; #endif m_layerCheckListBox->SetMinClientSize( wxSize( checkColSize + layerColSize, m_layerCheckListBox->GetMinClientSize().y ) ); wxStaticBox* allLayersLabel = new wxStaticBox( this, wxID_ANY, _( "Plot on All Layers" ) ); wxStaticBoxSizer* sbSizer = new wxStaticBoxSizer( allLayersLabel, wxVERTICAL ); m_plotAllLayersList = new wxRearrangeList( sbSizer->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, plotAllLayersOrder, plotAllLayersChoicesStrings, 0 ); m_plotAllLayersList->SetMinClientSize( wxSize( checkColSize + layerColSize, m_plotAllLayersList->GetMinClientSize().y ) ); // Attach the LAYER_ID to each item in m_plotAllLayersList // plotAllLayersChoicesStrings and layersIdChoiceList are in the same order, // but m_plotAllLayersList has these strings in a different order for( size_t idx = 0; idx < layersIdChoiceList.size(); idx++ ) { wxString& txt = plotAllLayersChoicesStrings[idx]; int list_idx = m_plotAllLayersList->FindString( txt, true ); PCB_LAYER_ID layer_id = layersIdChoiceList[idx]; m_plotAllLayersList->SetClientObject( list_idx, new PCB_LAYER_ID_CLIENT_DATA( layer_id ) ); } sbSizer->Add( m_plotAllLayersList, 1, wxALL | wxEXPAND, 3 ); wxBoxSizer* bButtonSizer; bButtonSizer = new wxBoxSizer( wxHORIZONTAL ); m_bpMoveUp = new STD_BITMAP_BUTTON( sbSizer->GetStaticBox(), wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW | 0 ); m_bpMoveUp->SetToolTip( _( "Move current selection up" ) ); m_bpMoveUp->SetBitmap( KiBitmapBundle( BITMAPS::small_up ) ); bButtonSizer->Add( m_bpMoveUp, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 3 ); m_bpMoveDown = new STD_BITMAP_BUTTON( sbSizer->GetStaticBox(), wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW | 0 ); m_bpMoveDown->SetToolTip( _( "Move current selection down" ) ); m_bpMoveDown->SetBitmap( KiBitmapBundle( BITMAPS::small_down ) ); bButtonSizer->Add( m_bpMoveDown, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5 ); sbSizer->Add( bButtonSizer, 0, wxALL | wxEXPAND, 3 ); bmiddleSizer->Insert( 1, sbSizer, 1, wxALL | wxEXPAND, 5 ); init_Dialog(); SetupStandardButtons( { { wxID_OK, _( "Plot" ) }, { wxID_APPLY, _( "Generate Drill Files..." ) }, { wxID_CANCEL, _( "Close" ) } } ); GetSizer()->Fit( this ); GetSizer()->SetSizeHints( this ); m_bpMoveUp->Bind( wxEVT_COMMAND_BUTTON_CLICKED, &DIALOG_PLOT::onPlotAllListMoveUp, this ); m_bpMoveDown->Bind( wxEVT_COMMAND_BUTTON_CLICKED, &DIALOG_PLOT::onPlotAllListMoveDown, this ); m_layerCheckListBox->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( DIALOG_PLOT::OnRightClickLayers ), nullptr, this ); m_plotAllLayersList->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( DIALOG_PLOT::OnRightClickAllLayers ), nullptr, this ); } DIALOG_PLOT::~DIALOG_PLOT() { s_lastAllLayersOrder.clear(); for( int ii = 0; ii < (int) m_plotAllLayersList->GetCount(); ++ii ) s_lastAllLayersOrder.push_back( getLayerClientData( m_plotAllLayersList, ii )->Layer() ); m_bpMoveDown->Unbind( wxEVT_COMMAND_BUTTON_CLICKED, &DIALOG_PLOT::onPlotAllListMoveDown, this ); m_bpMoveUp->Unbind( wxEVT_COMMAND_BUTTON_CLICKED, &DIALOG_PLOT::onPlotAllListMoveUp, this ); } void DIALOG_PLOT::init_Dialog() { BOARD* board = m_parent->GetBoard(); wxFileName fileName; PROJECT_FILE& projectFile = m_parent->Prj().GetProjectFile(); PCBNEW_SETTINGS* cfg = m_parent->GetPcbNewSettings(); if( !projectFile.m_PcbLastPath[ LAST_PATH_PLOT ].IsEmpty() ) m_plotOpts.SetOutputDirectory( projectFile.m_PcbLastPath[ LAST_PATH_PLOT ] ); m_XScaleAdjust = cfg->m_Plot.fine_scale_x; m_YScaleAdjust = cfg->m_Plot.fine_scale_y; m_zoneFillCheck->SetValue( cfg->m_Plot.check_zones_before_plotting ); m_browseButton->SetBitmap( KiBitmapBundle( BITMAPS::small_folder ) ); // m_PSWidthAdjust is stored in mm in user config m_PSWidthAdjust = KiROUND( cfg->m_Plot.ps_fine_width_adjust * pcbIUScale.IU_PER_MM ); // The reasonable width correction value must be in a range of // [-(MinTrackWidth-1), +(MinClearanceValue-1)] decimils. m_widthAdjustMinValue = -( board->GetDesignSettings().m_TrackMinWidth - 1 ); m_widthAdjustMaxValue = board->GetDesignSettings().GetSmallestClearanceValue() - 1; switch( m_plotOpts.GetFormat() ) { default: case PLOT_FORMAT::GERBER: m_plotFormatOpt->SetSelection( 0 ); break; case PLOT_FORMAT::POST: m_plotFormatOpt->SetSelection( 1 ); break; case PLOT_FORMAT::SVG: m_plotFormatOpt->SetSelection( 2 ); break; case PLOT_FORMAT::DXF: m_plotFormatOpt->SetSelection( 3 ); break; case PLOT_FORMAT::HPGL: m_plotFormatOpt->SetSelection( 4 ); break; case PLOT_FORMAT::PDF: m_plotFormatOpt->SetSelection( 5 ); break; } // Set units and value for HPGL pen size (this param is in mils). m_defaultPenSize.SetValue( m_plotOpts.GetHPGLPenDiameter() * pcbIUScale.IU_PER_MILS ); // Test for a reasonable scale value. Set to 1 if problem if( m_XScaleAdjust < PLOT_MIN_SCALE || m_YScaleAdjust < PLOT_MIN_SCALE || m_XScaleAdjust > PLOT_MAX_SCALE || m_YScaleAdjust > PLOT_MAX_SCALE ) { m_XScaleAdjust = m_YScaleAdjust = 1.0; } m_fineAdjustXCtrl->SetValue( EDA_UNIT_UTILS::UI::StringFromValue( unityScale, EDA_UNITS::UNSCALED, m_XScaleAdjust ) ); m_fineAdjustYCtrl->SetValue( EDA_UNIT_UTILS::UI::StringFromValue( unityScale, EDA_UNITS::UNSCALED, m_YScaleAdjust ) ); // Test for a reasonable PS width correction value. Set to 0 if problem. if( m_PSWidthAdjust < m_widthAdjustMinValue || m_PSWidthAdjust > m_widthAdjustMaxValue ) m_PSWidthAdjust = 0.; m_trackWidthCorrection.SetValue( m_PSWidthAdjust ); m_plotPSNegativeOpt->SetValue( m_plotOpts.GetNegative() ); m_forcePSA4OutputOpt->SetValue( m_plotOpts.GetA4Output() ); // Could devote a PlotOrder() function in place of UIOrder(). m_layerList = board->GetEnabledLayers().UIOrder(); // Populate the check list box by all enabled layers names. for( LSEQ seq = m_layerList; seq; ++seq ) { PCB_LAYER_ID layer = *seq; int checkIndex = m_layerCheckListBox->Append( board->GetLayerName( layer ) ); if( m_plotOpts.GetLayerSelection()[layer] ) m_layerCheckListBox->Check( checkIndex ); } arrangeAllLayersList( s_lastAllLayersOrder ); // Option for disabling Gerber Aperture Macro (for broken Gerber readers) m_disableApertMacros->SetValue( m_plotOpts.GetDisableGerberMacros() ); // Option for using proper Gerber extensions. Note also Protel extensions are // a broken feature. However, for now, we need to handle it. m_useGerberExtensions->SetValue( m_plotOpts.GetUseGerberProtelExtensions() ); // Option for including Gerber attributes, from Gerber X2 format, in the output // In X1 format, they will be added as comments m_useGerberX2Format->SetValue( m_plotOpts.GetUseGerberX2format() ); // Option for including Gerber netlist info (from Gerber X2 format) in the output m_useGerberNetAttributes->SetValue( m_plotOpts.GetIncludeGerberNetlistInfo() ); // Option to generate a Gerber job file m_generateGerberJobFile->SetValue( m_plotOpts.GetCreateGerberJobFile() ); // Gerber precision for coordinates m_coordFormatCtrl->SetSelection( m_plotOpts.GetGerberPrecision() == 5 ? 0 : 1 ); // SVG precision and units for coordinates m_svgPrecsision->SetValue( m_plotOpts.GetSvgPrecision() ); // Option to exclude pads from silkscreen layers m_sketchPadsOnFabLayers->SetValue( m_plotOpts.GetSketchPadsOnFabLayers() ); // Option to tent vias m_subtractMaskFromSilk->SetValue( m_plotOpts.GetSubtractMaskFromSilk() ); // Option to use aux origin m_useAuxOriginCheckBox->SetValue( m_plotOpts.GetUseAuxOrigin() ); // Option to plot page references: m_plotSheetRef->SetValue( m_plotOpts.GetPlotFrameRef() ); // Options to plot texts on footprints m_plotFootprintValues->SetValue( m_plotOpts.GetPlotValue() ); m_plotFootprintRefs->SetValue( m_plotOpts.GetPlotReference() ); m_plotFootprintText->SetValue( m_plotOpts.GetPlotFPText() ); m_plotInvisibleText->SetValue( m_plotOpts.GetPlotInvisibleText() ); // Options to plot pads and vias holes m_drillShapeOpt->SetSelection( (int)m_plotOpts.GetDrillMarksType() ); // Scale option m_scaleOpt->SetSelection( m_plotOpts.GetScaleSelection() ); // Plot mode setPlotModeChoiceSelection( m_plotOpts.GetPlotMode() ); // DXF outline mode m_DXF_plotModeOpt->SetValue( m_plotOpts.GetDXFPlotPolygonMode() ); // DXF text mode m_DXF_plotTextStrokeFontOpt->SetValue( m_plotOpts.GetTextMode() == PLOT_TEXT_MODE::DEFAULT ); // DXF units selection m_DXF_plotUnits->SetSelection( m_plotOpts.GetDXFPlotUnits() == DXF_UNITS::INCHES ? 0 : 1); // Plot mirror option m_plotMirrorOpt->SetValue( m_plotOpts.GetMirror() ); // Plot vias on mask layer (not tented) or not (tented) m_tentVias->SetValue( !m_plotOpts.GetPlotViaOnMaskLayer() ); // Black and white plotting m_SVGColorChoice->SetSelection( m_plotOpts.GetBlackAndWhite() ? 1 : 0 ); m_PDFColorChoice->SetSelection( m_plotOpts.GetBlackAndWhite() ? 1 : 0 ); m_frontFPPropertyPopups->SetValue( m_plotOpts.m_PDFFrontFPPropertyPopups ); m_backFPPropertyPopups->SetValue( m_plotOpts.m_PDFBackFPPropertyPopups ); // Initialize a few other parameters, which can also be modified // from the drill dialog reInitDialog(); // Update options values: wxCommandEvent cmd_event; SetPlotFormat( cmd_event ); OnSetScaleOpt( cmd_event ); } void DIALOG_PLOT::reInitDialog() { // after calling the Drill or DRC dialogs some parameters can be modified.... // Output directory m_outputDirectoryName->SetValue( m_plotOpts.GetOutputDirectory() ); // Origin of coordinates: m_useAuxOriginCheckBox->SetValue( m_plotOpts.GetUseAuxOrigin() ); int knownViolations = 0; int exclusions = 0; for( PCB_MARKER* marker : m_parent->GetBoard()->Markers() ) { if( marker->GetSeverity() == RPT_SEVERITY_EXCLUSION ) exclusions++; else knownViolations++; } if( knownViolations || exclusions ) { m_DRCExclusionsWarning->SetLabel( wxString::Format( m_DRCWarningTemplate, knownViolations, exclusions ) ); m_DRCExclusionsWarning->Show(); } else { m_DRCExclusionsWarning->Hide(); } BOARD* board = m_parent->GetBoard(); const BOARD_DESIGN_SETTINGS& brd_settings = board->GetDesignSettings(); if( getPlotFormat() == PLOT_FORMAT::GERBER && ( brd_settings.m_SolderMaskExpansion || brd_settings.m_SolderMaskMinWidth ) ) { m_PlotOptionsSizer->Show( m_SizerSolderMaskAlert ); } else { m_PlotOptionsSizer->Hide( m_SizerSolderMaskAlert ); } } void DIALOG_PLOT::arrangeAllLayersList( const LSEQ& aSeq ) { auto findLayer = [&]( wxRearrangeList* aList, PCB_LAYER_ID aLayer ) -> int { for( int ii = 0; ii < (int) aList->GetCount(); ++ii ) { if( getLayerClientData( aList, ii )->Layer() == aLayer ) return ii; } return -1; }; int idx = 0; for( LSEQ seq = aSeq; seq; ++seq, ++idx ) { int currentPos = findLayer( m_plotAllLayersList, *seq ); while( currentPos > idx ) { m_plotAllLayersList->Select( currentPos ); m_plotAllLayersList->MoveCurrentUp(); currentPos--; } } } #define ID_LAYER_FAB 13001 #define ID_SELECT_COPPER_LAYERS 13002 #define ID_DESELECT_COPPER_LAYERS 13003 #define ID_SELECT_ALL_LAYERS 13004 #define ID_DESELECT_ALL_LAYERS 13005 #define ID_STACKUP_ORDER 13006 // A helper function to show a popup menu, when the dialog is right clicked. void DIALOG_PLOT::OnRightClickLayers( wxMouseEvent& event ) { // Build a list of layers for usual fabrication: copper layers + tech layers without courtyard LSET fab_layer_set = ( LSET::AllCuMask() | LSET::AllTechMask() ) & ~LSET( 2, B_CrtYd, F_CrtYd ); wxMenu menu; menu.Append( new wxMenuItem( &menu, ID_LAYER_FAB, _( "Select Fab Layers" ) ) ); menu.AppendSeparator(); menu.Append( new wxMenuItem( &menu, ID_SELECT_COPPER_LAYERS, _( "Select All Copper Layers" ) ) ); menu.Append( new wxMenuItem( &menu, ID_DESELECT_COPPER_LAYERS, _( "Deselect All Copper Layers" ) ) ); menu.AppendSeparator(); menu.Append( new wxMenuItem( &menu, ID_SELECT_ALL_LAYERS, _( "Select All Layers" ) ) ); menu.Append( new wxMenuItem( &menu, ID_DESELECT_ALL_LAYERS, _( "Deselect All Layers" ) ) ); menu.Bind( wxEVT_COMMAND_MENU_SELECTED, [&]( wxCommandEvent& aCmd ) { switch( aCmd.GetId() ) { case ID_LAYER_FAB: // Select layers usually needed to build a board { for( unsigned i = 0; i < m_layerList.size(); i++ ) { LSET layermask( m_layerList[ i ] ); if( ( layermask & fab_layer_set ).any() ) m_layerCheckListBox->Check( i, true ); else m_layerCheckListBox->Check( i, false ); } break; } case ID_SELECT_COPPER_LAYERS: for( unsigned i = 0; i < m_layerList.size(); i++ ) { if( IsCopperLayer( m_layerList[i] ) ) m_layerCheckListBox->Check( i, true ); } break; case ID_DESELECT_COPPER_LAYERS: for( unsigned i = 0; i < m_layerList.size(); i++ ) { if( IsCopperLayer( m_layerList[i] ) ) m_layerCheckListBox->Check( i, false ); } break; case ID_SELECT_ALL_LAYERS: for( unsigned i = 0; i < m_layerList.size(); i++ ) m_layerCheckListBox->Check( i, true ); break; case ID_DESELECT_ALL_LAYERS: for( unsigned i = 0; i < m_layerList.size(); i++ ) m_layerCheckListBox->Check( i, false ); break; default: aCmd.Skip(); } } ); PopupMenu( &menu ); } void DIALOG_PLOT::OnRightClickAllLayers( wxMouseEvent& event ) { wxMenu menu; menu.Append( new wxMenuItem( &menu, ID_SELECT_ALL_LAYERS, _( "Select All Layers" ) ) ); menu.Append( new wxMenuItem( &menu, ID_DESELECT_ALL_LAYERS, _( "Deselect All Layers" ) ) ); menu.AppendSeparator(); menu.Append( new wxMenuItem( &menu, ID_STACKUP_ORDER, _( "Order as Board Stackup" ) ) ); menu.Bind( wxEVT_COMMAND_MENU_SELECTED, [&]( wxCommandEvent& aCmd ) { switch( aCmd.GetId() ) { case ID_SELECT_ALL_LAYERS: for( unsigned i = 0; i < m_plotAllLayersList->GetCount(); i++ ) m_plotAllLayersList->Check( i, true ); break; case ID_DESELECT_ALL_LAYERS: for( unsigned i = 0; i < m_plotAllLayersList->GetCount(); i++ ) m_plotAllLayersList->Check( i, false ); break; case ID_STACKUP_ORDER: { LSEQ stackup = m_parent->GetBoard()->GetEnabledLayers().SeqStackupForPlotting(); arrangeAllLayersList( stackup ); m_plotAllLayersList->Select( -1 ); break; } default: aCmd.Skip(); } } ); PopupMenu( &menu ); } void DIALOG_PLOT::CreateDrillFile( wxCommandEvent& event ) { // Be sure drill file use the same settings (axis option, plot directory) as plot files: applyPlotSettings(); DIALOG_GENDRILL dlg( m_parent, this ); dlg.ShowModal(); // a few plot settings can be modified: take them in account m_plotOpts = m_parent->GetPlotSettings(); reInitDialog(); } void DIALOG_PLOT::OnChangeDXFPlotMode( wxCommandEvent& event ) { // m_DXF_plotTextStrokeFontOpt is disabled if m_DXF_plotModeOpt is checked (plot in DXF // polygon mode) m_DXF_plotTextStrokeFontOpt->Enable( !m_DXF_plotModeOpt->GetValue() ); // if m_DXF_plotTextStrokeFontOpt option is disabled (plot DXF in polygon mode), force // m_DXF_plotTextStrokeFontOpt to true to use Pcbnew stroke font if( !m_DXF_plotTextStrokeFontOpt->IsEnabled() ) m_DXF_plotTextStrokeFontOpt->SetValue( true ); } void DIALOG_PLOT::OnSetScaleOpt( wxCommandEvent& event ) { /* Disable sheet reference for scale != 1:1 */ bool scale1 = ( m_scaleOpt->GetSelection() == 1 ); m_plotSheetRef->Enable( scale1 ); if( !scale1 ) m_plotSheetRef->SetValue( false ); } void DIALOG_PLOT::OnOutputDirectoryBrowseClicked( wxCommandEvent& event ) { // Build the absolute path of current output directory to preselect it in the file browser. std::function textResolver = [&]( wxString* token ) -> bool { return m_parent->GetBoard()->ResolveTextVar( token, 0 ); }; wxString path = m_outputDirectoryName->GetValue(); path = ExpandTextVars( path, &textResolver ); path = ExpandEnvVarSubstitutions( path, &Prj() ); path = Prj().AbsolutePath( path ); wxDirDialog dirDialog( this, _( "Select Output Directory" ), path ); if( dirDialog.ShowModal() == wxID_CANCEL ) return; wxFileName dirName = wxFileName::DirName( dirDialog.GetPath() ); wxFileName fn( Prj().AbsolutePath( m_parent->GetBoard()->GetFileName() ) ); wxString defaultPath = fn.GetPathWithSep(); wxString msg; wxFileName relPathTest; // Used to test if we can make the path relative relPathTest.Assign( dirDialog.GetPath() ); // Test if making the path relative is possible before asking the user if they want to do it if( relPathTest.MakeRelativeTo( defaultPath ) ) { msg.Printf( _( "Do you want to use a path relative to\n'%s'?" ), defaultPath ); wxMessageDialog dialog( this, msg, _( "Plot Output Directory" ), wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT ); if( dialog.ShowModal() == wxID_YES ) dirName.MakeRelativeTo( defaultPath ); } m_outputDirectoryName->SetValue( dirName.GetFullPath() ); } PLOT_FORMAT DIALOG_PLOT::getPlotFormat() { // plot format id's are ordered like displayed in m_plotFormatOpt static const PLOT_FORMAT plotFmt[] = { PLOT_FORMAT::GERBER, PLOT_FORMAT::POST, PLOT_FORMAT::SVG, PLOT_FORMAT::DXF, PLOT_FORMAT::HPGL, PLOT_FORMAT::PDF }; return plotFmt[m_plotFormatOpt->GetSelection()]; } void DIALOG_PLOT::SetPlotFormat( wxCommandEvent& event ) { // this option exist only in DXF format: m_DXF_plotModeOpt->Enable( getPlotFormat() == PLOT_FORMAT::DXF ); // The alert message about non 0 solder mask min width and margin is shown // only in gerber format and if min mask width or mask margin is not 0 BOARD* board = m_parent->GetBoard(); const BOARD_DESIGN_SETTINGS& brd_settings = board->GetDesignSettings(); if( getPlotFormat() == PLOT_FORMAT::GERBER && ( brd_settings.m_SolderMaskExpansion || brd_settings.m_SolderMaskMinWidth ) ) { m_PlotOptionsSizer->Show( m_SizerSolderMaskAlert ); } else { m_PlotOptionsSizer->Hide( m_SizerSolderMaskAlert ); } switch( getPlotFormat() ) { case PLOT_FORMAT::SVG: case PLOT_FORMAT::PDF: m_drillShapeOpt->Enable( true ); m_plotModeOpt->Enable( false ); setPlotModeChoiceSelection( FILLED ); m_plotMirrorOpt->Enable( true ); m_useAuxOriginCheckBox->Enable( true ); m_defaultPenSize.Enable( false ); m_scaleOpt->Enable( false ); m_scaleOpt->SetSelection( 1 ); m_fineAdjustXCtrl->Enable( false ); m_fineAdjustYCtrl->Enable( false ); m_trackWidthCorrection.Enable( false ); m_plotPSNegativeOpt->Enable( true ); m_forcePSA4OutputOpt->Enable( false ); m_forcePSA4OutputOpt->SetValue( false ); if( getPlotFormat() == PLOT_FORMAT::SVG ) { m_PlotOptionsSizer->Show( m_svgOptionsSizer ); m_PlotOptionsSizer->Hide( m_PDFOptionsSizer ); } else { m_PlotOptionsSizer->Hide( m_svgOptionsSizer ); m_PlotOptionsSizer->Show( m_PDFOptionsSizer ); } m_PlotOptionsSizer->Hide( m_GerberOptionsSizer ); m_PlotOptionsSizer->Hide( m_HPGLOptionsSizer ); m_PlotOptionsSizer->Hide( m_PSOptionsSizer ); m_PlotOptionsSizer->Hide( m_SizerDXF_options ); break; case PLOT_FORMAT::POST: m_drillShapeOpt->Enable( true ); m_plotModeOpt->Enable( true ); m_plotMirrorOpt->Enable( true ); m_useAuxOriginCheckBox->Enable( false ); m_useAuxOriginCheckBox->SetValue( false ); m_defaultPenSize.Enable( false ); m_scaleOpt->Enable( true ); m_fineAdjustXCtrl->Enable( true ); m_fineAdjustYCtrl->Enable( true ); m_trackWidthCorrection.Enable( true ); m_plotPSNegativeOpt->Enable( true ); m_forcePSA4OutputOpt->Enable( true ); m_PlotOptionsSizer->Hide( m_GerberOptionsSizer ); m_PlotOptionsSizer->Hide( m_HPGLOptionsSizer ); m_PlotOptionsSizer->Show( m_PSOptionsSizer ); m_PlotOptionsSizer->Hide( m_SizerDXF_options ); m_PlotOptionsSizer->Hide( m_svgOptionsSizer ); m_PlotOptionsSizer->Hide( m_PDFOptionsSizer ); break; case PLOT_FORMAT::GERBER: m_drillShapeOpt->Enable( false ); m_drillShapeOpt->SetSelection( 0 ); m_plotModeOpt->Enable( false ); setPlotModeChoiceSelection( FILLED ); m_plotMirrorOpt->Enable( false ); m_plotMirrorOpt->SetValue( false ); m_useAuxOriginCheckBox->Enable( true ); m_defaultPenSize.Enable( false ); m_scaleOpt->Enable( false ); m_scaleOpt->SetSelection( 1 ); m_fineAdjustXCtrl->Enable( false ); m_fineAdjustYCtrl->Enable( false ); m_trackWidthCorrection.Enable( false ); m_plotPSNegativeOpt->Enable( false ); m_plotPSNegativeOpt->SetValue( false ); m_forcePSA4OutputOpt->Enable( false ); m_forcePSA4OutputOpt->SetValue( false ); m_PlotOptionsSizer->Show( m_GerberOptionsSizer ); m_PlotOptionsSizer->Hide( m_HPGLOptionsSizer ); m_PlotOptionsSizer->Hide( m_PSOptionsSizer ); m_PlotOptionsSizer->Hide( m_SizerDXF_options ); m_PlotOptionsSizer->Hide( m_svgOptionsSizer ); m_PlotOptionsSizer->Hide( m_PDFOptionsSizer ); break; case PLOT_FORMAT::HPGL: m_drillShapeOpt->Enable( true ); m_plotModeOpt->Enable( true ); m_plotMirrorOpt->Enable( true ); m_useAuxOriginCheckBox->Enable( false ); m_useAuxOriginCheckBox->SetValue( false ); m_defaultPenSize.Enable( true ); m_scaleOpt->Enable( true ); m_fineAdjustXCtrl->Enable( false ); m_fineAdjustYCtrl->Enable( false ); m_trackWidthCorrection.Enable( false ); m_plotPSNegativeOpt->SetValue( false ); m_plotPSNegativeOpt->Enable( false ); m_forcePSA4OutputOpt->Enable( true ); m_PlotOptionsSizer->Hide( m_GerberOptionsSizer ); m_PlotOptionsSizer->Show( m_HPGLOptionsSizer ); m_PlotOptionsSizer->Hide( m_PSOptionsSizer ); m_PlotOptionsSizer->Hide( m_SizerDXF_options ); m_PlotOptionsSizer->Hide( m_svgOptionsSizer ); m_PlotOptionsSizer->Hide( m_PDFOptionsSizer ); break; case PLOT_FORMAT::DXF: m_drillShapeOpt->Enable( true ); m_plotModeOpt->Enable( false ); setPlotModeChoiceSelection( FILLED ); m_plotMirrorOpt->Enable( false ); m_plotMirrorOpt->SetValue( false ); m_useAuxOriginCheckBox->Enable( true ); m_defaultPenSize.Enable( false ); m_scaleOpt->Enable( false ); m_scaleOpt->SetSelection( 1 ); m_fineAdjustXCtrl->Enable( false ); m_fineAdjustYCtrl->Enable( false ); m_trackWidthCorrection.Enable( false ); m_plotPSNegativeOpt->Enable( false ); m_plotPSNegativeOpt->SetValue( false ); m_forcePSA4OutputOpt->Enable( false ); m_forcePSA4OutputOpt->SetValue( false ); m_PlotOptionsSizer->Hide( m_GerberOptionsSizer ); m_PlotOptionsSizer->Hide( m_HPGLOptionsSizer ); m_PlotOptionsSizer->Hide( m_PSOptionsSizer ); m_PlotOptionsSizer->Show( m_SizerDXF_options ); m_PlotOptionsSizer->Hide( m_svgOptionsSizer ); m_PlotOptionsSizer->Hide( m_PDFOptionsSizer ); OnChangeDXFPlotMode( event ); break; case PLOT_FORMAT::UNDEFINED: break; } /* Update the interlock between scale and frame reference * (scaling would mess up the frame border...) */ OnSetScaleOpt( event ); Layout(); m_MainSizer->SetSizeHints( this ); } // A helper function to "clip" aValue between aMin and aMax and write result in * aResult // return false if clipped, true if aValue is just copied into * aResult static bool setDouble( double* aResult, double aValue, double aMin, double aMax ) { if( aValue < aMin ) { *aResult = aMin; return false; } else if( aValue > aMax ) { *aResult = aMax; return false; } *aResult = aValue; return true; } static bool setInt( int* aResult, int aValue, int aMin, int aMax ) { if( aValue < aMin ) { *aResult = aMin; return false; } else if( aValue > aMax ) { *aResult = aMax; return false; } *aResult = aValue; return true; } void DIALOG_PLOT::applyPlotSettings() { REPORTER& reporter = m_messagesPanel->Reporter(); PCB_PLOT_PARAMS tempOptions; tempOptions.SetSubtractMaskFromSilk( m_subtractMaskFromSilk->GetValue() ); tempOptions.SetPlotFrameRef( m_plotSheetRef->GetValue() ); tempOptions.SetSketchPadsOnFabLayers( m_sketchPadsOnFabLayers->GetValue() ); tempOptions.SetUseAuxOrigin( m_useAuxOriginCheckBox->GetValue() ); tempOptions.SetPlotValue( m_plotFootprintValues->GetValue() ); tempOptions.SetPlotReference( m_plotFootprintRefs->GetValue() ); tempOptions.SetPlotFPText( m_plotFootprintText->GetValue() ); tempOptions.SetPlotInvisibleText( m_plotInvisibleText->GetValue() ); tempOptions.SetScaleSelection( m_scaleOpt->GetSelection() ); int sel = m_drillShapeOpt->GetSelection(); tempOptions.SetDrillMarksType( static_cast( sel ) ); tempOptions.SetMirror( m_plotMirrorOpt->GetValue() ); tempOptions.SetPlotMode( m_plotModeOpt->GetSelection() == 1 ? SKETCH : FILLED ); tempOptions.SetDXFPlotPolygonMode( m_DXF_plotModeOpt->GetValue() ); sel = m_DXF_plotUnits->GetSelection(); tempOptions.SetDXFPlotUnits( sel == 0 ? DXF_UNITS::INCHES : DXF_UNITS::MILLIMETERS ); tempOptions.SetPlotViaOnMaskLayer( !m_tentVias->GetValue() ); if( !m_DXF_plotTextStrokeFontOpt->IsEnabled() ) // Currently, only DXF supports this option tempOptions.SetTextMode( PLOT_TEXT_MODE::DEFAULT ); else tempOptions.SetTextMode( m_DXF_plotTextStrokeFontOpt->GetValue() ? PLOT_TEXT_MODE::DEFAULT : PLOT_TEXT_MODE::NATIVE ); if( getPlotFormat() == PLOT_FORMAT::SVG ) { tempOptions.SetBlackAndWhite( !!m_SVGColorChoice->GetSelection() ); } else if( getPlotFormat() == PLOT_FORMAT::PDF ) { tempOptions.SetBlackAndWhite( !!m_PDFColorChoice->GetSelection() ); tempOptions.m_PDFFrontFPPropertyPopups = m_frontFPPropertyPopups->GetValue(); tempOptions.m_PDFBackFPPropertyPopups = m_backFPPropertyPopups->GetValue(); } else { tempOptions.SetBlackAndWhite( true ); } // Update settings from text fields. Rewrite values back to the fields, // since the values may have been constrained by the setters. wxString msg; // read HPLG pen size (this param is stored in mils) // However, due to issues when converting this value from or to mm // that can slightly change the value, update this param only if it // is in use if( getPlotFormat() == PLOT_FORMAT::HPGL ) { if( !tempOptions.SetHPGLPenDiameter( m_defaultPenSize.GetValue() / pcbIUScale.IU_PER_MILS ) ) { m_defaultPenSize.SetValue( tempOptions.GetHPGLPenDiameter() * pcbIUScale.IU_PER_MILS ); msg.Printf( _( "HPGL pen size constrained." ) ); reporter.Report( msg, RPT_SEVERITY_INFO ); } } else // keep the last value (initial value if no HPGL plot made) { tempOptions.SetHPGLPenDiameter( m_plotOpts.GetHPGLPenDiameter() ); } // X scale double tmpDouble; msg = m_fineAdjustXCtrl->GetValue(); msg.ToDouble( &tmpDouble ); if( !setDouble( &m_XScaleAdjust, tmpDouble, PLOT_MIN_SCALE, PLOT_MAX_SCALE ) ) { msg.Printf( wxT( "%f" ), m_XScaleAdjust ); m_fineAdjustXCtrl->SetValue( msg ); msg.Printf( _( "X scale constrained." ) ); reporter.Report( msg, RPT_SEVERITY_INFO ); } // Y scale msg = m_fineAdjustYCtrl->GetValue(); msg.ToDouble( &tmpDouble ); if( !setDouble( &m_YScaleAdjust, tmpDouble, PLOT_MIN_SCALE, PLOT_MAX_SCALE ) ) { msg.Printf( wxT( "%f" ), m_YScaleAdjust ); m_fineAdjustYCtrl->SetValue( msg ); msg.Printf( _( "Y scale constrained." ) ); reporter.Report( msg, RPT_SEVERITY_INFO ); } auto cfg = m_parent->GetPcbNewSettings(); cfg->m_Plot.fine_scale_x = m_XScaleAdjust; cfg->m_Plot.fine_scale_y = m_YScaleAdjust; cfg->m_Plot.check_zones_before_plotting = m_zoneFillCheck->GetValue(); // PS Width correction if( !setInt( &m_PSWidthAdjust, m_trackWidthCorrection.GetValue(), m_widthAdjustMinValue, m_widthAdjustMaxValue ) ) { m_trackWidthCorrection.SetValue( m_PSWidthAdjust ); msg.Printf( _( "Width correction constrained. The width correction value must be in the" " range of [%s; %s] for the current design rules." ), m_parent->StringFromValue( m_widthAdjustMinValue, true ), m_parent->StringFromValue( m_widthAdjustMaxValue, true ) ); reporter.Report( msg, RPT_SEVERITY_WARNING ); } // Store m_PSWidthAdjust in mm in user config cfg->m_Plot.ps_fine_width_adjust = pcbIUScale.IUTomm( m_PSWidthAdjust ); tempOptions.SetFormat( getPlotFormat() ); tempOptions.SetDisableGerberMacros( m_disableApertMacros->GetValue() ); tempOptions.SetUseGerberProtelExtensions( m_useGerberExtensions->GetValue() ); tempOptions.SetUseGerberX2format( m_useGerberX2Format->GetValue() ); tempOptions.SetIncludeGerberNetlistInfo( m_useGerberNetAttributes->GetValue() ); tempOptions.SetCreateGerberJobFile( m_generateGerberJobFile->GetValue() ); tempOptions.SetGerberPrecision( m_coordFormatCtrl->GetSelection() == 0 ? 5 : 6 ); tempOptions.SetSvgPrecision( m_svgPrecsision->GetValue() ); LSET selectedLayers; for( unsigned i = 0; i < m_layerList.size(); i++ ) { if( m_layerCheckListBox->IsChecked( i ) ) selectedLayers.set( m_layerList[i] ); } // Get a list of copper layers that aren't being used by inverting enabled layers. LSET disabledCopperLayers = LSET::AllCuMask() & ~m_parent->GetBoard()->GetEnabledLayers(); LSET plotOnAllLayers; // Add selected layers from plot on all layers list in order set by user. wxArrayInt plotOnAllLayersSelections; m_plotAllLayersList->GetCheckedItems( plotOnAllLayersSelections ); size_t count = plotOnAllLayersSelections.GetCount(); for( size_t i = 0; i < count; i++ ) { int index = plotOnAllLayersSelections.Item( i ); wxClientData* tmp = m_plotAllLayersList->GetClientObject( index ); PCB_LAYER_ID_CLIENT_DATA* layerId = dynamic_cast( tmp ); wxCHECK2( layerId, continue ); plotOnAllLayers.set( layerId->Layer() ); } tempOptions.SetPlotOnAllLayersSelection( plotOnAllLayers ); // Enable all of the disabled copper layers. // If someone enables more copper layers they will be selected by default. selectedLayers = selectedLayers | disabledCopperLayers; tempOptions.SetLayerSelection( selectedLayers ); tempOptions.SetNegative( m_plotPSNegativeOpt->GetValue() ); tempOptions.SetA4Output( m_forcePSA4OutputOpt->GetValue() ); // Set output directory and replace backslashes with forward ones wxString dirStr; dirStr = m_outputDirectoryName->GetValue(); dirStr.Replace( wxT( "\\" ), wxT( "/" ) ); tempOptions.SetOutputDirectory( dirStr ); m_parent->Prj().GetProjectFile().m_PcbLastPath[ LAST_PATH_PLOT ] = dirStr; if( !m_plotOpts.IsSameAs( tempOptions ) ) { m_parent->SetPlotSettings( tempOptions ); m_parent->OnModify(); m_plotOpts = tempOptions; } } void DIALOG_PLOT::OnGerberX2Checked( wxCommandEvent& event ) { // Currently: do nothing } void DIALOG_PLOT::Plot( wxCommandEvent& event ) { BOARD* board = m_parent->GetBoard(); applyPlotSettings(); SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager(); PCBNEW_SETTINGS* cfg = mgr.GetAppSettings(); m_plotOpts.SetColorSettings( mgr.GetColorSettings( cfg->m_ColorTheme ) ); m_plotOpts.SetSketchPadLineWidth( board->GetDesignSettings().GetLineThickness( F_Fab ) ); // If no layer selected, we have nothing plotted. // Prompt user if it happens because he could think there is a bug in Pcbnew. if( !m_plotOpts.GetLayerSelection().any() ) { DisplayError( this, _( "No layer selected, Nothing to plot" ) ); return; } // Create output directory if it does not exist (also transform it in absolute form). // Bail if it fails. std::function textResolver = [&]( wxString* token ) -> bool { // Handles board->GetTitleBlock() *and* board->GetProject() return m_parent->GetBoard()->ResolveTextVar( token, 0 ); }; wxString path = m_plotOpts.GetOutputDirectory(); path = ExpandTextVars( path, &textResolver ); path = ExpandEnvVarSubstitutions( path, board->GetProject() ); wxFileName outputDir = wxFileName::DirName( path ); wxString boardFilename = m_parent->GetBoard()->GetFileName(); REPORTER& reporter = m_messagesPanel->Reporter(); if( !EnsureFileDirectoryExists( &outputDir, boardFilename, &reporter ) ) { wxString msg; msg.Printf( _( "Could not write plot files to folder '%s'." ), outputDir.GetPath() ); DisplayError( this, msg ); return; } if( m_zoneFillCheck->GetValue() ) m_parent->GetToolManager()->GetTool()->CheckAllZones( this ); m_plotOpts.SetAutoScale( false ); switch( m_plotOpts.GetScaleSelection() ) { default: m_plotOpts.SetScale( 1 ); break; case 0: m_plotOpts.SetAutoScale( true ); break; case 2: m_plotOpts.SetScale( 1.5 ); break; case 3: m_plotOpts.SetScale( 2 ); break; case 4: m_plotOpts.SetScale( 3 ); break; } /* If the scale factor edit controls are disabled or the scale value * is 0, don't adjust the base scale factor. This fixes a bug when * the default scale adjust is initialized to 0 and saved in program * settings resulting in a divide by zero fault. */ if( getPlotFormat() == PLOT_FORMAT::POST ) { if( m_XScaleAdjust != 0.0 ) m_plotOpts.SetFineScaleAdjustX( m_XScaleAdjust ); if( m_YScaleAdjust != 0.0 ) m_plotOpts.SetFineScaleAdjustY( m_YScaleAdjust ); m_plotOpts.SetWidthAdjust( m_PSWidthAdjust ); } wxString file_ext( GetDefaultPlotExtension( m_plotOpts.GetFormat() ) ); // Test for a reasonable scale value // XXX could this actually happen? isn't it constrained in the apply function? if( m_plotOpts.GetScale() < PLOT_MIN_SCALE ) DisplayInfoMessage( this, _( "Warning: Scale option set to a very small value" ) ); if( m_plotOpts.GetScale() > PLOT_MAX_SCALE ) DisplayInfoMessage( this, _( "Warning: Scale option set to a very large value" ) ); GERBER_JOBFILE_WRITER jobfile_writer( board, &reporter ); // Save the current plot options in the board m_parent->SetPlotSettings( m_plotOpts ); wxBusyCursor dummy; for( LSEQ seq = m_plotOpts.GetLayerSelection().UIOrder(); seq; ++seq ) { LSEQ plotSequence; // Base layer always gets plotted first. plotSequence.push_back( *seq ); // Add selected layers from plot on all layers list in order set by user. wxArrayInt plotOnAllLayers; if( m_plotAllLayersList->GetCheckedItems( plotOnAllLayers ) ) { size_t count = plotOnAllLayers.GetCount(); for( size_t i = 0; i < count; i++ ) { int index = plotOnAllLayers.Item( i ); PCB_LAYER_ID layer = getLayerClientData( m_plotAllLayersList, index )->Layer(); // Don't plot the same layer more than once; if( find( plotSequence.begin(), plotSequence.end(), layer ) != plotSequence.end() ) continue; plotSequence.push_back( layer ); } } PCB_LAYER_ID layer = *seq; // All copper layers that are disabled are actually selected // This is due to wonkyness in automatically selecting copper layers // for plotting when adding more than two layers to a board. // If plot options become accessible to the layers setup dialog // please move this functionality there! // This skips a copper layer if it is actually disabled on the board. if( ( LSET::AllCuMask() & ~board->GetEnabledLayers() )[layer] ) continue; // Pick the basename from the board file wxFileName fn( boardFilename ); // Use Gerber Extensions based on layer number // (See http://en.wikipedia.org/wiki/Gerber_File) if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER && m_useGerberExtensions->GetValue() ) file_ext = GetGerberProtelExtension( layer ); BuildPlotFileName( &fn, outputDir.GetPath(), board->GetLayerName( layer ), file_ext ); wxString fullname = fn.GetFullName(); jobfile_writer.AddGbrFile( layer, fullname ); LOCALE_IO toggle; //@todo allow controlling the sheet name and path that will be displayed in the title block // Leave blank for now PLOTTER* plotter = StartPlotBoard( board, &m_plotOpts, layer, fn.GetFullPath(), wxEmptyString, wxEmptyString ); // Print diags in messages box: wxString msg; if( plotter ) { PlotBoardLayers( board, plotter, plotSequence, m_plotOpts ); PlotInteractiveLayer( board, plotter, m_plotOpts ); plotter->EndPlot(); delete plotter->RenderSettings(); delete plotter; msg.Printf( _( "Plotted to '%s'." ), fn.GetFullPath() ); reporter.Report( msg, RPT_SEVERITY_ACTION ); } else { msg.Printf( _( "Failed to create file '%s'." ), fn.GetFullPath() ); reporter.Report( msg, RPT_SEVERITY_ERROR ); } wxSafeYield(); // displays report message. } if( m_plotOpts.GetFormat() == PLOT_FORMAT::GERBER && m_plotOpts.GetCreateGerberJobFile() ) { // Pick the basename from the board file wxFileName fn( boardFilename ); // Build gerber job file from basename BuildPlotFileName( &fn, outputDir.GetPath(), wxT( "job" ), FILEEXT::GerberJobFileExtension ); jobfile_writer.CreateJobFile( fn.GetFullPath() ); } reporter.ReportTail( _( "Done." ), RPT_SEVERITY_INFO ); } void DIALOG_PLOT::onRunDRC( wxCommandEvent& event ) { PCB_EDIT_FRAME* parent = dynamic_cast( GetParent() ); if( parent ) { DRC_TOOL* drcTool = parent->GetToolManager()->GetTool(); // First close an existing dialog if open // (low probability, but can happen) drcTool->DestroyDRCDialog(); // Open a new drc dialog, with the right parent frame, and in Modal Mode drcTool->ShowDRCDialog( this ); // Update DRC warnings on return to this dialog reInitDialog(); } } void DIALOG_PLOT::onBoardSetup( wxHyperlinkEvent& aEvent ) { PCB_EDIT_FRAME* parent = dynamic_cast( GetParent() ); if( parent ) { parent->ShowBoardSetupDialog( _( "Solder Mask/Paste" ) ); // Update warnings on return to this dialog reInitDialog(); } } void DIALOG_PLOT::onPlotAllListMoveUp( wxCommandEvent& aEvent ) { if( m_plotAllLayersList->CanMoveCurrentUp() ) m_plotAllLayersList->MoveCurrentUp(); } void DIALOG_PLOT::onPlotAllListMoveDown( wxCommandEvent& aEvent ) { if( m_plotAllLayersList->CanMoveCurrentDown() ) m_plotAllLayersList->MoveCurrentDown(); } void DIALOG_PLOT::onPlotFPRefs( wxCommandEvent& aEvent ) { if( aEvent.IsChecked() ) m_plotFootprintText->SetValue( true ); } void DIALOG_PLOT::onPlotFPValues( wxCommandEvent& aEvent ) { if( aEvent.IsChecked() ) m_plotFootprintText->SetValue( true ); } void DIALOG_PLOT::onPlotFPText( wxCommandEvent& aEvent ) { if( !aEvent.IsChecked() ) { m_plotFootprintRefs->SetValue( false ); m_plotFootprintValues->SetValue( false ); } }