kicad/pcbnew/dialog_layers_setup2.cpp

585 lines
15 KiB
C++

/*
* 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 <dick@softplc.com>
* 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
*/
/* functions relatives to the design rules editor
*/
#include "fctsys.h"
#include "common.h"
#include "class_drawpanel.h"
#include "confirm.h"
#include "pcbnew.h"
#include "wxPcbStruct.h"
#include "pcbnew_id.h"
#include "dialog_layers_setup2.h"
#include "class_board_design_settings.h"
// 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_CMP | SOLDERMASK_LAYER_CMP | ADHESIVE_LAYER_CMP | SOLDERPASTE_LAYER_CMP)
#define BACK_AUX (SILKSCREEN_LAYER_CU | SOLDERMASK_LAYER_CU | ADHESIVE_LAYER_CU | SOLDERPASTE_LAYER_CU)
0, // shift the array index up by one, matches with "Custom".
// "Two layers, parts on Front only"
EDGE_LAYER | CMP_LAYER | CUIVRE_LAYER | FRONT_AUX,
// "Two layers, parts on Back only",
EDGE_LAYER | CMP_LAYER | CUIVRE_LAYER | BACK_AUX,
// "Two layers, parts on Front and Back",
EDGE_LAYER | CMP_LAYER | CUIVRE_LAYER | BACK_AUX | FRONT_AUX,
// "Four layers, parts on Front only"
EDGE_LAYER | CMP_LAYER | CUIVRE_LAYER | LAYER_2 | LAYER_3 | FRONT_AUX,
// "Four layers, parts on Front and Back"
EDGE_LAYER | CMP_LAYER | CUIVRE_LAYER | LAYER_2 | LAYER_3 | FRONT_AUX | BACK_AUX,
// "All layers on",
ALL_LAYERS,
};
/**
* Class IDs
* holds the 3 ui control ids for a single board layer.
*/
struct IDs
{
IDs( int aName, int aCheckBox, int aChoice )
{
name = aName;
checkbox = aCheckBox;
choice = aChoice;
}
short name;
short checkbox;
short choice;
};
/**
* Function getIDs
* maps \a aLayerNumber to the wx IDs for that layer which are
* the layer name control ID, checkbox control ID, and choice control ID
*/
static IDs getIDs( int aLayerNumber )
{
#define RET(x) return IDs( x##NAME, x##CHECKBOX, x##CHOICE );
switch( aLayerNumber )
{
case ADHESIVE_N_CMP: RET( ID_ADHESFRONT );
case SOLDERPASTE_N_CMP: RET( ID_SOLDPFRONT );
case SILKSCREEN_N_CMP: RET( ID_SILKSFRONT );
case SOLDERMASK_N_CMP: RET( ID_MASKFRONT );
case LAYER_N_FRONT: RET( ID_FRONT );
case LAYER_N_2: RET( ID_INNER2 );
case LAYER_N_3: RET( ID_INNER3 );
case LAYER_N_4: RET( ID_INNER4 );
case LAYER_N_5: RET( ID_INNER5 );
case LAYER_N_6: RET( ID_INNER6 );
case LAYER_N_7: RET( ID_INNER7 );
case LAYER_N_8: RET( ID_INNER8 );
case LAYER_N_9: RET( ID_INNER9 );
case LAYER_N_10: RET( ID_INNER10 );
case LAYER_N_11: RET( ID_INNER11 );
case LAYER_N_12: RET( ID_INNER12 );
case LAYER_N_13: RET( ID_INNER13 );
case LAYER_N_14: RET( ID_INNER14 );
case LAYER_N_15: RET( ID_INNER15 );
case LAYER_N_BACK: RET( ID_BACK );
case SOLDERMASK_N_CU: RET( ID_MASKBACK );
case SILKSCREEN_N_CU: RET( ID_SILKSBACK );
case SOLDERPASTE_N_CU: RET( ID_SOLDPBACK );
case ADHESIVE_N_CU: RET( ID_ADHESBACK );
case EDGE_N: RET( ID_PCBEDGES );
case ECO2_N: RET( ID_ECO2 );
case ECO1_N: RET( ID_ECO1 );
case COMMENT_N: RET( ID_COMMENTS );
case DRAW_N: RET( ID_DRAWINGS );
default:
// wxDEBUGMSG( "bad layer id" );
return IDs( 0, 0, 0 );
}
#undef RET
}
/***********************************************************************************/
DIALOG_LAYERS_SETUP::DIALOG_LAYERS_SETUP( WinEDA_PcbFrame* parent ) :
DIALOG_LAYERS_SETUP_BASE2( parent )
/***********************************************************************************/
{
m_Parent = parent;
m_Pcb = m_Parent->GetBoard();
init();
SetAutoLayout( true );
Layout();
Center();
m_sdbSizer2OK->SetFocus();
}
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_BASE2::Show( show );
}
else
{
// Save the dialog's position before hiding
s_LastPos = GetPosition();
s_LastSize = GetSize();
ret = DIALOG_LAYERS_SETUP_BASE2::Show( show );
}
return ret;
}
void DIALOG_LAYERS_SETUP::showCopperChoice( int copperCount )
{
static const int copperCounts[] = { 2,4,6,8,10,12,14,16 };
D(printf("boardsCopperCount=%d\n", copperCount );)
for( unsigned i = 0; i<sizeof(copperCounts); ++i )
{
// note this will change a one layer board to 2:
if( copperCount <= copperCounts[i] )
{
m_CopperLayersChoice->SetSelection(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; layer<NB_LAYERS; ++layer )
{
int nameId = getIDs( layer ).name;
wxControl* ctl = (wxControl*) FindWindowById( nameId );
wxASSERT( ctl );
if( ctl )
{
wxString lname = m_Pcb->GetLayerName( layer );
D(printf("layerName[%d]=%s\n", layer, CONV_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; layer<NB_LAYERS; ++layer )
{
setLayerCheckBox( layer, (1<<layer) & enabledLayers );
}
}
void DIALOG_LAYERS_SETUP::showPresets( int enabledLayers )
{
int presetsNdx = 0; // the "Custom" setting, matches nothing
for( unsigned i=1; i<DIM(presets); ++i )
{
if( enabledLayers == presets[i] )
{
presetsNdx = i;
break;
}
}
m_PresetsChoice->SetSelection( presetsNdx );
}
void DIALOG_LAYERS_SETUP::showLayerTypes()
{
for( int copperLayer = FIRST_COPPER_LAYER;
copperLayer <= LAST_COPPER_LAYER; ++copperLayer )
{
int choiceId = getIDs( copperLayer ).choice;
wxChoice* ctl = (wxChoice*) FindWindowById( choiceId );
wxASSERT( ctl );
if( ctl )
ctl->SetSelection( m_Pcb->GetLayerType( copperLayer ) );
}
}
/********************************************************************/
void DIALOG_LAYERS_SETUP::init()
/********************************************************************/
{
m_CopperLayerCount = m_Pcb->GetCopperLayerCount();
showCopperChoice( m_CopperLayerCount );
showBoardLayerNames();
m_EnabledLayers = m_Pcb->GetEnabledLayers();
showSelectedLayerCheckBoxes( m_EnabledLayers );
showPresets( m_EnabledLayers );
showLayerTypes();
// @todo overload a layout function so we can reposition the column titles,
// which should probably not go in a sizer of their own so that we do not have
// to fight to position them, Dick. Will work this out next.
// Adjust the vertical scroll rate so our list scrolls always one full line each time.
// m_LayersListPanel->SetScrollRate( 0, m_textCtrl1[0]->GetSize().y );
}
int DIALOG_LAYERS_SETUP::getUILayerMask()
{
int layerMaskResult = 0;
for( int layer=0; layer<NB_LAYERS; ++layer )
{
int checkBoxId = getIDs( layer ).checkbox;
wxCheckBox* ctl = (wxCheckBox*) FindWindowById( checkBoxId );
wxASSERT( ctl );
if( ctl && ctl->GetValue() )
{
layerMaskResult |= (1 << layer);
}
}
return layerMaskResult;
}
void DIALOG_LAYERS_SETUP::setLayerCheckBox( int layer, bool isChecked )
{
int checkBoxId = getIDs( layer ).checkbox;
wxCheckBox* ctl = (wxCheckBox*) FindWindowById( checkBoxId );
wxASSERT( ctl );
if( ctl )
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; copperCount > 0; ++layer, --copperCount )
{
setLayerCheckBox( layer, true );
}
for( ; layer < NB_COPPER_LAYERS-1; ++layer )
{
setLayerCheckBox( layer, false );
}
}
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
// the one place, the drop down m_CopperLayersChoice control.
// I tried to simply diable 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 );
}
}
void DIALOG_LAYERS_SETUP::OnCopperLayersChoice( wxCommandEvent& event )
{
m_CopperLayerCount = m_CopperLayersChoice->GetCurrentSelection() * 2 + 2;
setCopperLayerCheckBoxes( m_CopperLayerCount );
// event.Skip();
}
/*****************************************************************/
void DIALOG_LAYERS_SETUP::OnCancelButtonClick( wxCommandEvent& event )
/*****************************************************************/
{
EndModal( 0 );
}
/**************************************************************************/
void DIALOG_LAYERS_SETUP::OnOkButtonClick( wxCommandEvent& event )
/**************************************************************************/
{
if( testLayerNames() )
{
wxString name;
m_EnabledLayers = getUILayerMask();
m_Pcb->SetEnabledLayers( m_EnabledLayers );
for( int layer = FIRST_COPPER_LAYER;
layer <= LAST_COPPER_LAYER; ++layer )
{
if( (1<<layer) & m_EnabledLayers )
{
name = getLayerName( layer );
m_Pcb->SetLayerName( layer, name );
LAYER_T t = (LAYER_T) getLayerTypeIndex(layer);
m_Pcb->SetLayerType( layer, t );
}
}
m_Parent->ReCreateLayerBox( NULL );
EndModal( wxID_OK );
}
}
int DIALOG_LAYERS_SETUP::getLayerTypeIndex( int layer )
{
int choiceId = getIDs( layer ).choice;
int ret = 0;
wxChoice* ctl = (wxChoice*) FindWindowById( choiceId );
wxASSERT( ctl );
if( ctl )
ret = ctl->GetCurrentSelection(); // indices must have same sequence as LAYER_T
return ret;
}
wxString DIALOG_LAYERS_SETUP::getLayerName( int layer )
{
wxString ret;
int nameId = getIDs( layer ).name;
wxTextCtrl* ctl = (wxTextCtrl*) FindWindowById( nameId );
wxASSERT( ctl );
if( ctl )
ret = ctl->GetValue().Trim();
return ret;
}
static bool hasOneOf( const wxString& str, const wxString& chars )
{
for( unsigned i=0; i<chars.Len(); ++i )
if( str.Find( chars[i] ) != wxNOT_FOUND )
return true;
return false;
}
bool DIALOG_LAYERS_SETUP::testLayerNames()
{
std::vector<wxString> 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<<layer)) )
continue;
wxString name = getLayerName( layer );
//D(printf("name[%d]=%s\n", layer, CONV_TO_UTF8(name) );)
int nameId = getIDs( layer ).name;
ctl = (wxTextCtrl*) FindWindowById( nameId );
// check name for legality.
// 1) cannot be blank.
// 2) cannot have blanks.
// 3) cannot have " chars
// 4) cannot be 'signal'
// 5) must be unique.
static const wxString badchars( wxT("%$\" ") );
if( name == wxEmptyString )
{
DisplayError( this, _("Layer name may not be empty" ) );
ctl->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<wxString>::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 DisplayDialogLayerSetup( WinEDA_PcbFrame* parent )
{
DIALOG_LAYERS_SETUP frame( parent );
frame.ShowModal();
frame.Destroy();
}