kicad/pcbnew/dialog_design_rules.cpp

615 lines
22 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: dialog_design_rules.cpp
// Author: jean-pierre Charras
/////////////////////////////////////////////////////////////////////////////
/* 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 "id.h"
#include "dialog_design_rules.h"
#include "wx/generic/gridctrl.h"
// Fields Positions on layer grid
#define LAYERS_GRID_ROUTABLE_POSITION 0
#define LAYERS_GRID_STATUS_POSITION 1
#define LAYERS_GRID_NAME_POSITION 2
// Fields Positions on rules grid
#define RULE_GRID_TRACKSIZE_POSITION 0
#define RULE_GRID_VIASIZE_POSITION 1
#define RULE_GRID_CLEARANCE_POSITION 2
#define RULE_GRID_MINTRACKSIZE_POSITION 3
#define RULE_GRID_MINVIASIZE_POSITION 4
/***********************************************************************************/
DIALOG_DESIGN_RULES::DIALOG_DESIGN_RULES( WinEDA_PcbFrame* parent ) :
DIALOG_DESIGN_RULES_BASE( parent )
/***********************************************************************************/
{
m_Parent = parent;
Init();
SetAutoLayout( true );
GetSizer()->Fit( this );
GetSizer()->SetSizeHints( this );
}
/********************************************************************/
void DIALOG_DESIGN_RULES::Init()
/********************************************************************/
{
SetFocus();
SetReturnCode( 0 );
// Initialize the layers grid:
m_ActivesLayersCount = g_DesignSettings.m_CopperLayerCount;
m_Pcb = m_Parent->GetBoard();
m_LayersCountSelection->SetSelection( m_ActivesLayersCount / 2 );
// Initialize the Routable column
SetRoutableLayerStatus();
// Initialize the Status column (layers attribute)
LAYER_T typelist[4] = { LT_SIGNAL, LT_POWER, LT_MIXED, LT_JUMPER };
for( int ii = 0; ii < 4; ii++ )
{
m_LayersType[ii] = typelist[ii];
m_LayersTypeName[ii] = CONV_FROM_UTF8( LAYER::ShowType( typelist[ii] ) );
}
for( int ii = 0; ii < m_gridLayersProperties->GetNumberRows(); ii++ )
{
m_gridLayersProperties->SetCellEditor( ii, LAYERS_GRID_STATUS_POSITION,
new wxGridCellChoiceEditor( WXSIZEOF(
m_LayersTypeName ),
m_LayersTypeName ) );
int select = LT_SIGNAL;
for( int jj = 0; jj < 4; jj++ )
{
int layer = LAYER_CMP_N - ii;
if( m_Pcb->GetLayerType( layer ) == m_LayersType[jj] )
{
select = m_LayersType[jj];
break;
}
}
m_gridLayersProperties->SetCellValue( ii, LAYERS_GRID_STATUS_POSITION,
m_LayersTypeName[select] );
m_gridLayersProperties->SetCellOverflow( ii, LAYERS_GRID_STATUS_POSITION, false );
}
// Initialize the Name column
for( int ii = 0; ii < m_gridLayersProperties->GetNumberRows(); ii++ )
{
wxString layer_name = m_Pcb->GetLayerName( LAYER_CMP_N - ii );
m_gridLayersProperties->SetCellValue( ii, LAYERS_GRID_NAME_POSITION, layer_name );
}
// Initialize the Rules List
InitRulesList();
/* Initialize the list of nets buffers
* (note the netcode 0 is not a real net, so it is not loaded)
*/
for( unsigned ii = 1; ; ii++ )
{
NETINFO_ITEM* net = m_Pcb->FindNet( ii );
if( net == NULL )
break;
m_StockNets.push_back( net );
// search the index in rules list for this net
int rules_idx = 0;
for( int jj = 0; jj < m_gridNetClassesProperties->GetNumberRows(); jj++ )
{
if( m_gridNetClassesProperties->GetRowLabelValue( jj ).CmpNoCase( net->GetClassName() )
== 0 )
{
rules_idx = jj;
break;
}
}
m_NetsLinkToClasses.push_back( rules_idx ); // All nets are set to default net class
}
InitializeRulesSelectionBoxes();
}
/** Function FillListBoxWithNetsNames
* populates the aListBox with net names members of the aNetclassIndex net class
* the "Client Data pointer" is used to store the index of nets in ne nets lists
*/
void DIALOG_DESIGN_RULES::FillListBoxWithNetsNames( wxListBox* aListBox, int aNetclassIndex )
{
aListBox->Clear();
unsigned idx = 0;
for( unsigned ii = 0; ii < m_StockNets.size(); ii++ )
{
if( aNetclassIndex == m_NetsLinkToClasses[ii] )
{
aListBox->Append( m_StockNets[ii]->GetNetname() );
// Store the index of this net
// This is a trick to get an unsigned integer index from a pointer value.
// Some compilers cannot accept to convert an unsigned to a pointer without complains
char * ptr = (char*)0 + ii;
aListBox->SetClientData( idx, ptr );
idx++;
}
}
}
/* Initialize the combno boxes by the list of existing net classes
*/
void DIALOG_DESIGN_RULES::InitializeRulesSelectionBoxes()
{
m_CBoxRightSelection->Clear();
m_CBoxLeftSelection->Clear();
for( int ii = 0; ii < m_gridNetClassesProperties->GetNumberRows(); ii++ )
{
m_CBoxRightSelection->Append( m_gridNetClassesProperties->GetRowLabelValue( ii ) );
m_CBoxLeftSelection->Append( m_gridNetClassesProperties->GetRowLabelValue( ii ) );
}
m_CBoxRightSelection->Select( 0 );
m_CBoxLeftSelection->Select( 0 );
m_buttonRightToLeft->Enable( false );
m_buttonLeftToRight->Enable( false );;
FillListBoxWithNetsNames( m_listBoxLeftNetSelect, m_CBoxLeftSelection->GetCurrentSelection() );
FillListBoxWithNetsNames( m_listBoxRightNetSelect, m_CBoxRightSelection->GetCurrentSelection() );
}
/* Initialize the Routable column, and the R/W property of some cells
*/
void DIALOG_DESIGN_RULES::SetRoutableLayerStatus()
{
m_gridLayersProperties->SetColFormatBool( LAYERS_GRID_ROUTABLE_POSITION );
for( int ii = 0; ii < m_gridLayersProperties->GetNumberRows(); ii++ )
{
int layer = LAYER_CMP_N - ii;
wxString value = layer < (m_ActivesLayersCount - 1) ? wxT( "1" ) : wxT( "0" );
if( m_ActivesLayersCount > 1 && layer == LAYER_CMP_N )
value = wxT( "1" );
if( layer == COPPER_LAYER_N )
value = wxT( "1" );
m_gridLayersProperties->SetCellValue( ii, LAYERS_GRID_ROUTABLE_POSITION, value );
m_gridLayersProperties->SetReadOnly( ii, LAYERS_GRID_ROUTABLE_POSITION );
// Set to Read Only cell for non existing copper layers:
m_gridLayersProperties->SetReadOnly( ii, LAYERS_GRID_STATUS_POSITION, value != wxT( "1" ) );
m_gridLayersProperties->SetReadOnly( ii, LAYERS_GRID_NAME_POSITION, value != wxT( "1" ) );
}
}
/* Initialize the rules list from board
*/
void DIALOG_DESIGN_RULES::InitRulesList()
{
NETCLASSES& netclasses = m_Pcb->m_NetClasses;
int ii = 0;
for( NETCLASSES::iterator i=netclasses.begin(); i!=netclasses.end(); ++i, ++ii )
{
NETCLASS* netclass = i->second;
// Creates one entry if needed
if( ii >= m_gridNetClassesProperties->GetNumberRows() )
m_gridNetClassesProperties->AppendRows();
// Init name
m_gridNetClassesProperties->SetRowLabelValue( ii, netclass->GetName() );
// Init data
wxString msg;
msg = ReturnStringFromValue( g_UnitMetric,
netclass->GetTrackWidth(),
m_Parent->m_InternalUnits, false );
m_gridNetClassesProperties->SetCellValue( ii, RULE_GRID_TRACKSIZE_POSITION, msg );
msg = ReturnStringFromValue( g_UnitMetric,
netclass->GetViaSize(),
m_Parent->m_InternalUnits, false );
m_gridNetClassesProperties->SetCellValue( ii, RULE_GRID_VIASIZE_POSITION, msg );
msg = ReturnStringFromValue( g_UnitMetric,
netclass->GetClearance(),
m_Parent->m_InternalUnits, false );
m_gridNetClassesProperties->SetCellValue( ii, RULE_GRID_CLEARANCE_POSITION, msg );
msg = ReturnStringFromValue( g_UnitMetric,
netclass->GetTrackMinWidth(),
m_Parent->m_InternalUnits, false );
m_gridNetClassesProperties->SetCellValue( ii, RULE_GRID_MINTRACKSIZE_POSITION, msg );
msg = ReturnStringFromValue( g_UnitMetric,
netclass->GetViaMinSize(),
m_Parent->m_InternalUnits, false );
m_gridNetClassesProperties->SetCellValue( ii, RULE_GRID_MINVIASIZE_POSITION, msg );
}
}
/* Copy the rules list to board
*/
void DIALOG_DESIGN_RULES::CopyRulesListToBoard()
{
NETCLASSES& netclasses = m_Pcb->m_NetClasses;
netclasses.Clear();
for( int ii = 0; ii < m_gridNetClassesProperties->GetNumberRows(); ii++ )
{
NETCLASS netclass( m_Pcb, m_gridNetClassesProperties->GetRowLabelValue( ii ) );
// Init data
netclass.SetTrackWidth(
ReturnValueFromString( g_UnitMetric,
m_gridNetClassesProperties->GetCellValue( ii,
RULE_GRID_TRACKSIZE_POSITION ),
m_Parent->m_InternalUnits ));
netclass.SetViaSize(
ReturnValueFromString( g_UnitMetric,
m_gridNetClassesProperties->GetCellValue( ii,
RULE_GRID_VIASIZE_POSITION ),
m_Parent->m_InternalUnits ));
netclass.SetClearance(
ReturnValueFromString( g_UnitMetric,
m_gridNetClassesProperties->GetCellValue( ii,
RULE_GRID_CLEARANCE_POSITION ),
m_Parent->m_InternalUnits ));
netclass.SetTrackMinWidth(
ReturnValueFromString( g_UnitMetric,
m_gridNetClassesProperties->GetCellValue( ii,
RULE_GRID_MINTRACKSIZE_POSITION ),
m_Parent->m_InternalUnits ));
netclass.SetViaMinSize(
ReturnValueFromString( g_UnitMetric,
m_gridNetClassesProperties->GetCellValue( ii,
RULE_GRID_MINVIASIZE_POSITION ),
m_Parent->m_InternalUnits ));
// Copy the list of nets associated to this netclass:
for( unsigned idx = 0; idx < m_StockNets.size(); idx++ )
{
if( m_NetsLinkToClasses[idx] == ii )
netclass.Add( m_StockNets[idx]->GetNetname() );
}
// this calls copy constructor, so all data must be in 'netclass' before Add()ing.
m_Pcb->m_NetClasses.Add( netclass );
}
m_Pcb->SynchronizeNetsAndNetClasses();
}
/*****************************************************************/
void DIALOG_DESIGN_RULES::OnCancelButtonClick( wxCommandEvent& event )
/*****************************************************************/
{
EndModal( 0 );
}
/**************************************************************************/
void DIALOG_DESIGN_RULES::OnOkButtonClick( wxCommandEvent& event )
/**************************************************************************/
{
if( !TestDataValidity() )
{
DisplayError( this, _( "Errors detected, Abort" ) );
return;
}
g_DesignSettings.m_CopperLayerCount = m_ActivesLayersCount;
// Initialize the new layer name
for( int ii = 0; ii < m_gridLayersProperties->GetNumberRows(); ii++ )
{
wxString layer_name = m_gridLayersProperties->GetCellValue( ii, LAYERS_GRID_NAME_POSITION );
if( layer_name != m_Pcb->GetLayerName( LAYER_CMP_N - ii ) )
{
m_Pcb->SetLayerName( LAYER_CMP_N - ii, layer_name );
}
}
// Initialize the layer type
for( int ii = 0; ii < m_gridLayersProperties->GetNumberRows(); ii++ )
{
wxString txt = m_gridLayersProperties->GetCellValue( ii, LAYERS_GRID_STATUS_POSITION );
int layer = LAYER_CMP_N - ii;
for( int jj = 0; jj < 3; jj++ )
{
if( m_LayersTypeName[jj] == txt )
{
m_Pcb->SetLayerType( layer, m_LayersType[jj] );
break;
}
}
}
CopyRulesListToBoard();
EndModal( wxID_OK );
}
/**************************************************************************/
void DIALOG_DESIGN_RULES::OnLayerCountClick( wxCommandEvent& event )
/**************************************************************************/
{
m_ActivesLayersCount = m_LayersCountSelection->GetSelection() * 2;
if( m_ActivesLayersCount <= 0 )
m_ActivesLayersCount = 1;
// Reinit the routable layers status
SetRoutableLayerStatus();
}
/**************************************************************************/
void DIALOG_DESIGN_RULES::OnAddNetclassClick( wxCommandEvent& event )
/**************************************************************************/
{
wxString class_name;
if( Get_Message( _( "New Net Class Name:" ),
wxEmptyString,
class_name,
this ) )
return;
// The name must dot exists:
for( int ii = 0; ii < m_gridNetClassesProperties->GetNumberRows(); ii++ )
{
wxString value;
value = m_gridNetClassesProperties->GetRowLabelValue( ii );
if( class_name.CmpNoCase( value ) == 0 ) // Already exists!
{
DisplayError( this, _( "This NetClass is already existing, cannot add it; Aborted" ) );
return;
}
}
m_gridNetClassesProperties->AppendRows();
m_gridNetClassesProperties->SetRowLabelValue(
m_gridNetClassesProperties->GetNumberRows() - 1,
class_name );
// Copy values of the previous class:
int irow = m_gridNetClassesProperties->GetNumberRows() - 1;
for( int icol = 0; icol < m_gridNetClassesProperties->GetNumberCols(); icol++ )
{
wxString value;
value = m_gridNetClassesProperties->GetCellValue( irow - 1, icol );
m_gridNetClassesProperties->SetCellValue( irow, icol, value );
}
InitializeRulesSelectionBoxes();
}
/**************************************************************************/
void DIALOG_DESIGN_RULES::OnRemoveNetclassClick( wxCommandEvent& event )
/**************************************************************************/
{
wxArrayInt select = m_gridNetClassesProperties->GetSelectedRows();
for( int ii = select.GetCount() - 1; ii >= 0; ii-- )
{
if( select[ii] != 0 ) // Do not remove the default class
{
m_gridNetClassesProperties->DeleteRows( select[ii] );
// reset the net class to default for nets member of the removed net class
for( unsigned jj = 0; jj< m_NetsLinkToClasses.size(); jj++ )
if( m_NetsLinkToClasses[jj] == ii )
m_NetsLinkToClasses[jj] = 0; // Reset to default net class
}
}
InitializeRulesSelectionBoxes();
}
/*
* Called on the left Choice Box selection
*/
void DIALOG_DESIGN_RULES::OnLeftCBSelection( wxCommandEvent& event )
{
FillListBoxWithNetsNames( m_listBoxLeftNetSelect, m_CBoxLeftSelection->GetCurrentSelection() );
if( m_CBoxLeftSelection->GetCurrentSelection() == m_CBoxRightSelection->GetCurrentSelection() )
{
m_buttonRightToLeft->Enable( false );
m_buttonLeftToRight->Enable( false );
}
else
{
m_buttonRightToLeft->Enable( true );
m_buttonLeftToRight->Enable( true );
}
}
/*
* Called on the Right Choice Box selection
*/
void DIALOG_DESIGN_RULES::OnRightCBSelection( wxCommandEvent& event )
{
FillListBoxWithNetsNames( m_listBoxRightNetSelect, m_CBoxRightSelection->GetCurrentSelection() );
if( m_CBoxLeftSelection->GetCurrentSelection() == m_CBoxRightSelection->GetCurrentSelection() )
{
m_buttonRightToLeft->Enable( false );
m_buttonLeftToRight->Enable( false );;
}
else
{
m_buttonRightToLeft->Enable( true );
m_buttonLeftToRight->Enable( true );
}
}
/* Called on clicking the "<<<" or Copy Right to Left button:
* Selected items are moved from the right list to the left list
*/
void DIALOG_DESIGN_RULES::OnRightToLeftCopyButton( wxCommandEvent& event )
{
int idx_class = m_CBoxLeftSelection->GetCurrentSelection();
if( idx_class == wxNOT_FOUND )
return;
for( unsigned ii = 0; ii < m_listBoxRightNetSelect->GetCount(); ii++ )
{
if( !m_listBoxRightNetSelect->IsSelected( ii ) )
continue;
// This is a trick to get an unsigned integer index from a pointer value.
// Some compilers cannot accept to convert a pointer to an unsigned without complains
char * ptr = (char*) m_listBoxRightNetSelect->GetClientData( ii );
unsigned idx = ptr - (char*)0;
m_NetsLinkToClasses[idx] = idx_class;
}
FillListBoxWithNetsNames( m_listBoxLeftNetSelect, m_CBoxLeftSelection->GetCurrentSelection() );
FillListBoxWithNetsNames( m_listBoxRightNetSelect, m_CBoxRightSelection->GetCurrentSelection() );
}
/* Called on clicking the ">>>" or Copy Left to Right button:
* Selected items are moved from the left list to the right list
*/
void DIALOG_DESIGN_RULES::OnLeftToRightCopyButton( wxCommandEvent& event )
{
int idx_class = m_CBoxRightSelection->GetCurrentSelection();
if( idx_class == wxNOT_FOUND )
return;
for( unsigned ii = 0; ii < m_listBoxLeftNetSelect->GetCount(); ii++ )
{
if( !m_listBoxLeftNetSelect->IsSelected( ii ) )
continue;
// This is a trick to get an unsigned integer index from a pointer value.
// Some compilers cannot accept to convert a pointer to an unsigned without complains
char * ptr = (char*) m_listBoxLeftNetSelect->GetClientData( ii );
unsigned idx = ptr - (char*)0 ;
m_NetsLinkToClasses[idx] = idx_class;
}
FillListBoxWithNetsNames( m_listBoxLeftNetSelect, m_CBoxLeftSelection->GetCurrentSelection() );
FillListBoxWithNetsNames( m_listBoxRightNetSelect, m_CBoxRightSelection->GetCurrentSelection() );
}
/* Called on clicking the left "select all" button:
* select alls items of the left netname list lisxt box
*/
void DIALOG_DESIGN_RULES::OnLeftSelectAllButton( wxCommandEvent& event )
{
for( unsigned ii = 0; ii < m_listBoxLeftNetSelect->GetCount(); ii++ )
m_listBoxLeftNetSelect->SetSelection( ii );
}
/* Called on clicking the right "select all" button:
* select alls items of the right netname list lisxt box
*/
void DIALOG_DESIGN_RULES::OnRightSelectAllButton( wxCommandEvent& event )
{
for( unsigned ii = 0; ii < m_listBoxRightNetSelect->GetCount(); ii++ )
m_listBoxRightNetSelect->SetSelection( ii );
}
/* TestDataValidity
* Performs a control of data validity
* set the background of a bad cell in RED and display an info message
* @return true if Ok, false if error
*/
bool DIALOG_DESIGN_RULES::TestDataValidity()
{
bool success = true;
m_MessagesList->SetPage(wxEmptyString); // Clear message list
// Test duplicate layers names
for( int ii = 0; ii < m_gridLayersProperties->GetNumberRows() - 1; ii++ )
{
wxString value = m_gridLayersProperties->GetCellValue( ii, LAYERS_GRID_NAME_POSITION );
for( int jj = ii+1; jj < m_gridLayersProperties->GetNumberRows(); jj++ )
{
wxString othervalue = m_gridLayersProperties->GetCellValue( ii,
LAYERS_GRID_NAME_POSITION );
othervalue = m_gridLayersProperties->GetCellValue( jj, LAYERS_GRID_NAME_POSITION );
if( value.CmpNoCase( othervalue ) == 0 ) // Already exists!
{
wxString text;
text.Printf( _(
"<small>This layer name <b>%s</b> is already existing<br>" ),
value.GetData() );
m_MessagesList->AppendToPage( text );
success = false;
}
}
}
int value;
int minvalue;
for( int ii = 0; ii < m_gridNetClassesProperties->GetNumberRows(); ii++ )
{
value = ReturnValueFromString( g_UnitMetric,
m_gridNetClassesProperties->GetCellValue(
ii, RULE_GRID_TRACKSIZE_POSITION ),
m_Parent->m_InternalUnits );
minvalue = ReturnValueFromString( g_UnitMetric,
m_gridNetClassesProperties->GetCellValue( ii,
RULE_GRID_MINTRACKSIZE_POSITION ),
m_Parent->m_InternalUnits );
if( value < minvalue )
{
success = false;
m_MessagesList->AppendToPage( _( "The <b>track</b> minimum size is bigger than the size<br>" ) );
}
value = ReturnValueFromString( g_UnitMetric,
m_gridNetClassesProperties->GetCellValue(
ii, RULE_GRID_VIASIZE_POSITION ),
m_Parent->m_InternalUnits );
minvalue = ReturnValueFromString( g_UnitMetric,
m_gridNetClassesProperties->GetCellValue( ii,
RULE_GRID_MINVIASIZE_POSITION ),
m_Parent->m_InternalUnits );
if( value < minvalue )
{
success = false;
m_MessagesList->AppendToPage( _( "The <b>via</b> minimum size is bigger than the size<br>" ) );
}
}
return success;
}