46#include <unordered_map>
58 [
this]( wxKeyEvent& aEvent )
61 wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
64 [
this]( wxStyledTextEvent& aEvent )
73 m_typeRegex.Compile(
"^Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
74 m_viaTypeRegex.Compile(
"^Via_Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
75 m_padTypeRegex.Compile(
"^Pad_Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
76 m_pinTypeRegex.Compile(
"^Pin_Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
77 m_fabPropRegex.Compile(
"^Fabrication_Property\\s*[!=]=\\s*$", wxRE_ADVANCED );
78 m_shapeRegex.Compile(
"^Shape\\s*[!=]=\\s*$", wxRE_ADVANCED );
83 m_hJustRegex.Compile(
"^Horizontal_Justification\\s*[!=]=\\s*$", wxRE_ADVANCED );
84 m_vJustRegex.Compile(
"^Vertical_Justification\\s*[!=]=\\s*$", wxRE_ADVANCED );
88 m_textEditor->SetZoom(
Pgm().GetCommonSettings()->m_Appearance.text_editor_zoom );
110 if( aEvent.GetKeyCode() == WXK_ESCAPE && !
m_textEditor->AutoCompActive() )
114 if(
IsOK( wxGetTopLevelParent(
this ),
_(
"Cancel Changes?" ) ) )
132 menu.Append( wxID_UNDO,
_(
"Undo" ) );
133 menu.Append( wxID_REDO,
_(
"Redo" ) );
135 menu.AppendSeparator();
137 menu.Append( 1,
_(
"Cut" ) );
138 menu.Append( 2,
_(
"Copy" ) );
139 menu.Append( 3,
_(
"Paste" ) );
140 menu.Append( 4,
_(
"Delete" ) );
142 menu.AppendSeparator();
144 menu.Append( 5,
_(
"Select All" ) );
146 menu.AppendSeparator();
148 menu.Append( wxID_ZOOM_IN,
_(
"Zoom In" ) );
149 menu.Append( wxID_ZOOM_OUT,
_(
"Zoom Out" ) );
151 menu.AppendSeparator();
153 menu.Append( 6,
_(
"Show Matching Items" ) );
155 switch( GetPopupMenuSelectionFromUser( menu ) )
204 if(
m_frame->Prj().IsNullProject() )
213 const int cursorLine =
m_textEditor->LineFromPosition( cursorPos ) + 1;
214 const int cursorCol = cursorPos -
m_textEditor->PositionFromLine( cursorLine - 1 ) + 1;
216 std::vector<std::shared_ptr<DRC_RULE>> rules;
220 std::function<bool( wxString* )>
resolver = [&]( wxString* token ) ->
bool
222 return m_frame->GetBoard()->ResolveTextVar( token, 0 );
225 wxString rulesText =
m_frame->GetBoard()->ConvertCrossReferencesToKIIDs(
text );
240 wxString targetRuleName;
241 bool foundRule =
false;
245 std::string utf8(
text.mb_str( wxConvUTF8 ) );
246 DRC_RULES_LEXER lex( utf8,
_(
"DRC rules" ) );
256 std::vector<RuleFrame> ruleStack;
261 int tok = lex.NextTok();
268 int leftLine = lex.CurLineNumber();
269 int leftCol = lex.CurOffset();
273 int sub = lex.NextTok();
275 if( sub == DRCRULE_T::T_rule )
278 int nameTok = lex.NextTok();
281 name = wxString::FromUTF8( lex.CurText() );
283 ruleStack.push_back( { depth, leftLine, leftCol,
name } );
300 if( !ruleStack.empty() && ruleStack.back().depth == depth )
302 RuleFrame opened = ruleStack.back();
303 ruleStack.pop_back();
305 int endLine = lex.CurLineNumber();
306 int endCol = lex.CurOffset();
308 bool afterStart = cursorLine > opened.startLine
309 || ( cursorLine == opened.startLine && cursorCol >= opened.startCol );
310 bool beforeEnd = cursorLine < endLine || ( cursorLine == endLine && cursorCol <= endCol );
312 if( afterStart && beforeEnd )
314 targetRuleName = opened.name;
326 m_errorsReport->Report( wxString::Format(
_(
"Could not scan rules text: %s" ), e.
What() ),
334 m_errorsReport->Report(
_(
"Place the cursor inside a (rule ...) block to show matching "
341 if( targetRuleName.IsEmpty() )
343 m_errorsReport->Report(
_(
"The rule under the cursor has no name; add a name to identify "
350 std::shared_ptr<DRC_RULE> targetRule;
352 for(
const auto& rule : rules )
354 if( rule->m_Name == targetRuleName )
363 m_errorsReport->Report( wxString::Format(
_(
"Rule '%s' could not be located after "
371 if( targetRule->m_Condition && !targetRule->m_Condition->GetExpression().IsEmpty()
378 std::shared_ptr<DRC_ENGINE> engine =
m_frame->GetBoard()->GetDesignSettings().m_DRCEngine;
387 std::vector<BOARD_ITEM*> allMatches = engine->GetItemsMatchingRule( targetRule,
m_errorsReport );
389 std::vector<BOARD_ITEM*> matches;
393 switch( item->Type() )
398 default: matches.push_back( item );
break;
402 m_frame->FocusOnItems( matches );
405 wxString::Format(
_(
"Rule '%s': %zu matching item(s)." ), targetRule->m_Name, matches.size() ),
413 if( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKey() ==
' ' )
427 for(
int line =
m_textEditor->LineFromPosition( currentPos ); line > 0; line-- )
430 wxString beginning =
m_textEditor->GetTextRange( lineStart, lineStart + 10 );
432 if( beginning.StartsWith( wxT(
"(rule " ) ) )
434 startPos = lineStart;
449 auto isDisallowToken =
450 [](
const wxString& token ) ->
bool
452 return token == wxT(
"blind_via" )
453 || token == wxT(
"buried_via" )
454 || token == wxT(
"graphic" )
455 || token == wxT(
"hole" )
456 || token == wxT(
"micro_via" )
457 || token == wxT(
"pad" )
458 || token == wxT(
"text" )
459 || token == wxT(
"through_via" )
460 || token == wxT(
"track" )
461 || token == wxT(
"via" )
462 || token == wxT(
"zone" );
465 auto isConstraintTypeToken =
466 [](
const wxString& token ) ->
bool
468 return token == wxT(
"annular_width" )
469 || token == wxT(
"assertion" )
470 || token == wxT(
"clearance" )
471 || token == wxT(
"connection_width" )
472 || token == wxT(
"courtyard_clearance" )
473 || token == wxT(
"diff_pair_gap" )
474 || token == wxT(
"diff_pair_uncoupled" )
475 || token == wxT(
"disallow" )
476 || token == wxT(
"edge_clearance" )
477 || token == wxT(
"length" )
478 || token == wxT(
"hole_clearance" )
479 || token == wxT(
"hole_size" )
480 || token == wxT(
"hole_to_hole" )
481 || token == wxT(
"min_resolved_spokes" )
482 || token == wxT(
"physical_clearance" )
483 || token == wxT(
"physical_hole_clearance" )
484 || token == wxT(
"silk_clearance" )
485 || token == wxT(
"skew" )
486 || token == wxT(
"solder_mask_expansion" )
487 || token == wxT(
"solder_paste_abs_margin" )
488 || token == wxT(
"solder_paste_rel_margin" )
489 || token == wxT(
"text_height" )
490 || token == wxT(
"text_thickness" )
491 || token == wxT(
"thermal_relief_gap" )
492 || token == wxT(
"thermal_spoke_width" )
493 || token == wxT(
"track_width" )
494 || token == wxT(
"track_angle" )
495 || token == wxT(
"track_segment_length" )
496 || token == wxT(
"via_count" )
497 || token == wxT(
"via_diameter" )
498 || token == wxT(
"zone_connection" );
501 std::stack<wxString> sexprs;
504 wxString constraintType;
505 int context = NO_CONTEXT;
506 int expr_context = NO_CONTEXT;
508 for(
int i = startPos; i < currentPos; ++i )
516 else if( context ==
STRING )
520 context = NO_CONTEXT;
524 if( expr_context ==
STRING )
527 expr_context = NO_CONTEXT;
534 partial = wxEmptyString;
539 partial = wxEmptyString;
540 expr_context = STRUCT_REF;
551 partial = wxEmptyString;
556 if( context == SEXPR_OPEN && !partial.IsEmpty() )
559 sexprs.push( partial );
562 partial = wxEmptyString;
563 context = SEXPR_OPEN;
567 while( !sexprs.empty() && ( sexprs.top() == wxT(
"assertion" )
568 || sexprs.top() == wxT(
"disallow" )
569 || isDisallowToken( sexprs.top() )
570 || sexprs.top() == wxT(
"min_resolved_spokes" )
571 || sexprs.top() == wxT(
"zone_connection" ) ) )
576 if( !sexprs.empty() )
579 if( partial == wxT(
"within_diff_pairs" ) )
581 partial = wxEmptyString;
585 if( sexprs.top() == wxT(
"constraint" ) )
587 constraintType = wxEmptyString;
594 context = NO_CONTEXT;
598 if( context == SEXPR_OPEN && !partial.IsEmpty() )
601 sexprs.push( partial );
603 if( partial == wxT(
"constraint" )
604 || partial == wxT(
"layer" )
605 || partial == wxT(
"severity" ) )
607 context = SEXPR_TOKEN;
609 else if( partial == wxT(
"rule" )
610 || partial == wxT(
"condition" ) )
612 context = SEXPR_STRING;
616 context = NO_CONTEXT;
619 partial = wxEmptyString;
622 else if( partial == wxT(
"disallow" )
623 || isDisallowToken( partial )
624 || partial == wxT(
"min_resolved_spokes" )
625 || partial == wxT(
"zone_connection" ) )
628 sexprs.push( partial );
630 partial = wxEmptyString;
631 context = SEXPR_TOKEN;
634 else if( partial == wxT(
"assertion" ) )
637 sexprs.push( partial );
639 partial = wxEmptyString;
640 context = SEXPR_STRING;
643 else if( isConstraintTypeToken( partial ) )
645 constraintType = partial;
648 context = NO_CONTEXT;
658 if( context == SEXPR_OPEN )
662 tokens = wxT(
"rule|"
665 else if( sexprs.top() == wxT(
"rule" ) )
667 tokens = wxT(
"condition|"
672 else if( sexprs.top() == wxT(
"constraint" ) )
674 if( constraintType == wxT(
"skew" ) )
675 tokens = wxT(
"max|min|opt|within_diff_pairs" );
677 tokens = wxT(
"max|min|opt" );
680 else if( context == SEXPR_TOKEN )
686 else if( sexprs.top() == wxT(
"constraint" ) )
688 tokens = wxT(
"annular_width|"
693 "courtyard_clearance|"
696 "diff_pair_uncoupled|"
703 "min_resolved_spokes|"
704 "physical_clearance|"
705 "physical_hole_clearance|"
708 "solder_mask_expansion|"
709 "solder_paste_abs_margin|"
710 "solder_paste_rel_margin|"
713 "thermal_relief_gap|"
714 "thermal_spoke_width|"
717 "track_segment_length|"
722 else if( sexprs.top() == wxT(
"disallow" ) || isDisallowToken( sexprs.top() ) )
724 tokens = wxT(
"blind_via|"
737 else if( sexprs.top() == wxT(
"zone_connection" ) )
739 tokens = wxT(
"none|solid|thermal_reliefs" );
741 else if( sexprs.top() == wxT(
"min_resolved_spokes" ) )
743 tokens = wxT(
"0|1|2|3|4" );
745 else if( sexprs.top() == wxT(
"layer" ) )
747 tokens = wxT(
"inner|outer|\"x\"" );
749 else if( sexprs.top() == wxT(
"severity" ) )
751 tokens = wxT(
"warning|error|ignore|exclusion" );
754 else if( context == SEXPR_STRING && !sexprs.empty()
755 && ( sexprs.top() == wxT(
"condition" ) || sexprs.top() == wxT(
"assertion" ) ) )
759 else if( context ==
STRING && !sexprs.empty()
760 && ( sexprs.top() == wxT(
"condition" ) || sexprs.top() == wxT(
"assertion" ) ) )
762 if( expr_context == STRUCT_REF )
765 std::set<wxString> propNames;
769 const std::vector<PROPERTY_BASE*>& props = propMgr.
GetProperties( cls.type );
776 if( prop->IsHiddenFromRulesEditor() )
779 wxString ref( prop->Name() );
780 ref.Replace( wxT(
" " ), wxT(
"_" ) );
781 propNames.insert( ref );
785 for(
const wxString& propName : propNames )
786 tokens += wxT(
"|" ) + propName;
792 if( !funcSig.Contains(
"DEPRECATED" ) )
793 tokens += wxT(
"|" ) + funcSig;
796 else if( expr_context ==
STRING )
801 std::shared_ptr<NET_SETTINGS>& netSettings = bds.
m_NetSettings;
803 for(
const auto& [
name, netclass] : netSettings->GetNetclasses() )
804 tokens += wxT(
"|" ) +
name;
811 tokens += wxT(
"|" ) + netnameCandidate;
815 tokens = wxT(
"Bitmap|"
831 tokens = wxT(
"Through|"
838 tokens = wxT(
"Through-hole|"
841 "NPTH, mechanical" );
845 tokens = wxT(
"Input|"
860 tokens = wxT(
"None|"
862 "Fiducial, global to board|"
863 "Fiducial, local to footprint|"
870 tokens = wxT(
"Segment|"
879 tokens = wxT(
"Circle|"
884 "Chamfered rectangle|"
889 tokens = wxT(
"Inherited|"
893 "Thermal reliefs for PTH" );
897 tokens = wxT(
"Inherited|"
904 tokens = wxT(
"Default|"
913 tokens = wxT(
"Left|"
926 if( !tokens.IsEmpty() )
937 std::vector<std::shared_ptr<DRC_RULE>> dummyRules;
939 std::function<bool( wxString* )>
resolver =
940 [&]( wxString* token ) ->
bool
942 if(
m_frame->GetBoard()->ResolveTextVar( token, 0 ) )
949 rulesText =
m_frame->GetBoard()->ConvertCrossReferencesToKIIDs( rulesText );
959 m_errorsReport->Report( wxString::Format( wxT(
"%s <a href='%d:%d'>%s</a>%s" ),
979 std::map<std::pair<wxString, wxString>, wxString> seenConditions;
980 std::regex netclassPattern(
"NetClass\\s*[!=]=\\s*'\"?([^\"\\s]+)'\"?" );
982 for(
const auto& rule : aRules )
986 if( rule->m_Condition )
987 condition = rule->m_Condition->GetExpression();
989 condition.Trim(
true ).Trim(
false );
991 auto key = std::make_pair( condition, rule->m_LayerSource );
993 if( seenConditions.count( key ) )
995 m_errorsReport->Report( wxString::Format(
_(
"Rules '%s' and '%s' share the same condition." ),
997 seenConditions[key] ),
1002 seenConditions[key] = rule->m_Name;
1005 std::string condUtf8 = condition.ToStdString();
1006 std::sregex_iterator it( condUtf8.begin(), condUtf8.end(), netclassPattern );
1007 std::sregex_iterator
end;
1009 for( ; it !=
end; ++it )
1011 wxString ncName = wxString::FromUTF8( ( *it )[1].str() );
1015 m_errorsReport->Report( wxString::Format(
_(
"Rule '%s' references undefined netclass '%s'." ),
1022 const bool isInner = rule->m_LayerSource.IsSameAs( wxT(
"'inner'" ),
false );
1023 const bool isOuter = rule->m_LayerSource.IsSameAs( wxT(
"'outer'" ),
false );
1025 if( !rule->m_LayerSource.IsEmpty() && !isInner && !isOuter )
1027 LSET invalid = rule->m_LayerCondition & ~enabledLayers;
1035 if( !badLayers.IsEmpty() )
1041 m_errorsReport->Report( wxString::Format(
_(
"Rule '%s' references undefined layer(s): %s." ),
1053 wxString link =
event.GetLinkInfo().GetHref();
1054 wxArrayString parts;
1055 long line = 0, offset = 0;
1059 if( parts.size() > 1 )
1061 parts[0].ToLong( &line );
1062 parts[1].ToLong( &offset );
1065 int pos =
m_textEditor->PositionFromLine( line - 1 ) + ( offset - 1 );
1078 wxFileName rulesFile(
m_frame->GetBoard()->GetDesignRulesPath() );
1080 if( rulesFile.FileExists() )
1082 wxTextFile file( rulesFile.GetFullPath() );
1086 for ( wxString str = file.GetFirstLine(); !file.Eof(); str = file.GetNextLine() )
1094 wxCommandEvent
dummy;
1105 if(
m_frame->Prj().IsNullProject() )
1108 m_textEditor->AddText(
_(
"Design rules cannot be added without a project" ) );
1121 if(
m_frame->Prj().IsNullProject() )
1127 wxString rulesFilepath =
m_frame->GetBoard()->GetDesignRulesPath();
1130 std::string utf8 = std::string( content.mb_str( wxConvUTF8 ) );
1131 wxString writeError;
1135 wxLogError(
_(
"Cannot save design rules to '%s': %s" ), rulesFilepath, writeError );
1141 m_frame->GetBoard()->GetDesignSettings().m_DRCEngine->InitEngine( rulesFilepath );
1160 std::vector<wxString> msg;
1166 msg.emplace_back( t );
1170 msg.emplace_back( t );
1174 msg.emplace_back( t );
1178 msg.emplace_back( t );
1182 msg.emplace_back( t );
1186 msg.emplace_back( t );
1190 msg.emplace_back( t );
1194 msg.emplace_back( t );
1198 msg.emplace_back( t );
1202 msg.emplace_back( t );
1204 wxString msg_txt = wxEmptyString;
1206 for( wxString i : msg )
1207 msg_txt << wxGetTranslation( i );
1210 msg_txt.Replace( wxT(
"Ctrl+" ), wxT(
"Cmd+" ) );
1212 const wxString& msGg_txt = msg_txt;
1217 wxString html_txt = wxEmptyString;
1220 html_txt.Replace( wxS(
"<td" ), wxS(
"<td valign=top" ) );
1229 if( !
m_frame->Prj().IsNullProject() )
1236 if( absFile.FileExists() )
1238 wxTextFile file( absFile.GetFullPath() );
1244 for ( wxString str = file.GetFirstLine(); !file.Eof(); str = file.GetNextLine() )
1252 wxCommandEvent
dummy;
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Container for design settings for a BOARD object.
std::shared_ptr< NET_SETTINGS > m_NetSettings
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Information pertinent to a Pcbnew printed circuit board.
std::set< wxString > GetNetClassAssignmentCandidates() const
Return the set of netname candidates for netclass assignment.
const wxString & GetFileName() const
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
PROJECT * GetProject() const
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
void Parse(std::vector< std::shared_ptr< DRC_RULE > > &aRules, REPORTER *aReporter)
static bool IsSymbol(int aTok)
Test a token to see if it is a symbol.
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
LSET is a set of PCB_LAYER_IDs.
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
bool HasNetclass(const wxString &netclassName) const
Determines if the given netclass exists.
static PAGED_DIALOG * GetDialog(wxWindow *aWindow)
STD_BITMAP_BUTTON * m_compileButton
wxStyledTextCtrl * m_textEditor
PANEL_SETUP_RULES_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
WX_HTML_REPORT_BOX * m_errorsReport
bool TransferDataToWindow() override
void OnErrorLinkClicked(wxHtmlLinkEvent &event) override
void checkPlausibility(const std::vector< std::shared_ptr< DRC_RULE > > &aRules)
void onScintillaCharAdded(wxStyledTextEvent &aEvent)
void ImportSettingsFrom(BOARD *aBoard)
void OnContextMenu(wxMouseEvent &event) override
~PANEL_SETUP_RULES() override
HTML_MESSAGE_BOX * m_helpWindow
void OnCompile(wxCommandEvent &event) override
wxRegEx m_padConnectionsRegex
wxRegEx m_zoneConnStyleRegex
bool TransferDataFromWindow() override
void OnSyntaxHelp(wxHyperlinkEvent &aEvent) override
PANEL_SETUP_RULES(wxWindow *aParentWindow, PCB_EDIT_FRAME *aFrame)
void onCharHook(wxKeyEvent &aEvent)
SCINTILLA_TRICKS * m_scintillaTricks
const wxArrayString GetSignatures() const
static PCBEXPR_BUILTIN_FUNCTIONS & Instance()
The main frame for Pcbnew.
virtual COMMON_SETTINGS * GetCommonSettings() const
virtual const wxString AbsolutePath(const wxString &aFileName) const
Fix up aFileName if it is relative to the project's directory to be an absolute path and filename.
Provide class metadata.Helper macro to map type hashes to names.
const std::vector< PROPERTY_BASE * > & GetProperties(TYPE_ID aType) const
Return all properties for a specific type.
CLASSES_INFO GetAllClasses()
static PROPERTY_MANAGER & Instance()
Add cut/copy/paste, dark theme, autocomplete and brace highlighting to a wxStyleTextCtrl instance.
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
This file is part of the common library.
static FILENAME_RESOLVER * resolver
static const std::string DesignRulesFileExtension
PCB_LAYER_ID
A quick note on layer IDs:
PGM_BASE & Pgm()
The global program "get" accessor.
std::vector< FAB_LAYER_COLOR > dummy
bool ConvertSmartQuotesAndDashes(wxString *aString)
Convert curly quotes and em/en dashes to straight quotes and dashes.
void wxStringSplit(const wxString &aText, wxArrayString &aStrings, wxChar aSplitter)
Split aString to a string list separated at aSplitter.
void ConvertMarkdown2Html(const wxString &aMarkdownInput, wxString &aHtmlOutput)
A filename or source description, a problem input line, a line number, a byte offset,...
int lineNumber
at which line number, 1 based index.
const wxString ParseProblem()
int byteIndex
at which byte offset within the line, 1 based index
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
@ PCB_NETINFO_T
class NETINFO_ITEM, a description of a net
Definition of file extensions used in Kicad.