47#include <unordered_map>
59 [
this]( wxKeyEvent& aEvent )
62 wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
65 [
this]( wxStyledTextEvent& aEvent )
74 m_typeRegex.Compile(
"^Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
75 m_viaTypeRegex.Compile(
"^Via_Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
76 m_padTypeRegex.Compile(
"^Pad_Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
77 m_pinTypeRegex.Compile(
"^Pin_Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
78 m_fabPropRegex.Compile(
"^Fabrication_Property\\s*[!=]=\\s*$", wxRE_ADVANCED );
79 m_shapeRegex.Compile(
"^Shape\\s*[!=]=\\s*$", wxRE_ADVANCED );
84 m_hJustRegex.Compile(
"^Horizontal_Justification\\s*[!=]=\\s*$", wxRE_ADVANCED );
85 m_vJustRegex.Compile(
"^Vertical_Justification\\s*[!=]=\\s*$", wxRE_ADVANCED );
89 m_textEditor->SetZoom(
Pgm().GetCommonSettings()->m_Appearance.text_editor_zoom );
111 if( aEvent.GetKeyCode() == WXK_ESCAPE && !
m_textEditor->AutoCompActive() )
115 if(
IsOK( wxGetTopLevelParent(
this ),
_(
"Cancel Changes?" ) ) )
133 menu.Append( wxID_UNDO,
_(
"Undo" ) );
134 menu.Append( wxID_REDO,
_(
"Redo" ) );
136 menu.AppendSeparator();
138 menu.Append( 1,
_(
"Cut" ) );
139 menu.Append( 2,
_(
"Copy" ) );
140 menu.Append( 3,
_(
"Paste" ) );
141 menu.Append( 4,
_(
"Delete" ) );
143 menu.AppendSeparator();
145 menu.Append( 5,
_(
"Select All" ) );
147 menu.AppendSeparator();
149 menu.Append( wxID_ZOOM_IN,
_(
"Zoom In" ) );
150 menu.Append( wxID_ZOOM_OUT,
_(
"Zoom Out" ) );
152 menu.AppendSeparator();
154 menu.Append( 6,
_(
"Show Matching Items" ) );
156 switch( GetPopupMenuSelectionFromUser( menu ) )
205 if(
m_frame->Prj().IsNullProject() )
214 const int cursorLine =
m_textEditor->LineFromPosition( cursorPos ) + 1;
215 const int cursorCol = cursorPos -
m_textEditor->PositionFromLine( cursorLine - 1 ) + 1;
217 std::vector<std::shared_ptr<DRC_RULE>> rules;
221 std::function<bool( wxString* )> resolver =
222 [&]( wxString* token ) ->
bool
227 return m_frame->GetBoard()->ResolveTextVar( token, 0 );
230 wxString rulesText =
m_frame->GetBoard()->ConvertCrossReferencesToKIIDs(
text );
245 wxString targetRuleName;
246 bool foundRule =
false;
250 std::string utf8(
text.mb_str( wxConvUTF8 ) );
251 DRC_RULES_LEXER lex( utf8,
_(
"DRC rules" ) );
261 std::vector<RuleFrame> ruleStack;
266 int tok = lex.NextTok();
273 int leftLine = lex.CurLineNumber();
274 int leftCol = lex.CurOffset();
278 int sub = lex.NextTok();
280 if( sub == DRCRULE_T::T_rule )
283 int nameTok = lex.NextTok();
286 name = wxString::FromUTF8( lex.CurText() );
288 ruleStack.push_back( { depth, leftLine, leftCol,
name } );
305 if( !ruleStack.empty() && ruleStack.back().depth == depth )
307 RuleFrame opened = ruleStack.back();
308 ruleStack.pop_back();
310 int endLine = lex.CurLineNumber();
311 int endCol = lex.CurOffset();
313 bool afterStart = cursorLine > opened.startLine
314 || ( cursorLine == opened.startLine && cursorCol >= opened.startCol );
315 bool beforeEnd = cursorLine < endLine || ( cursorLine == endLine && cursorCol <= endCol );
317 if( afterStart && beforeEnd )
319 targetRuleName = opened.name;
331 m_errorsReport->Report( wxString::Format(
_(
"Could not scan rules text: %s" ), e.
What() ),
339 m_errorsReport->Report(
_(
"Place the cursor inside a (rule ...) block to show matching "
346 if( targetRuleName.IsEmpty() )
348 m_errorsReport->Report(
_(
"The rule under the cursor has no name; add a name to identify "
355 std::shared_ptr<DRC_RULE> targetRule;
357 for(
const auto& rule : rules )
359 if( rule->m_Name == targetRuleName )
368 m_errorsReport->Report( wxString::Format(
_(
"Rule '%s' could not be located after "
376 if( targetRule->m_Condition && !targetRule->m_Condition->GetExpression().IsEmpty()
383 std::shared_ptr<DRC_ENGINE> engine =
m_frame->GetBoard()->GetDesignSettings().m_DRCEngine;
392 std::vector<BOARD_ITEM*> allMatches = engine->GetItemsMatchingRule( targetRule,
m_errorsReport );
394 std::vector<BOARD_ITEM*> matches;
398 switch( item->Type() )
403 default: matches.push_back( item );
break;
407 m_frame->FocusOnItems( matches );
410 wxString::Format(
_(
"Rule '%s': %zu matching item(s)." ), targetRule->m_Name, matches.size() ),
418 if( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKey() ==
' ' )
432 for(
int line =
m_textEditor->LineFromPosition( currentPos ); line > 0; line-- )
435 wxString beginning =
m_textEditor->GetTextRange( lineStart, lineStart + 10 );
437 if( beginning.StartsWith( wxT(
"(rule " ) ) )
439 startPos = lineStart;
454 auto isDisallowToken =
455 [](
const wxString& token ) ->
bool
457 return token == wxT(
"blind_via" )
458 || token == wxT(
"buried_via" )
459 || token == wxT(
"graphic" )
460 || token == wxT(
"hole" )
461 || token == wxT(
"micro_via" )
462 || token == wxT(
"pad" )
463 || token == wxT(
"text" )
464 || token == wxT(
"through_via" )
465 || token == wxT(
"track" )
466 || token == wxT(
"via" )
467 || token == wxT(
"zone" );
470 auto isConstraintTypeToken =
471 [](
const wxString& token ) ->
bool
473 return token == wxT(
"annular_width" )
474 || token == wxT(
"assertion" )
475 || token == wxT(
"clearance" )
476 || token == wxT(
"connection_width" )
477 || token == wxT(
"courtyard_clearance" )
478 || token == wxT(
"diff_pair_gap" )
479 || token == wxT(
"diff_pair_uncoupled" )
480 || token == wxT(
"disallow" )
481 || token == wxT(
"edge_clearance" )
482 || token == wxT(
"length" )
483 || token == wxT(
"hole_clearance" )
484 || token == wxT(
"hole_size" )
485 || token == wxT(
"hole_to_hole" )
486 || token == wxT(
"min_resolved_spokes" )
487 || token == wxT(
"physical_clearance" )
488 || token == wxT(
"physical_hole_clearance" )
489 || token == wxT(
"silk_clearance" )
490 || token == wxT(
"skew" )
491 || token == wxT(
"solder_mask_expansion" )
492 || token == wxT(
"solder_paste_abs_margin" )
493 || token == wxT(
"solder_paste_rel_margin" )
494 || token == wxT(
"text_height" )
495 || token == wxT(
"text_thickness" )
496 || token == wxT(
"thermal_relief_gap" )
497 || token == wxT(
"thermal_spoke_width" )
498 || token == wxT(
"track_width" )
499 || token == wxT(
"track_angle" )
500 || token == wxT(
"track_segment_length" )
501 || token == wxT(
"via_count" )
502 || token == wxT(
"via_diameter" )
503 || token == wxT(
"zone_connection" );
506 std::stack<wxString> sexprs;
509 wxString constraintType;
510 int context = NO_CONTEXT;
511 int expr_context = NO_CONTEXT;
513 for(
int i = startPos; i < currentPos; ++i )
521 else if( context ==
STRING )
525 context = NO_CONTEXT;
529 if( expr_context ==
STRING )
532 expr_context = NO_CONTEXT;
539 partial = wxEmptyString;
544 partial = wxEmptyString;
545 expr_context = STRUCT_REF;
556 partial = wxEmptyString;
561 if( context == SEXPR_OPEN && !partial.IsEmpty() )
564 sexprs.push( partial );
567 partial = wxEmptyString;
568 context = SEXPR_OPEN;
572 while( !sexprs.empty() && ( sexprs.top() == wxT(
"assertion" )
573 || sexprs.top() == wxT(
"disallow" )
574 || isDisallowToken( sexprs.top() )
575 || sexprs.top() == wxT(
"min_resolved_spokes" )
576 || sexprs.top() == wxT(
"zone_connection" ) ) )
581 if( !sexprs.empty() )
584 if( partial == wxT(
"within_diff_pairs" ) )
586 partial = wxEmptyString;
590 if( sexprs.top() == wxT(
"constraint" ) )
592 constraintType = wxEmptyString;
599 context = NO_CONTEXT;
603 if( context == SEXPR_OPEN && !partial.IsEmpty() )
606 sexprs.push( partial );
608 if( partial == wxT(
"constraint" )
609 || partial == wxT(
"layer" )
610 || partial == wxT(
"severity" ) )
612 context = SEXPR_TOKEN;
614 else if( partial == wxT(
"rule" )
615 || partial == wxT(
"condition" ) )
617 context = SEXPR_STRING;
621 context = NO_CONTEXT;
624 partial = wxEmptyString;
627 else if( partial == wxT(
"disallow" )
628 || isDisallowToken( partial )
629 || partial == wxT(
"min_resolved_spokes" )
630 || partial == wxT(
"zone_connection" ) )
633 sexprs.push( partial );
635 partial = wxEmptyString;
636 context = SEXPR_TOKEN;
639 else if( partial == wxT(
"assertion" ) )
642 sexprs.push( partial );
644 partial = wxEmptyString;
645 context = SEXPR_STRING;
648 else if( isConstraintTypeToken( partial ) )
650 constraintType = partial;
653 context = NO_CONTEXT;
663 if( context == SEXPR_OPEN )
667 tokens = wxT(
"rule|"
670 else if( sexprs.top() == wxT(
"rule" ) )
672 tokens = wxT(
"condition|"
677 else if( sexprs.top() == wxT(
"constraint" ) )
679 if( constraintType == wxT(
"skew" ) )
680 tokens = wxT(
"max|min|opt|within_diff_pairs" );
682 tokens = wxT(
"max|min|opt" );
685 else if( context == SEXPR_TOKEN )
691 else if( sexprs.top() == wxT(
"constraint" ) )
693 tokens = wxT(
"annular_width|"
698 "courtyard_clearance|"
701 "diff_pair_uncoupled|"
708 "min_resolved_spokes|"
709 "physical_clearance|"
710 "physical_hole_clearance|"
713 "solder_mask_expansion|"
714 "solder_paste_abs_margin|"
715 "solder_paste_rel_margin|"
718 "thermal_relief_gap|"
719 "thermal_spoke_width|"
722 "track_segment_length|"
727 else if( sexprs.top() == wxT(
"disallow" ) || isDisallowToken( sexprs.top() ) )
729 tokens = wxT(
"blind_via|"
742 else if( sexprs.top() == wxT(
"zone_connection" ) )
744 tokens = wxT(
"none|solid|thermal_reliefs" );
746 else if( sexprs.top() == wxT(
"min_resolved_spokes" ) )
748 tokens = wxT(
"0|1|2|3|4" );
750 else if( sexprs.top() == wxT(
"layer" ) )
752 tokens = wxT(
"inner|outer|\"x\"" );
754 else if( sexprs.top() == wxT(
"severity" ) )
756 tokens = wxT(
"warning|error|ignore|exclusion" );
759 else if( context == SEXPR_STRING && !sexprs.empty()
760 && ( sexprs.top() == wxT(
"condition" ) || sexprs.top() == wxT(
"assertion" ) ) )
764 else if( context ==
STRING && !sexprs.empty()
765 && ( sexprs.top() == wxT(
"condition" ) || sexprs.top() == wxT(
"assertion" ) ) )
767 if( expr_context == STRUCT_REF )
770 std::set<wxString> propNames;
774 const std::vector<PROPERTY_BASE*>& props = propMgr.
GetProperties( cls.type );
781 if( prop->IsHiddenFromRulesEditor() )
784 wxString ref( prop->Name() );
785 ref.Replace( wxT(
" " ), wxT(
"_" ) );
786 propNames.insert( ref );
790 for(
const wxString& propName : propNames )
791 tokens += wxT(
"|" ) + propName;
797 if( !funcSig.Contains(
"DEPRECATED" ) )
798 tokens += wxT(
"|" ) + funcSig;
801 else if( expr_context ==
STRING )
806 std::shared_ptr<NET_SETTINGS>& netSettings = bds.
m_NetSettings;
808 for(
const auto& [
name, netclass] : netSettings->GetNetclasses() )
809 tokens += wxT(
"|" ) +
name;
816 tokens += wxT(
"|" ) + netnameCandidate;
820 tokens = wxT(
"Bitmap|"
836 tokens = wxT(
"Through|"
843 tokens = wxT(
"Through-hole|"
846 "NPTH, mechanical" );
850 tokens = wxT(
"Input|"
865 tokens = wxT(
"None|"
867 "Fiducial, global to board|"
868 "Fiducial, local to footprint|"
875 tokens = wxT(
"Segment|"
884 tokens = wxT(
"Circle|"
889 "Chamfered rectangle|"
894 tokens = wxT(
"Inherited|"
898 "Thermal reliefs for PTH" );
902 tokens = wxT(
"Inherited|"
909 tokens = wxT(
"Default|"
918 tokens = wxT(
"Left|"
931 if( !tokens.IsEmpty() )
942 std::vector<std::shared_ptr<DRC_RULE>> dummyRules;
944 std::function<bool( wxString* )> resolver =
945 [&]( wxString* token ) ->
bool
950 return m_frame->GetBoard()->ResolveTextVar( token, 0 );
954 rulesText =
m_frame->GetBoard()->ConvertCrossReferencesToKIIDs( rulesText );
964 m_errorsReport->Report( wxString::Format( wxT(
"%s <a href='%d:%d'>%s</a>%s" ),
984 std::map<std::pair<wxString, wxString>, wxString> seenConditions;
985 std::regex netclassPattern(
"NetClass\\s*[!=]=\\s*'\"?([^\"\\s]+)'\"?" );
987 for(
const auto& rule : aRules )
991 if( rule->m_Condition )
992 condition = rule->m_Condition->GetExpression();
994 condition.Trim(
true ).Trim(
false );
996 auto key = std::make_pair( condition, rule->m_LayerSource );
998 if( seenConditions.count( key ) )
1000 m_errorsReport->Report( wxString::Format(
_(
"Rules '%s' and '%s' share the same condition." ),
1002 seenConditions[key] ),
1007 seenConditions[key] = rule->m_Name;
1010 std::string condUtf8 = condition.ToStdString();
1011 std::sregex_iterator it( condUtf8.begin(), condUtf8.end(), netclassPattern );
1012 std::sregex_iterator
end;
1014 for( ; it !=
end; ++it )
1016 wxString ncName = wxString::FromUTF8( ( *it )[1].str() );
1020 m_errorsReport->Report( wxString::Format(
_(
"Rule '%s' references undefined netclass '%s'." ),
1027 const bool isInner = rule->m_LayerSource.IsSameAs( wxT(
"'inner'" ),
false );
1028 const bool isOuter = rule->m_LayerSource.IsSameAs( wxT(
"'outer'" ),
false );
1030 if( !rule->m_LayerSource.IsEmpty() && !isInner && !isOuter )
1032 LSET invalid = rule->m_LayerCondition & ~enabledLayers;
1040 if( !badLayers.IsEmpty() )
1046 m_errorsReport->Report( wxString::Format(
_(
"Rule '%s' references undefined layer(s): %s." ),
1058 wxString link =
event.GetLinkInfo().GetHref();
1059 wxArrayString parts;
1060 long line = 0, offset = 0;
1064 if( parts.size() > 1 )
1066 parts[0].ToLong( &line );
1067 parts[1].ToLong( &offset );
1070 int pos =
m_textEditor->PositionFromLine( line - 1 ) + ( offset - 1 );
1083 wxFileName rulesFile(
m_frame->GetBoard()->GetDesignRulesPath() );
1085 if( rulesFile.FileExists() )
1087 wxTextFile file( rulesFile.GetFullPath() );
1091 for ( wxString str = file.GetFirstLine(); !file.Eof(); str = file.GetNextLine() )
1099 wxCommandEvent
dummy;
1110 if(
m_frame->Prj().IsNullProject() )
1113 m_textEditor->AddText(
_(
"Design rules cannot be added without a project" ) );
1126 if(
m_frame->Prj().IsNullProject() )
1132 wxString rulesFilepath =
m_frame->GetBoard()->GetDesignRulesPath();
1135 std::string utf8 = std::string( content.mb_str( wxConvUTF8 ) );
1136 wxString writeError;
1140 wxLogError(
_(
"Cannot save design rules to '%s': %s" ), rulesFilepath, writeError );
1146 m_frame->GetBoard()->GetDesignSettings().m_DRCEngine->InitEngine( rulesFilepath );
1165 std::vector<wxString> msg;
1171 msg.emplace_back( t );
1175 msg.emplace_back( t );
1179 msg.emplace_back( t );
1183 msg.emplace_back( t );
1187 msg.emplace_back( t );
1191 msg.emplace_back( t );
1195 msg.emplace_back( t );
1199 msg.emplace_back( t );
1203 msg.emplace_back( t );
1207 msg.emplace_back( t );
1209 wxString msg_txt = wxEmptyString;
1211 for( wxString i : msg )
1212 msg_txt << wxGetTranslation( i );
1215 msg_txt.Replace( wxT(
"Ctrl+" ), wxT(
"Cmd+" ) );
1217 const wxString& msGg_txt = msg_txt;
1222 wxString html_txt = wxEmptyString;
1225 html_txt.Replace( wxS(
"<td" ), wxS(
"<td valign=top" ) );
1234 if( !
m_frame->Prj().IsNullProject() )
1241 if( absFile.FileExists() )
1243 wxTextFile file( absFile.GetFullPath() );
1249 for ( wxString str = file.GetFirstLine(); !file.Eof(); str = file.GetNextLine() )
1257 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.
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, RESOLUTION_CONTEXT aContext)
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.
bool IsComponentClassSelector(const wxString &aToken)
${Class:X} inside a DRC rule is a component-class selector consumed by testFootprintSelector(),...
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.