/* * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright (C) 2009 Isaac Marino Bavaresco, isaacbavaresco@yahoo.com.br * Copyright (C) 2009 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 2009 Kicad Developers, see change_log.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 "fctsys.h" #include "class_drawpanel.h" #include "confirm.h" #include "pcbnew.h" #include "wxPcbStruct.h" #include "dialog_layers_setup_base.h" #include "class_board_design_settings.h" // some define to choose how copper layers widgets are shown // if defined, display only active copper layers // if not displays always 1=the full set (16 layers) #define HIDE_INACTIVE_LAYERS // if defined, use the layer manager copper layers order (from FRONT to BACK) // to display inner layers. // if not, use the default order (from BACK to FRONT) #define USE_LAYER_MANAGER_COPPER_LAYERS_ORDER /** * Struct CTLs * holds the 3 ui control pointers for a single board layer. */ struct CTLs { CTLs( wxControl* aName, wxCheckBox* aCheckBox, wxControl* aChoiceOrDesc, wxPanel * aPanel = NULL) { name = aName; checkbox = aCheckBox; choice = aChoiceOrDesc; panel = aPanel; } wxControl* name; wxCheckBox* checkbox; wxControl* choice; wxPanel * panel; }; class DIALOG_LAYERS_SETUP : public DIALOG_LAYERS_SETUP_BASE { private: static wxPoint s_LastPos; static wxSize s_LastSize; PCB_EDIT_FRAME* m_Parent; int m_CopperLayerCount; int m_EnabledLayers; BOARD* m_Pcb; wxStaticText* m_NameStaticText; wxStaticText* m_EnabledStaticText; wxStaticText* m_TypeStaticText; void setLayerCheckBox( int layer, bool isChecked ); void setCopperLayerCheckBoxes( int copperCount ); void showCopperChoice( int copperCount ); void showBoardLayerNames(); void showSelectedLayerCheckBoxes( int enableLayerMask ); void showLayerTypes(); void showPresets( int enabledLayerMask ); /** return the selected layer mask within the UI checkboxes */ int getUILayerMask(); wxString getLayerName( int layer ); int getLayerTypeIndex( int layer ); void OnCancelButtonClick( wxCommandEvent& event ); void OnOkButtonClick( wxCommandEvent& event ); void OnCheckBox( wxCommandEvent& event ); void DenyChangeCheckBox( wxCommandEvent& event ); void OnPresetsChoice( wxCommandEvent& event ); void OnCopperLayersChoice( wxCommandEvent& event ); bool testLayerNames(); /** * Function getCTLs * maps \a aLayerNumber to the wx IDs for that layer which are * the layer name control ID, checkbox control ID, and choice control ID */ CTLs getCTLs( int aLayerNumber ); wxControl* getName( int aLayer ) { return getCTLs( aLayer ).name; } wxCheckBox* getCheckBox( int aLayer ) { return getCTLs( aLayer ).checkbox; } wxChoice* getChoice( int aLayer ) { return (wxChoice*) getCTLs( aLayer ).choice; } void moveTitles() { wxArrayInt widths = m_LayerListFlexGridSizer->GetColWidths(); int offset = 0; wxSize txtz; txtz = m_NameStaticText->GetSize(); m_NameStaticText->Move( offset + (widths[0] - txtz.x)/2, 5 ); offset += widths[0]; txtz = m_EnabledStaticText->GetSize(); m_EnabledStaticText->Move( offset + (widths[1] - txtz.x)/2, 5 ); offset += widths[1]; txtz = m_TypeStaticText->GetSize(); m_TypeStaticText->Move( offset + (widths[2] - txtz.x)/2, 5 ); } public: DIALOG_LAYERS_SETUP( PCB_EDIT_FRAME* parent ); ~DIALOG_LAYERS_SETUP( ) { }; bool Show( bool show ); // overload stock function /** * Function Layout * overrides the standard Layout() function so that the column titles can * be positioned using information in the flexgridsizer. */ bool Layout() { bool ret = DIALOG_LAYERS_SETUP_BASE::Layout(); moveTitles(); return ret; } }; // We want our dialog to remember its previous screen position wxPoint DIALOG_LAYERS_SETUP::s_LastPos( -1, -1 ); wxSize DIALOG_LAYERS_SETUP::s_LastSize; // Layer bit masks for each defined "Preset Layer Grouping" static const int presets[] = { #define FRONT_AUX (SILKSCREEN_LAYER_FRONT | SOLDERMASK_LAYER_FRONT | ADHESIVE_LAYER_FRONT | SOLDERPASTE_LAYER_FRONT) #define BACK_AUX (SILKSCREEN_LAYER_BACK | SOLDERMASK_LAYER_BACK | ADHESIVE_LAYER_BACK | SOLDERPASTE_LAYER_BACK) 0, // shift the array index up by one, matches with "Custom". // "Two layers, parts on Front only" EDGE_LAYER | LAYER_FRONT | LAYER_BACK | FRONT_AUX, // "Two layers, parts on Back only", EDGE_LAYER | LAYER_FRONT | LAYER_BACK | BACK_AUX, // "Two layers, parts on Front and Back", EDGE_LAYER | LAYER_FRONT | LAYER_BACK | BACK_AUX | FRONT_AUX, // "Four layers, parts on Front only" EDGE_LAYER | LAYER_FRONT | LAYER_BACK | LAYER_2 | LAYER_3 | FRONT_AUX, // "Four layers, parts on Front and Back" EDGE_LAYER | LAYER_FRONT | LAYER_BACK | LAYER_2 | LAYER_3 | FRONT_AUX | BACK_AUX, // "All layers on", ALL_LAYERS, }; CTLs DIALOG_LAYERS_SETUP::getCTLs( int aLayerNumber ) { #define RETCOP(x) return CTLs( x##Name, x##CheckBox, x##Choice, x##Panel ); #define RETAUX(x) return CTLs( x##Name, x##CheckBox, x##StaticText, x##Panel ); switch( aLayerNumber ) { case ADHESIVE_N_FRONT: RETAUX( m_AdhesFront ); case SOLDERPASTE_N_FRONT: RETAUX( m_SoldPFront ); case SILKSCREEN_N_FRONT: RETAUX( m_SilkSFront ); case SOLDERMASK_N_FRONT: RETAUX( m_MaskFront ); case LAYER_N_FRONT: RETCOP( m_Front ); #ifdef USE_LAYER_MANAGER_COPPER_LAYERS_ORDER case LAYER_N_15: RETCOP( m_Inner2 ); case LAYER_N_14: RETCOP( m_Inner3 ); case LAYER_N_13: RETCOP( m_Inner4 ); case LAYER_N_12: RETCOP( m_Inner5 ); case LAYER_N_11: RETCOP( m_Inner6 ); case LAYER_N_10: RETCOP( m_Inner7 ); case LAYER_N_9: RETCOP( m_Inner8 ); case LAYER_N_8: RETCOP( m_Inner9 ); case LAYER_N_7: RETCOP( m_Inner10 ); case LAYER_N_6: RETCOP( m_Inner11 ); case LAYER_N_5: RETCOP( m_Inner12 ); case LAYER_N_4: RETCOP( m_Inner13 ); case LAYER_N_3: RETCOP( m_Inner14 ); case LAYER_N_2: RETCOP( m_Inner15 ); #else case LAYER_N_2: RETCOP( m_Inner2 ); case LAYER_N_3: RETCOP( m_Inner3 ); case LAYER_N_4: RETCOP( m_Inner4 ); case LAYER_N_5: RETCOP( m_Inner5 ); case LAYER_N_6: RETCOP( m_Inner6 ); case LAYER_N_7: RETCOP( m_Inner7 ); case LAYER_N_8: RETCOP( m_Inner8 ); case LAYER_N_9: RETCOP( m_Inner9 ); case LAYER_N_10: RETCOP( m_Inner10 ); case LAYER_N_11: RETCOP( m_Inner11 ); case LAYER_N_12: RETCOP( m_Inner12 ); case LAYER_N_13: RETCOP( m_Inner13 ); case LAYER_N_14: RETCOP( m_Inner14 ); case LAYER_N_15: RETCOP( m_Inner15 ); #endif case LAYER_N_BACK: RETCOP( m_Back ); case SOLDERMASK_N_BACK: RETAUX( m_MaskBack ); case SILKSCREEN_N_BACK: RETAUX( m_SilkSBack ); case SOLDERPASTE_N_BACK: RETAUX( m_SoldPBack ); case ADHESIVE_N_BACK: RETAUX( m_AdhesBack ); case EDGE_N: RETAUX( m_PCBEdges ); case ECO2_N: RETAUX( m_Eco2 ); case ECO1_N: RETAUX( m_Eco1 ); case COMMENT_N: RETAUX( m_Comments ); case DRAW_N: RETAUX( m_Drawings ); default: // wxDEBUGMSG( "bad layer id" ); return CTLs( 0, 0, 0 ); } #undef RETCOP #undef RETAUX } /***********************************************************************************/ DIALOG_LAYERS_SETUP::DIALOG_LAYERS_SETUP( PCB_EDIT_FRAME* parent ) : DIALOG_LAYERS_SETUP_BASE( parent ) /***********************************************************************************/ { m_Parent = parent; m_Pcb = m_Parent->GetBoard(); m_CopperLayerCount = m_Pcb->GetCopperLayerCount(); showCopperChoice( m_CopperLayerCount ); setCopperLayerCheckBoxes( m_CopperLayerCount ); showBoardLayerNames(); m_EnabledLayers = m_Pcb->GetEnabledLayers(); showSelectedLayerCheckBoxes( m_EnabledLayers ); showPresets( m_EnabledLayers ); showLayerTypes(); SetAutoLayout( true ); // these 3 controls are handled outside wxformbuilder so that we can add // them without a sizer. Then we position them manually based on the column // widths from m_LayerListFlexGridSizer->GetColWidths() m_NameStaticText = new wxStaticText( m_TitlePanel, wxID_ANY, _("Name"), wxDefaultPosition, wxDefaultSize, 0 ); m_EnabledStaticText = new wxStaticText( m_TitlePanel, wxID_ANY, _("Enabled"), wxDefaultPosition, wxDefaultSize, 0 ); m_TypeStaticText = new wxStaticText( m_TitlePanel, wxID_ANY, _("Type"), wxDefaultPosition, wxDefaultSize, 0 ); // set the height of the title panel to be the size of any wxStaticText object // plus 10 so we can have a border of 5 on both top and bottom. m_TitlePanel->SetMinSize( wxSize( -1, m_AdhesFrontName->GetSize().y+10 ) ); Layout(); Center(); m_sdbSizer2OK->SetFocus(); m_sdbSizer2OK->SetDefault(); } bool DIALOG_LAYERS_SETUP::Show( bool show ) { bool ret; if( show ) { if( s_LastPos.x != -1 ) { SetSize( s_LastPos.x, s_LastPos.y, s_LastSize.x, s_LastSize.y, 0 ); } ret = DIALOG_LAYERS_SETUP_BASE::Show( show ); } else { // Save the dialog's position before hiding s_LastPos = GetPosition(); s_LastSize = GetSize(); ret = DIALOG_LAYERS_SETUP_BASE::Show( show ); } return ret; } void DIALOG_LAYERS_SETUP::showCopperChoice( int copperCount ) { static const int copperCounts[] = { 2,4,6,8,10,12,14,16 }; for( unsigned i = 0; iSetSelection(i); break; } } } void DIALOG_LAYERS_SETUP::showBoardLayerNames() { // Establish all the board's layer names into the dialog presentation, by // obtaining them from BOARD::GetLayerName() which calls // BOARD::GetDefaultLayerName() for non-coppers. for( int layer=0; layerGetLayerName( layer ); //D(printf("layerName[%d]=%s\n", layer, TO_UTF8( lname ) );) if( ctl->IsKindOf( CLASSINFO(wxTextCtrl) ) ) ((wxTextCtrl*)ctl)->SetValue( lname ); // wxTextCtrl else ctl->SetLabel( lname ); // wxStaticText } } } void DIALOG_LAYERS_SETUP::showSelectedLayerCheckBoxes( int enabledLayers ) { for( int layer=0; layerSetSelection( presetsNdx ); } void DIALOG_LAYERS_SETUP::showLayerTypes() { for( int copperLayer = FIRST_COPPER_LAYER; copperLayer <= LAST_COPPER_LAYER; ++copperLayer ) { wxChoice* ctl = getChoice( copperLayer ); ctl->SetSelection( m_Pcb->GetLayerType( copperLayer ) ); } } int DIALOG_LAYERS_SETUP::getUILayerMask() { int layerMaskResult = 0; for( int layer=0; layerGetValue() ) { layerMaskResult |= (1 << layer); } } return layerMaskResult; } void DIALOG_LAYERS_SETUP::setLayerCheckBox( int aLayer, bool isChecked ) { wxCheckBox* ctl = getCheckBox( aLayer ); ctl->SetValue( isChecked ); } void DIALOG_LAYERS_SETUP::setCopperLayerCheckBoxes( int copperCount ) { if( copperCount > 0 ) { setLayerCheckBox( LAYER_N_BACK, true ); --copperCount; } if( copperCount > 0 ) { setLayerCheckBox( LAYER_N_FRONT, true ); --copperCount; } else { setLayerCheckBox( LAYER_N_FRONT, false ); } int layer; for( layer=LAYER_N_2; layer < NB_COPPER_LAYERS-1; ++layer, --copperCount ) { bool state = copperCount > 0; #ifdef HIDE_INACTIVE_LAYERS // This code hides non-active copper layers, or redisplays hidden // layers which are now needed. CTLs ctl = getCTLs( layer ); ctl.name->Show( state ); ctl.checkbox->Show( state ); ctl.choice->Show( state ); if( ctl.panel ) ctl.panel->Show( state ); #endif setLayerCheckBox( layer, state ); } #ifdef HIDE_INACTIVE_LAYERS // Send an size event to force sizers to be updated, // because the number of copper layers can have changed. wxSizeEvent evt_size(m_LayersListPanel->GetSize() ); m_LayersListPanel->GetEventHandler()->ProcessEvent( evt_size ); #endif } void DIALOG_LAYERS_SETUP::OnCheckBox( wxCommandEvent& event ) { m_EnabledLayers = getUILayerMask(); showPresets( m_EnabledLayers ); } void DIALOG_LAYERS_SETUP::DenyChangeCheckBox( wxCommandEvent& event ) { // user may not change copper layer checkboxes from anything other than // either presets choice or the copper layer choice controls. // I tried to simply disable the copper CheckBoxes but they look like crap, // so leave them enabled and reverse the user's attempt to toggle them. setCopperLayerCheckBoxes( m_CopperLayerCount ); } void DIALOG_LAYERS_SETUP::OnPresetsChoice( wxCommandEvent& event ) { unsigned presetNdx = m_PresetsChoice->GetCurrentSelection(); if( presetNdx == 0 ) // the Custom setting controls nothing. return; if( presetNdx < DIM(presets) ) { m_EnabledLayers = presets[ presetNdx ]; int coppersMask = m_EnabledLayers & ALL_CU_LAYERS; int copperCount = 0; while( coppersMask ) { if( coppersMask & 1 ) ++copperCount; coppersMask >>= 1; } m_CopperLayerCount = copperCount; showCopperChoice( m_CopperLayerCount ); showSelectedLayerCheckBoxes( m_EnabledLayers ); setCopperLayerCheckBoxes( m_CopperLayerCount ); } } void DIALOG_LAYERS_SETUP::OnCopperLayersChoice( wxCommandEvent& event ) { m_CopperLayerCount = m_CopperLayersChoice->GetCurrentSelection() * 2 + 2; setCopperLayerCheckBoxes( m_CopperLayerCount ); m_EnabledLayers = getUILayerMask(); showPresets( m_EnabledLayers ); } /*****************************************************************/ void DIALOG_LAYERS_SETUP::OnCancelButtonClick( wxCommandEvent& event ) /*****************************************************************/ { EndModal( wxID_CANCEL ); } /**************************************************************************/ void DIALOG_LAYERS_SETUP::OnOkButtonClick( wxCommandEvent& event ) /**************************************************************************/ { if( testLayerNames() ) { wxString name; m_EnabledLayers = getUILayerMask(); m_Pcb->SetEnabledLayers( m_EnabledLayers ); /* Ensure enabled layers are also visible * This is mainly to avoid mistakes if some enabled * layers are not visible when exiting this dialog */ m_Pcb->SetVisibleLayers( m_EnabledLayers ); for( int layer = FIRST_COPPER_LAYER; layer <= LAST_COPPER_LAYER; ++layer ) { if( (1<SetLayerName( layer, name ); LAYER_T t = (LAYER_T) getLayerTypeIndex(layer); m_Pcb->SetLayerType( layer, t ); } } m_Parent->OnModify(); m_Parent->ReCreateLayerBox( NULL ); m_Parent->ReFillLayerWidget(); EndModal( wxID_OK ); } } int DIALOG_LAYERS_SETUP::getLayerTypeIndex( int aLayer ) { wxChoice* ctl = getChoice( aLayer ); int ret = ctl->GetCurrentSelection(); // indices must have same sequence as LAYER_T return ret; } wxString DIALOG_LAYERS_SETUP::getLayerName( int aLayer ) { wxString ret; wxASSERT( aLayer >= FIRST_COPPER_LAYER && aLayer <= LAST_COPPER_LAYER ); wxTextCtrl* ctl = (wxTextCtrl*) getName( aLayer ); ret = ctl->GetValue().Trim(); return ret; } static bool hasOneOf( const wxString& str, const wxString& chars ) { for( unsigned i=0; i names; wxTextCtrl* ctl; for( int layer=0; layer<=LAST_COPPER_LAYER; ++layer ) { // we _can_ rely on m_EnabledLayers being current here: if( !(m_EnabledLayers & (1<SetFocus(); // on the bad name return false; } if( hasOneOf( name, badchars ) ) { DisplayError( this, _("Layer name has an illegal character, one of: '") + badchars + wxT("'") ); ctl->SetFocus(); // on the bad name return false; } if( name == wxT("signal") ) { DisplayError( this, _("'signal' is a reserved layer name") ); ctl->SetFocus(); // on the bad name return false; } for( std::vector::iterator it = names.begin(); it != names.end(); ++it ) { if( name == *it ) { DisplayError( this, _("Layer name is a duplicate of another") ); ctl->SetFocus(); // on the bad name return false; } } names.push_back( name ); } return true; } void PCB_EDIT_FRAME::InstallDialogLayerSetup() { DIALOG_LAYERS_SETUP dlg( this ); if( dlg.ShowModal() == wxID_CANCEL ) return; wxLogDebug( wxT( "Current layer selected %d." ), getActiveLayer() ); // If the current active layer was removed, find the next avaiable layer to set as the // active layer. if( ( ( 1 << getActiveLayer() ) & GetBoard()->GetEnabledLayers() ) == 0 ) { for( int i = 0; i < LAYER_COUNT; i++ ) { int tmp = i; if( i >= LAYER_COUNT ) tmp = i - LAYER_COUNT; if( ( 1 << tmp ) & GetBoard()->GetEnabledLayers() ) { wxLogDebug( wxT( "Setting current layer to %d." ), getActiveLayer() ); setActiveLayer( tmp, true ); break; } } } else { setActiveLayer( getActiveLayer(), true ); } }