/** * @file dialog_design_rules.cpp */ /* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2004-2009 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2009 Dick Hollenbeck, dick@softplc.com * Copyright (C) 2009-2015 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 relative to the design rules editor */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Column labels for net lists #define NET_TITLE _( "Net" ) #define CLASS_TITLE _( "Class" ) // Field Positions on rules grid enum { GRID_CLEARANCE, GRID_TRACKSIZE, GRID_VIASIZE, GRID_VIADRILL, GRID_uVIASIZE, GRID_uVIADRILL, GRID_DIFF_PAIR_WIDTH, GRID_DIFF_PAIR_GAP }; const wxString DIALOG_DESIGN_RULES::wildCard = _( "* (Any)" ); // dialog should remember its previously selected tab int DIALOG_DESIGN_RULES::s_LastTabSelection = -1; // methods for the helper class NETS_LIST_CTRL wxString NETS_LIST_CTRL::OnGetItemText( long item, long column ) const { if( column == 0 ) { if( item < (long) m_Netnames.GetCount() ) return m_Netnames[item]; else return wxEmptyString; } else if( item < (long) m_Classnames.GetCount() ) return m_Classnames[item]; return wxEmptyString; } void NETS_LIST_CTRL::SetRowItems( unsigned aRow, const wxString& aNetname, const wxString& aNetclassName ) { // insert blanks if aRow is larger than existing row count unsigned cnt = m_Netnames.GetCount(); if( cnt <= aRow ) m_Netnames.Add( wxEmptyString, aRow - cnt + 1 ); cnt = m_Classnames.GetCount(); if( cnt <= aRow ) m_Classnames.Add( wxEmptyString, aRow - cnt + 1 ); if( (int)aRow <= GetItemCount() ) SetItemCount( aRow + 1 ); m_Netnames[aRow] = aNetname; m_Classnames[aRow] = aNetclassName; } /** * Function EnsureGridColumnWidths * resizes all the columns in a wxGrid based only on the requirements of the * column titles and not on the grid cell requirements, assuming that the grid * cell width requirements are narrower than the column title requirements. */ // @todo: maybe move this to common.cpp if it works. static void EnsureGridColumnWidths( wxWindow* aShower, wxGrid* aGrid ) { wxWindowDC sDC( aShower ); sDC.SetFont( aGrid->GetLabelFont() ); int colCount = aGrid->GetNumberCols(); for( int col = 0; col < colCount; ++col ) { // add two spaces to the text and size it. wxString colText = aGrid->GetColLabelValue( col ) + wxT( " " ); wxSize needed = sDC.GetTextExtent( colText ); // set the width of this column aGrid->SetColSize( col, needed.x ); } } static void EnsureGridRowTitleWidth( wxWindow* aShower, wxGrid* aGrid, int aMinWidth ) { wxWindowDC sDC( aShower ); sDC.SetFont( aGrid->GetLabelFont() ); int minsize = aMinWidth; int rowCount = aGrid->GetNumberRows(); for( int row = 0; row < rowCount; ++row ) { // add two spaces to the text and size it. wxString rowText = aGrid->GetRowLabelValue( row ) + wxT( " " ); wxSize needed = sDC.GetTextExtent( rowText ); minsize = std::max( minsize, needed.x ); } // set the width of the row laberls aGrid->SetRowLabelSize( minsize ); } DIALOG_DESIGN_RULES::DIALOG_DESIGN_RULES( PCB_EDIT_FRAME* parent ) : DIALOG_DESIGN_RULES_BASE( parent ) { m_Parent = parent; SetAutoLayout( true ); m_initialRowLabelsSize = m_grid->GetRowLabelSize(); EnsureGridColumnWidths( this, m_grid ); // override any column widths set by wxformbuilder. wxListItem column0; wxListItem column1; column0.Clear(); column1.Clear(); column0.SetMask( wxLIST_MASK_TEXT ); column1.SetMask( wxLIST_MASK_TEXT ); column0.SetText( NET_TITLE ); column1.SetText( CLASS_TITLE ); m_leftListCtrl->InsertColumn( 0, column0 ); m_leftListCtrl->InsertColumn( 1, column1 ); m_leftListCtrl->SetColumnWidth( 0, wxLIST_AUTOSIZE ); m_leftListCtrl->SetColumnWidth( 1, wxLIST_AUTOSIZE ); m_rightListCtrl->InsertColumn( 0, column0 ); m_rightListCtrl->InsertColumn( 1, column1 ); m_rightListCtrl->SetColumnWidth( 0, wxLIST_AUTOSIZE ); m_rightListCtrl->SetColumnWidth( 1, wxLIST_AUTOSIZE ); // if user has been into the dialog before, go back to same tab if( s_LastTabSelection != -1 ) { m_DRnotebook->SetSelection( s_LastTabSelection ); } InitDialogRules(); EnsureGridRowTitleWidth( this, m_grid, m_initialRowLabelsSize ); m_sdbSizer1OK->SetDefault(); // Allow tabbing out of grid controls. m_grid->SetTabBehaviour( wxGrid::Tab_Leave ); m_gridViaSizeList->SetTabBehaviour( wxGrid::Tab_Leave ); m_gridTrackWidthList->SetTabBehaviour( wxGrid::Tab_Leave ); Layout(); // Now all widgets have the size fixed, call FinishDialogSettings FinishDialogSettings(); } void DIALOG_DESIGN_RULES::InitDialogRules() { // @todo: Move the initialization code into TransferDataToWindow() to follow wxWidgets // dialog data transfer convention. SetFocus(); SetReturnCode( 0 ); m_Pcb = m_Parent->GetBoard(); m_BrdSettings = &m_Pcb->GetDesignSettings(); // Initialize the Rules List InitRulesList(); // Reassure that all nets have net classes assigned m_Pcb->BuildListOfNets(); // copy all NETs into m_AllNets by adding them as NETCUPs. // @todo go fix m_Pcb->SynchronizeNetsAndNetClasses() so that the netcode==0 is not // present in the BOARD::m_NetClasses NETCLASSES& netclasses = m_BrdSettings->m_NetClasses; NETCLASSPTR netclass = netclasses.GetDefault(); // Initialize list of nets for Default Net Class for( NETCLASS::iterator name = netclass->begin(); name != netclass->end(); ++name ) { m_AllNets.push_back( NETCUP( *name, netclass->GetName() ) ); } // Initialize list of nets for others (custom) Net Classes for( NETCLASSES::const_iterator nc = netclasses.begin(); nc != netclasses.end(); ++nc ) { netclass = nc->second; for( NETCLASS::const_iterator name = netclass->begin(); name != netclass->end(); ++name ) { m_AllNets.push_back( NETCUP( *name, netclass->GetName() ) ); } } InitializeRulesSelectionBoxes(); InitGlobalRules(); } void DIALOG_DESIGN_RULES::InitGlobalRules() { // Set unit labels const wxString units = GetAbbreviatedUnitsLabel( g_UserUnit ); m_ViaMinUnits->SetLabel( units ); m_ViaMinDrillUnits->SetLabel( units ); m_MicroViaMinSizeUnits->SetLabel( units ); m_MicroViaMinDrillUnits->SetLabel( units ); m_TrackMinWidthUnits->SetLabel( units ); PutValueInLocalUnits( *m_SetViasMinSizeCtrl, m_BrdSettings->m_ViasMinSize ); PutValueInLocalUnits( *m_SetViasMinDrillCtrl, m_BrdSettings->m_ViasMinDrill ); m_OptAllowBlindBuriedVias->SetValue( m_BrdSettings->m_BlindBuriedViaAllowed ); m_OptAllowMicroVias->SetValue( m_BrdSettings->m_MicroViasAllowed ); CheckAllowMicroVias(); PutValueInLocalUnits( *m_SetMicroViasMinSizeCtrl, m_BrdSettings->m_MicroViasMinSize ); PutValueInLocalUnits( *m_SetMicroViasMinDrillCtrl, m_BrdSettings->m_MicroViasMinDrill ); PutValueInLocalUnits( *m_SetTrackMinWidthCtrl, m_BrdSettings->m_TrackMinWidth ); // Initialize Vias and Tracks sizes lists. // note we display only extra values, never the current netclass value. // (the first value in history list) m_TracksWidthList = m_BrdSettings->m_TrackWidthList; m_TracksWidthList.erase( m_TracksWidthList.begin() ); // remove the netclass value m_ViasDimensionsList = m_BrdSettings->m_ViasDimensionsList; m_ViasDimensionsList.erase( m_ViasDimensionsList.begin() ); // remove the netclass value InitDimensionsLists(); } void DIALOG_DESIGN_RULES::InitDimensionsLists() { wxString msg; // Compute the column widths here, after setting texts msg = wxT("000000.000000"); // This is a very long text to display values. // Actual values are shorter. m_gridViaSizeList->SetCellValue( 0, 0, msg ); m_gridViaSizeList->SetCellValue( 0, 1, msg ); m_gridTrackWidthList->SetCellValue( 0, 0, msg ); m_gridViaSizeList->SetColMinimalWidth( 0, 150 ); m_gridViaSizeList->SetColMinimalWidth( 1, 150 ); m_gridViaSizeList->AutoSizeColumns( true ); m_gridTrackWidthList->SetColMinimalWidth( 0, 150 ); m_gridTrackWidthList->AutoSizeColumns( true ); m_gridViaSizeList->SetColMinimalWidth( 1, 150 ); // Fill cells with actual values: m_gridViaSizeList->SetCellValue( 0, 0, wxEmptyString ); m_gridViaSizeList->SetCellValue( 0, 1, wxEmptyString ); m_gridTrackWidthList->SetCellValue( 0, 0, wxEmptyString ); // Give a correct size to row labels column m_gridViaSizeList->SetRowLabelSize( wxGRID_AUTOSIZE ); m_gridTrackWidthList->SetRowLabelSize( wxGRID_AUTOSIZE ); for( unsigned ii = 0; ii < m_TracksWidthList.size(); ii++ ) { msg = StringFromValue( g_UserUnit, m_TracksWidthList[ii], false ); m_gridTrackWidthList->SetCellValue( ii, 0, msg ); } for( unsigned ii = 0; ii < m_ViasDimensionsList.size(); ii++ ) { msg = StringFromValue( g_UserUnit, m_ViasDimensionsList[ii].m_Diameter, false ); m_gridViaSizeList->SetCellValue( ii, 0, msg ); if( m_ViasDimensionsList[ii].m_Drill > 0 ) { msg = StringFromValue( g_UserUnit, m_ViasDimensionsList[ii].m_Drill, false ); m_gridViaSizeList->SetCellValue( ii, 1, msg ); } } } // Sort comparison function (helper for makePointers() ) static bool sortByClassThenName( NETCUP* a, NETCUP* b ) { // return a < b if( a->clazz < b->clazz ) return true; // inside the same class, sort by net name: if( a->clazz == b->clazz ) { if( a->net < b->net ) return true; } return false; } void DIALOG_DESIGN_RULES::makePointers( PNETCUPS* aList, const wxString& aNetClassName ) { aList->clear(); if( wildCard == aNetClassName ) { for( NETCUPS::iterator n = m_AllNets.begin(); n != m_AllNets.end(); ++n ) { aList->push_back( &*n ); } sort( aList->begin(), aList->end(), sortByClassThenName ); // could use a different sort order for wildCard case. } else { for( NETCUPS::iterator n = m_AllNets.begin(); n != m_AllNets.end(); ++n ) { if( n->clazz == aNetClassName ) aList->push_back( &*n ); } sort( aList->begin(), aList->end(), sortByClassThenName ); } } void DIALOG_DESIGN_RULES::FillListBoxWithNetNames( NETS_LIST_CTRL* aListCtrl, const wxString& aNetClass ) { aListCtrl->ClearList(); PNETCUPS ptrList; // get a subset of m_AllNets in pointer form, sorted as desired. makePointers( &ptrList, aNetClass ); #if 0 && defined(DEBUG) int r = 0; for( PNETCUPS::iterator i = ptrList.begin(); i!=ptrList.end(); ++i, ++r ) { printf( "[%d]: %s %s\n", r, TO_UTF8( (*i)->net ), TO_UTF8( (*i)->clazz ) ); } #endif // Add netclass info to m_Netnames and m_Classnames wxArrayString buffers // aListCtrl uses wxLC_VIRTUAL option, so this is fast wxClientDC sDC( aListCtrl ); int row = 0; // recompute the column widths here, after setting texts int net_colsize = sDC.GetTextExtent( NET_TITLE ).x; int class_colsize = sDC.GetTextExtent( CLASS_TITLE ).x; for( PNETCUPS::iterator i = ptrList.begin(); i!=ptrList.end(); ++i, ++row ) { wxSize net_needed = sDC.GetTextExtent( (*i)->net ); wxSize class_needed = sDC.GetTextExtent( (*i)->clazz ); net_colsize = std::max( net_colsize, net_needed.x ); class_colsize = std::max( class_colsize, class_needed.x ); aListCtrl->SetRowItems( row, (*i)->net, (*i)->clazz ); } int margin = sDC.GetTextExtent( wxT( "XX" ) ).x; aListCtrl->SetColumnWidth( 0, net_colsize + margin ); aListCtrl->SetColumnWidth( 1, class_colsize + margin ); aListCtrl->Refresh(); } /* Populates combo boxes with the list of existing net classes */ void DIALOG_DESIGN_RULES::InitializeRulesSelectionBoxes() { m_rightClassChoice->Clear(); m_leftClassChoice->Clear(); m_rightClassChoice->Append( wildCard ); m_leftClassChoice->Append( wildCard ); for( int ii = 0; ii < m_grid->GetNumberRows(); ii++ ) { m_rightClassChoice->Append( m_grid->GetRowLabelValue( ii ) ); m_leftClassChoice->Append( m_grid->GetRowLabelValue( ii ) ); } m_rightClassChoice->Select( 0 ); m_leftClassChoice->Select( 0 ); m_buttonRightToLeft->Enable( false ); m_buttonLeftToRight->Enable( false ); FillListBoxWithNetNames( m_leftListCtrl, m_leftClassChoice->GetStringSelection() ); FillListBoxWithNetNames( m_rightListCtrl, m_rightClassChoice->GetStringSelection() ); } /* Initialize the rules list from board */ static void class2gridRow( wxGrid* grid, int row, const NETCLASSPTR& nc ) { wxString msg; // label is netclass name grid->SetRowLabelValue( row, nc->GetName() ); msg = StringFromValue( g_UserUnit, nc->GetClearance() ); grid->SetCellValue( row, GRID_CLEARANCE, msg ); msg = StringFromValue( g_UserUnit, nc->GetTrackWidth() ); grid->SetCellValue( row, GRID_TRACKSIZE, msg ); msg = StringFromValue( g_UserUnit, nc->GetViaDiameter() ); grid->SetCellValue( row, GRID_VIASIZE, msg ); msg = StringFromValue( g_UserUnit, nc->GetViaDrill() ); grid->SetCellValue( row, GRID_VIADRILL, msg ); msg = StringFromValue( g_UserUnit, nc->GetuViaDiameter() ); grid->SetCellValue( row, GRID_uVIASIZE, msg ); msg = StringFromValue( g_UserUnit, nc->GetuViaDrill() ); grid->SetCellValue( row, GRID_uVIADRILL, msg ); msg = StringFromValue( g_UserUnit, nc->GetDiffPairGap() ); grid->SetCellValue( row, GRID_DIFF_PAIR_GAP, msg ); msg = StringFromValue( g_UserUnit, nc->GetDiffPairWidth() ); grid->SetCellValue( row, GRID_DIFF_PAIR_WIDTH, msg ); } void DIALOG_DESIGN_RULES::InitRulesList() { NETCLASSES& netclasses = m_BrdSettings->m_NetClasses; // the +1 is for the Default NETCLASS. if( netclasses.GetCount() + 1 > (unsigned) m_grid->GetNumberRows() ) { m_grid->AppendRows( netclasses.GetCount() + 1 - m_grid->GetNumberRows() ); } // enter the Default NETCLASS. class2gridRow( m_grid, 0, netclasses.GetDefault() ); // enter others netclasses int row = 1; for( NETCLASSES::iterator i = netclasses.begin(); i!=netclasses.end(); ++i, ++row ) { NETCLASSPTR netclass = i->second; class2gridRow( m_grid, row, netclass ); } } static void gridRow2class( wxGrid* grid, int row, const NETCLASSPTR& nc ) { #define MYCELL( col ) \ ValueFromString( g_UserUnit, grid->GetCellValue( row, col ) ) nc->SetClearance( MYCELL( GRID_CLEARANCE ) ); nc->SetTrackWidth( MYCELL( GRID_TRACKSIZE ) ); nc->SetViaDiameter( MYCELL( GRID_VIASIZE ) ); nc->SetViaDrill( MYCELL( GRID_VIADRILL ) ); nc->SetuViaDiameter( MYCELL( GRID_uVIASIZE ) ); nc->SetuViaDrill( MYCELL( GRID_uVIADRILL ) ); nc->SetDiffPairGap( MYCELL( GRID_DIFF_PAIR_GAP ) ); nc->SetDiffPairWidth( MYCELL( GRID_DIFF_PAIR_WIDTH ) ); } void DIALOG_DESIGN_RULES::CopyRulesListToBoard() { // Commit any pending in-place edits first m_grid->SaveEditControlValue(); NETCLASSES& netclasses = m_BrdSettings->m_NetClasses; // Remove all netclasses from board. We'll copy new list after netclasses.Clear(); // Copy the default NetClass: gridRow2class( m_grid, 0, netclasses.GetDefault() ); // Copy other NetClasses : for( int row = 1; row < m_grid->GetNumberRows(); ++row ) { NETCLASSPTR nc = std::make_shared( m_grid->GetRowLabelValue( row ) ); if( !m_BrdSettings->m_NetClasses.Add( nc ) ) { // this netclass cannot be added because another netclass with the same name exists // Should not occur because OnAddNetclassClick() tests for existing NetClass names wxString msg; msg.Printf( wxT( "CopyRulesListToBoard(): The NetClass \"%s\" already exists. Skip" ), GetChars( m_grid->GetRowLabelValue( row ) ) ); wxMessageBox( msg ); continue; } gridRow2class( m_grid, row, nc ); } // Now read all nets and push them in the corresponding netclass net buffer for( NETCUPS::const_iterator netcup = m_AllNets.begin(); netcup != m_AllNets.end(); ++netcup ) { NETCLASSPTR nc = netclasses.Find( netcup->clazz ); wxASSERT( nc ); nc->Add( netcup->net ); } m_Pcb->SynchronizeNetsAndNetClasses(); } void DIALOG_DESIGN_RULES::CopyGlobalRulesToBoard() { m_BrdSettings->m_BlindBuriedViaAllowed = m_OptAllowBlindBuriedVias->GetValue(); // Update vias minimum values for DRC m_BrdSettings->m_ViasMinSize = ValueFromTextCtrl( *m_SetViasMinSizeCtrl ); m_BrdSettings->m_ViasMinDrill = ValueFromTextCtrl( *m_SetViasMinDrillCtrl ); m_BrdSettings->m_MicroViasAllowed = m_OptAllowMicroVias->GetValue(); // Update microvias minimum values for DRC m_BrdSettings->m_MicroViasMinSize = ValueFromTextCtrl( *m_SetMicroViasMinSizeCtrl ); m_BrdSettings->m_MicroViasMinDrill = ValueFromTextCtrl( *m_SetMicroViasMinDrillCtrl ); // Update tracks minimum values for DRC m_BrdSettings->m_TrackMinWidth = ValueFromTextCtrl( *m_SetTrackMinWidthCtrl ); } void DIALOG_DESIGN_RULES::CopyDimensionsListsToBoard() { wxString msg; // Commit any pending in-place edits from grid controls m_gridTrackWidthList->SaveEditControlValue(); m_gridViaSizeList->SaveEditControlValue(); // Reinitialize m_TrackWidthList m_TracksWidthList.clear(); for( int row = 0; row < m_gridTrackWidthList->GetNumberRows(); ++row ) { msg = m_gridTrackWidthList->GetCellValue( row, 0 ); if( msg.IsEmpty() ) continue; int value = ValueFromString( g_UserUnit, msg ); m_TracksWidthList.push_back( value ); } // Sort new list by by increasing value sort( m_TracksWidthList.begin(), m_TracksWidthList.end() ); // Reinitialize m_ViasDimensionsList m_ViasDimensionsList.clear(); for( int row = 0; row < m_gridViaSizeList->GetNumberRows(); ++row ) { msg = m_gridViaSizeList->GetCellValue( row, 0 ); if( msg.IsEmpty() ) continue; int value = ValueFromString( g_UserUnit, msg ); VIA_DIMENSION via_dim; via_dim.m_Diameter = value; msg = m_gridViaSizeList->GetCellValue( row, 1 ); if( !msg.IsEmpty() ) { value = ValueFromString( g_UserUnit, msg ); via_dim.m_Drill = value; } m_ViasDimensionsList.push_back( via_dim ); } // Sort new list by by increasing value sort( m_ViasDimensionsList.begin(), m_ViasDimensionsList.end() ); std::vector* tlist = &m_BrdSettings->m_TrackWidthList; // Remove old "custom" sizes tlist->erase( tlist->begin() + 1, tlist->end() ); // Add new "custom" sizes tlist->insert( tlist->end(), m_TracksWidthList.begin(), m_TracksWidthList.end() ); // Reinitialize m_ViaSizeList std::vector* vialist = &m_BrdSettings->m_ViasDimensionsList; vialist->erase( vialist->begin() + 1, vialist->end() ); vialist->insert( vialist->end(), m_ViasDimensionsList.begin(), m_ViasDimensionsList.end() ); } void DIALOG_DESIGN_RULES::OnNotebookPageChanged( wxNotebookEvent& event ) { s_LastTabSelection = event.GetSelection(); // Skip() allows OSX to properly refresh controls. event.Skip(); } bool DIALOG_DESIGN_RULES::TransferDataFromWindow() { if( !wxDialog::TransferDataFromWindow() ) return false; wxString errorMsg; if( !TestDataValidity( &errorMsg ) ) { HTML_MESSAGE_BOX dlg( this, _( "Design Rule Setting Error" ) ); dlg.MessageSet( errorMsg ); dlg.ShowModal(); return false; } CopyRulesListToBoard(); CopyGlobalRulesToBoard(); CopyDimensionsListsToBoard(); m_BrdSettings->SetCurrentNetClass( NETCLASS::Default ); //this event causes the routing tool to reload its design rules information TOOL_MANAGER* toolManager = m_Parent->GetToolManager(); TOOL_EVENT event( TC_COMMAND, TA_MODEL_CHANGE, AS_ACTIVE ); toolManager->ProcessEvent( event ); return true; } void DIALOG_DESIGN_RULES::OnAddNetclassClick( wxCommandEvent& event ) { wxString class_name; // @todo set validator to ensure net class name is valid rather than all of the checks // after the OK button has been selected. WX_TEXT_ENTRY_DIALOG dlg( this, _( "New Net Class Name:" ), wxEmptyString, class_name ); if( dlg.ShowModal() != wxID_OK ) return; // canceled by user class_name = dlg.GetValue(); class_name.Trim( true ); class_name.Trim( false ); if( class_name.IsEmpty() ) return; // empty name not allowed // The name must dot exists: for( int ii = 0; ii < m_grid->GetNumberRows(); ii++ ) { wxString value; value = m_grid->GetRowLabelValue( ii ); if( class_name.CmpNoCase( value ) == 0 ) // Already exists! { DisplayError( this, _( "Duplicate net class names are not allowed." ) ); return; } } m_grid->AppendRows(); m_grid->SetRowLabelValue( m_grid->GetNumberRows() - 1, class_name ); // Copy values of the default class: int irow = m_grid->GetNumberRows() - 1; for( int icol = 0; icol < m_grid->GetNumberCols(); icol++ ) { wxString value; value = m_grid->GetCellValue( 0, icol ); m_grid->SetCellValue( irow, icol, value ); } EnsureGridRowTitleWidth( this, m_grid, m_initialRowLabelsSize ); InitializeRulesSelectionBoxes(); } // Sort function for wxArrayInt. Items (ints) are sorted by decreasing value // used in DIALOG_DESIGN_RULES::OnRemoveNetclassClick int sort_int( int* first, int* second ) { return *second - *first; } void DIALOG_DESIGN_RULES::OnRemoveNetclassClick( wxCommandEvent& event ) { wxArrayInt select = m_grid->GetSelectedRows(); // Sort selection by decreasing index order: select.Sort( sort_int ); bool reinit = false; // rows labels are not removed when deleting rows: they are not deleted. // So we must store them, remove corresponding labels and reinit them wxArrayString labels; for( int ii = 0; ii < m_grid->GetNumberRows(); ii++ ) labels.Add( m_grid->GetRowLabelValue( ii ) ); // Delete rows from last to first (this is the order wxArrayInt select after sorting) ) // This order is Ok when removing rows for( unsigned ii = 0; ii < select.GetCount(); ii++ ) { int grid_row = select[ii]; if( grid_row != 0 ) // Do not remove the default class { wxString classname = m_grid->GetRowLabelValue( grid_row ); m_grid->DeleteRows( grid_row ); labels.RemoveAt( grid_row ); // Remove corresponding row label reinit = true; // reset the net class to default for members of the removed class swapNetClass( classname, NETCLASS::Default ); } else wxMessageBox( _( "The default net class cannot be removed" ) ); } if( reinit ) { // Reinit labels : for( unsigned ii = 1; ii < labels.GetCount(); ii++ ) m_grid->SetRowLabelValue( ii, labels[ii] ); InitializeRulesSelectionBoxes(); EnsureGridRowTitleWidth( this, m_grid, m_initialRowLabelsSize ); } } void DIALOG_DESIGN_RULES::CheckAllowMicroVias() { bool enabled = m_OptAllowMicroVias->GetValue(); m_SetMicroViasMinSizeCtrl->Enable( enabled ); m_SetMicroViasMinDrillCtrl->Enable( enabled ); } /** * Function OnAllowMicroVias * is called whenever the AllowMicroVias checkbox is toggled */ void DIALOG_DESIGN_RULES::OnAllowMicroVias( wxCommandEvent& event ) { CheckAllowMicroVias(); } void DIALOG_DESIGN_RULES::OnMoveUpSelectedNetClass( wxCommandEvent& event ) { // Cannot move up rules if we have 1 or 2 rules only if( m_grid->GetNumberRows() < 3 ) return; wxArrayInt select = m_grid->GetSelectedRows(); bool reinit = false; for( unsigned irow = 0; irow < select.GetCount(); irow++ ) { int ii = select[irow]; if( ii < 2 ) // The default netclass *must* be the first netclass continue; // so we cannot move up line 0 and 1 // Swap the rule and the previous rule wxString curr_value, previous_value; for( int icol = 0; icol < m_grid->GetNumberCols(); icol++ ) { reinit = true; curr_value = m_grid->GetCellValue( ii, icol ); previous_value = m_grid->GetCellValue( ii - 1, icol ); m_grid->SetCellValue( ii, icol, previous_value ); m_grid->SetCellValue( ii - 1, icol, curr_value ); } curr_value = m_grid->GetRowLabelValue( ii ); previous_value = m_grid->GetRowLabelValue( ii - 1 ); m_grid->SetRowLabelValue( ii, previous_value ); m_grid->SetRowLabelValue( ii - 1, curr_value ); } if( reinit ) InitializeRulesSelectionBoxes(); } void DIALOG_DESIGN_RULES::OnLeftCBSelection( wxCommandEvent& event ) { FillListBoxWithNetNames( m_leftListCtrl, m_leftClassChoice->GetStringSelection() ); if( m_leftClassChoice->GetStringSelection() == m_rightClassChoice->GetStringSelection() ) { m_buttonRightToLeft->Enable( false ); m_buttonLeftToRight->Enable( false ); } else { m_buttonRightToLeft->Enable( true ); m_buttonLeftToRight->Enable( true ); } } void DIALOG_DESIGN_RULES::OnRightCBSelection( wxCommandEvent& event ) { FillListBoxWithNetNames( m_rightListCtrl, m_rightClassChoice->GetStringSelection() ); if( m_leftClassChoice->GetStringSelection() == m_rightClassChoice->GetStringSelection() ) { m_buttonRightToLeft->Enable( false ); m_buttonLeftToRight->Enable( false ); } else { m_buttonRightToLeft->Enable( true ); m_buttonLeftToRight->Enable( true ); } } void DIALOG_DESIGN_RULES::moveSelectedItems( NETS_LIST_CTRL* src, const wxString& newClassName ) { wxListItem item; wxString netName; item.m_mask |= wxLIST_MASK_TEXT; // Validate the member m_text of the wxListItem item for( int row = 0; row < src->GetItemCount(); ++row ) { if( !src->GetItemState( row, wxLIST_STATE_SELECTED ) ) continue; item.SetColumn( 0 ); item.SetId( row ); src->GetItem( item ); netName = item.GetText(); setNetClass( netName, newClassName == wildCard ? NETCLASS::Default : newClassName ); } } void DIALOG_DESIGN_RULES::OnRightToLeftCopyButton( wxCommandEvent& event ) { wxString newClassName = m_leftClassChoice->GetStringSelection(); moveSelectedItems( m_rightListCtrl, newClassName ); FillListBoxWithNetNames( m_leftListCtrl, m_leftClassChoice->GetStringSelection() ); FillListBoxWithNetNames( m_rightListCtrl, m_rightClassChoice->GetStringSelection() ); } void DIALOG_DESIGN_RULES::OnLeftToRightCopyButton( wxCommandEvent& event ) { wxString newClassName = m_rightClassChoice->GetStringSelection(); moveSelectedItems( m_leftListCtrl, newClassName ); FillListBoxWithNetNames( m_leftListCtrl, m_leftClassChoice->GetStringSelection() ); FillListBoxWithNetNames( m_rightListCtrl, m_rightClassChoice->GetStringSelection() ); } void DIALOG_DESIGN_RULES::OnLeftSelectAllButton( wxCommandEvent& event ) { for( int ii = 0; ii < m_leftListCtrl->GetItemCount(); ii++ ) m_leftListCtrl->SetItemState( ii, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED ); } void DIALOG_DESIGN_RULES::OnRightSelectAllButton( wxCommandEvent& event ) { for( int ii = 0; ii < m_rightListCtrl->GetItemCount(); ii++ ) m_rightListCtrl->SetItemState( ii, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED ); } void DIALOG_DESIGN_RULES::setNetClass( const wxString& aNetName, const wxString& aClassName ) { for( NETCUPS::iterator i = m_AllNets.begin(); i != m_AllNets.end(); ++i ) { if( i->net == aNetName ) { i->clazz = aClassName; break; } } } bool DIALOG_DESIGN_RULES::TestDataValidity( wxString* aErrorMsg ) { bool result = true; wxString msg; wxString errorMsg; wxString netclassLabel; // Name of a given netclass wxString netclassError; // Error message particular to a given netclass wxString units = ReturnUnitSymbol( g_UserUnit, "%s" ); int minViaDia = ValueFromTextCtrl( *m_SetViasMinSizeCtrl ); int minViaDrill = ValueFromTextCtrl( *m_SetViasMinDrillCtrl ); int minUViaDia = ValueFromTextCtrl( *m_SetMicroViasMinSizeCtrl ); int minUViaDrill = ValueFromTextCtrl( *m_SetMicroViasMinDrillCtrl ); int minTrackWidth = ValueFromTextCtrl( *m_SetTrackMinWidthCtrl ); int maxval = 1000 * IU_PER_MILS; // a max value for tracks and vias sizes (1 inch) // @todo Change this code to set the focus to the control where the first error occurs // so the user doesn't have to figure out where the issue is. // Test net class parameters. for( int row = 0; row < m_grid->GetNumberRows(); row++ ) { netclassLabel = GetChars( m_grid->GetRowLabelValue( row ) ); netclassError.Clear(); // Clear the error for this netclass int tracksize = ValueFromString( g_UserUnit, m_grid->GetCellValue( row, GRID_TRACKSIZE ) ); if( tracksize < minTrackWidth ) { result = false; msg.Printf( _( " - Track Size (%f %s) < Min Track Size (%f %s)
" ), To_User_Unit( g_UserUnit, tracksize ), units, To_User_Unit( g_UserUnit, minTrackWidth ), units ); netclassError += msg; } int dpsize = ValueFromString( g_UserUnit, m_grid->GetCellValue( row, GRID_DIFF_PAIR_WIDTH ) ); if( dpsize < minTrackWidth ) { result = false; msg.Printf( _( " - Differential Pair Size (%f %s) < Min Track Size (%f %s)
" ), To_User_Unit( g_UserUnit, dpsize ), units, To_User_Unit( g_UserUnit, minTrackWidth ), units ); netclassError += msg; } // Test vias int viadia = ValueFromString( g_UserUnit, m_grid->GetCellValue( row, GRID_VIASIZE ) ); if( viadia < minViaDia ) { result = false; msg.Printf( _( " - Via Diameter (%f %s) < Minimum Via Diameter (%f %s)
" ), To_User_Unit( g_UserUnit, viadia ), units, To_User_Unit( g_UserUnit, minViaDia ), units ); netclassError += msg; } int viadrill = ValueFromString( g_UserUnit, m_grid->GetCellValue( row, GRID_VIADRILL ) ); if( viadrill >= viadia ) { result = false; msg.Printf( _( " - Via Drill (%f %s) ≥ Via Dia (%f %s)
" ), To_User_Unit( g_UserUnit, viadrill ), units, To_User_Unit( g_UserUnit, viadia ), units ); netclassError += msg; } if( viadrill < minViaDrill ) { result = false; msg.Printf( _( " - Via Drill (%f %s) < Min Via Drill (%f %s)
" ), To_User_Unit( g_UserUnit, viadrill ), units, To_User_Unit( g_UserUnit, minViaDrill ), units ); netclassError += msg; } // Test Micro vias int muviadia = ValueFromString( g_UserUnit, m_grid->GetCellValue( row, GRID_uVIASIZE ) ); if( muviadia < minUViaDia ) { result = false; msg.Printf( _( " - MicroVia Diameter (%f %s) < MicroVia Min Diameter (%f %s)
" ), To_User_Unit( g_UserUnit, muviadia ), units, To_User_Unit( g_UserUnit, minUViaDia ), units ); netclassError += msg; } int muviadrill = ValueFromString( g_UserUnit, m_grid->GetCellValue( row, GRID_uVIADRILL ) ); if( muviadrill >= muviadia ) { result = false; msg.Printf( _( " - MicroVia Drill (%f %s) ≥ MicroVia Dia (%f %s)
" ), To_User_Unit( g_UserUnit, muviadrill ), units, To_User_Unit( g_UserUnit, muviadia ), units ); netclassError += msg; } if( muviadrill < minUViaDrill ) { result = false; msg.Printf( _( " - MicroVia Drill (%f %s) < MicroVia Min Drill (%f %s)
" ), To_User_Unit( g_UserUnit, muviadrill ), units, To_User_Unit( g_UserUnit, minUViaDrill ), units ); netclassError += msg; } // If this netclass contains errors, add it to the error message if ( !netclassError.IsEmpty() ) { msg.Printf( _( "Netclass: %s
" ), netclassLabel ); errorMsg += msg; errorMsg += netclassError; errorMsg += "
"; } } // Test custom tracks for( int row = 0; row < m_gridTrackWidthList->GetNumberRows(); ++row ) { wxString tvalue = m_gridTrackWidthList->GetCellValue( row, 0 ); if( tvalue.IsEmpty() ) continue; int tracksize = ValueFromString( g_UserUnit, tvalue ); if( tracksize < minTrackWidth ) { result = false; msg.Printf( _( "Extra Track %d Size %s < Min Track Size
" ), row + 1, GetChars( tvalue ) ); errorMsg += msg; } if( tracksize > maxval ) { result = false; msg.Printf( _( "Extra Track %d Size %s > 1 inch!
" ), row + 1, GetChars( tvalue ) ); errorMsg += msg; } } // Test custom vias for( int row = 0; row < m_gridViaSizeList->GetNumberRows(); ++row ) { wxString tvalue = m_gridViaSizeList->GetCellValue( row, 0 ); if( tvalue.IsEmpty() ) continue; int viadia = ValueFromString( g_UserUnit, tvalue ); if( viadia < minViaDia ) { result = false; msg.Printf( _( "Extra Via %d Size %s < Min Via Size
" ), row + 1, GetChars( tvalue ) ); errorMsg += msg; } wxString drlvalue = m_gridViaSizeList->GetCellValue( row, 1 ); if( drlvalue.IsEmpty() ) { result = false; msg.Printf( _( "No via drill size define in row %d
" ), row + 1 ); errorMsg += msg; continue; } int viadrill = ValueFromString( g_UserUnit, drlvalue ); if( viadrill < minViaDrill ) { result = false; msg.Printf( _( "Extra Via %d Drill %s < Min Via Drill %s
" ), row + 1, GetChars( drlvalue ), GetChars( m_SetViasMinDrillCtrl->GetValue() ) ); errorMsg += msg; } if( viadia <= viadrill ) { result = false; msg.Printf( _( "Extra Via %d Size %s ≤ Drill Size %s
" ), row + 1, GetChars( tvalue ), GetChars( drlvalue ) ); errorMsg += msg; } // Test for a reasonable via size: if( viadia > maxval ) // 1 inch! { result = false; msg.Printf( _( "Extra Via %d Size%s > 1 inch!
" ), row + 1, GetChars( tvalue ) ); errorMsg += msg; } } if( !result && aErrorMsg ) *aErrorMsg = errorMsg; return result; }