50#include <unordered_map>
62 [
this]( wxKeyEvent& aEvent )
65 wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
68 [
this]( wxStyledTextEvent& aEvent )
77 m_typeRegex.Compile(
"^Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
78 m_viaTypeRegex.Compile(
"^Via_Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
79 m_padTypeRegex.Compile(
"^Pad_Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
80 m_pinTypeRegex.Compile(
"^Pin_Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
81 m_fabPropRegex.Compile(
"^Fabrication_Property\\s*[!=]=\\s*$", wxRE_ADVANCED );
82 m_shapeRegex.Compile(
"^Shape\\s*[!=]=\\s*$", wxRE_ADVANCED );
87 m_hJustRegex.Compile(
"^Horizontal_Justification\\s*[!=]=\\s*$", wxRE_ADVANCED );
88 m_vJustRegex.Compile(
"^Vertical_Justification\\s*[!=]=\\s*$", wxRE_ADVANCED );
92 m_textEditor->SetZoom(
Pgm().GetCommonSettings()->m_Appearance.text_editor_zoom );
114 if( aEvent.GetKeyCode() == WXK_ESCAPE && !
m_textEditor->AutoCompActive() )
118 if(
IsOK( wxGetTopLevelParent(
this ),
_(
"Cancel Changes?" ) ) )
136 menu.Append( wxID_UNDO,
_(
"Undo" ) );
137 menu.Append( wxID_REDO,
_(
"Redo" ) );
139 menu.AppendSeparator();
141 menu.Append( 1,
_(
"Cut" ) );
142 menu.Append( 2,
_(
"Copy" ) );
143 menu.Append( 3,
_(
"Paste" ) );
144 menu.Append( 4,
_(
"Delete" ) );
146 menu.AppendSeparator();
148 menu.Append( 5,
_(
"Select All" ) );
150 menu.AppendSeparator();
152 menu.Append( wxID_ZOOM_IN,
_(
"Zoom In" ) );
153 menu.Append( wxID_ZOOM_OUT,
_(
"Zoom Out" ) );
155 menu.AppendSeparator();
157 menu.Append( 6,
_(
"Show Matching Items" ) );
159 switch( GetPopupMenuSelectionFromUser( menu ) )
208 if(
m_frame->Prj().IsNullProject() )
217 const int cursorLine =
m_textEditor->LineFromPosition( cursorPos ) + 1;
218 const int cursorCol = cursorPos -
m_textEditor->PositionFromLine( cursorLine - 1 ) + 1;
220 std::vector<std::shared_ptr<DRC_RULE>> rules;
224 std::function<bool( wxString* )>
resolver = [&]( wxString* token ) ->
bool
226 return m_frame->GetBoard()->ResolveTextVar( token, 0 );
229 wxString rulesText =
m_frame->GetBoard()->ConvertCrossReferencesToKIIDs(
text );
244 wxString targetRuleName;
245 bool foundRule =
false;
249 std::string utf8(
text.mb_str( wxConvUTF8 ) );
250 DRC_RULES_LEXER lex( utf8,
_(
"DRC rules" ) );
260 std::vector<RuleFrame> ruleStack;
265 int tok = lex.NextTok();
272 int leftLine = lex.CurLineNumber();
273 int leftCol = lex.CurOffset();
277 int sub = lex.NextTok();
279 if( sub == DRCRULE_T::T_rule )
282 int nameTok = lex.NextTok();
285 name = wxString::FromUTF8( lex.CurText() );
287 ruleStack.push_back( { depth, leftLine, leftCol,
name } );
304 if( !ruleStack.empty() && ruleStack.back().depth == depth )
306 RuleFrame opened = ruleStack.back();
307 ruleStack.pop_back();
309 int endLine = lex.CurLineNumber();
310 int endCol = lex.CurOffset();
312 bool afterStart = cursorLine > opened.startLine
313 || ( cursorLine == opened.startLine && cursorCol >= opened.startCol );
314 bool beforeEnd = cursorLine < endLine || ( cursorLine == endLine && cursorCol <= endCol );
316 if( afterStart && beforeEnd )
318 targetRuleName = opened.name;
330 m_errorsReport->Report( wxString::Format(
_(
"Could not scan rules text: %s" ), e.
What() ),
338 m_errorsReport->Report(
_(
"Place the cursor inside a (rule ...) block to show matching "
345 if( targetRuleName.IsEmpty() )
347 m_errorsReport->Report(
_(
"The rule under the cursor has no name; add a name to identify "
354 std::shared_ptr<DRC_RULE> targetRule;
356 for(
const auto& rule : rules )
358 if( rule->m_Name == targetRuleName )
367 m_errorsReport->Report( wxString::Format(
_(
"Rule '%s' could not be located after "
375 if( targetRule->m_Condition && !targetRule->m_Condition->GetExpression().IsEmpty()
382 std::shared_ptr<DRC_ENGINE> engine =
m_frame->GetBoard()->GetDesignSettings().m_DRCEngine;
391 std::vector<BOARD_ITEM*> allMatches = engine->GetItemsMatchingRule( targetRule,
m_errorsReport );
393 std::vector<BOARD_ITEM*> matches;
397 switch( item->Type() )
402 default: matches.push_back( item );
break;
406 m_frame->FocusOnItems( matches );
409 wxString::Format(
_(
"Rule '%s': %zu matching item(s)." ), targetRule->m_Name, matches.size() ),
417 if( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKey() ==
' ' )
431 for(
int line =
m_textEditor->LineFromPosition( currentPos ); line > 0; line-- )
434 wxString beginning =
m_textEditor->GetTextRange( lineStart, lineStart + 10 );
436 if( beginning.StartsWith( wxT(
"(rule " ) ) )
438 startPos = lineStart;
453 auto isDisallowToken =
454 [](
const wxString& token ) ->
bool
456 return token == wxT(
"blind_via" )
457 || token == wxT(
"buried_via" )
458 || token == wxT(
"graphic" )
459 || token == wxT(
"hole" )
460 || token == wxT(
"micro_via" )
461 || token == wxT(
"pad" )
462 || token == wxT(
"text" )
463 || token == wxT(
"through_via" )
464 || token == wxT(
"track" )
465 || token == wxT(
"via" )
466 || token == wxT(
"zone" );
469 auto isConstraintTypeToken =
470 [](
const wxString& token ) ->
bool
472 return token == wxT(
"annular_width" )
473 || token == wxT(
"assertion" )
474 || token == wxT(
"clearance" )
475 || token == wxT(
"connection_width" )
476 || token == wxT(
"courtyard_clearance" )
477 || token == wxT(
"diff_pair_gap" )
478 || token == wxT(
"diff_pair_uncoupled" )
479 || token == wxT(
"disallow" )
480 || token == wxT(
"edge_clearance" )
481 || token == wxT(
"length" )
482 || token == wxT(
"hole_clearance" )
483 || token == wxT(
"hole_size" )
484 || token == wxT(
"hole_to_hole" )
485 || token == wxT(
"min_resolved_spokes" )
486 || token == wxT(
"physical_clearance" )
487 || token == wxT(
"physical_hole_clearance" )
488 || token == wxT(
"silk_clearance" )
489 || token == wxT(
"skew" )
490 || token == wxT(
"solder_mask_expansion" )
491 || token == wxT(
"solder_paste_abs_margin" )
492 || token == wxT(
"solder_paste_rel_margin" )
493 || token == wxT(
"text_height" )
494 || token == wxT(
"text_thickness" )
495 || token == wxT(
"thermal_relief_gap" )
496 || token == wxT(
"thermal_spoke_width" )
497 || token == wxT(
"track_width" )
498 || token == wxT(
"track_angle" )
499 || token == wxT(
"track_segment_length" )
500 || token == wxT(
"via_count" )
501 || token == wxT(
"via_diameter" )
502 || token == wxT(
"zone_connection" );
505 std::stack<wxString> sexprs;
508 wxString constraintType;
509 int context = NO_CONTEXT;
510 int expr_context = NO_CONTEXT;
512 for(
int i = startPos; i < currentPos; ++i )
520 else if( context ==
STRING )
524 context = NO_CONTEXT;
528 if( expr_context ==
STRING )
531 expr_context = NO_CONTEXT;
538 partial = wxEmptyString;
543 partial = wxEmptyString;
544 expr_context = STRUCT_REF;
555 partial = wxEmptyString;
560 if( context == SEXPR_OPEN && !partial.IsEmpty() )
563 sexprs.push( partial );
566 partial = wxEmptyString;
567 context = SEXPR_OPEN;
571 while( !sexprs.empty() && ( sexprs.top() == wxT(
"assertion" )
572 || sexprs.top() == wxT(
"disallow" )
573 || isDisallowToken( sexprs.top() )
574 || sexprs.top() == wxT(
"min_resolved_spokes" )
575 || sexprs.top() == wxT(
"zone_connection" ) ) )
580 if( !sexprs.empty() )
583 if( partial == wxT(
"within_diff_pairs" ) )
585 partial = wxEmptyString;
589 if( sexprs.top() == wxT(
"constraint" ) )
591 constraintType = wxEmptyString;
598 context = NO_CONTEXT;
602 if( context == SEXPR_OPEN && !partial.IsEmpty() )
605 sexprs.push( partial );
607 if( partial == wxT(
"constraint" )
608 || partial == wxT(
"layer" )
609 || partial == wxT(
"severity" ) )
611 context = SEXPR_TOKEN;
613 else if( partial == wxT(
"rule" )
614 || partial == wxT(
"condition" ) )
616 context = SEXPR_STRING;
620 context = NO_CONTEXT;
623 partial = wxEmptyString;
626 else if( partial == wxT(
"disallow" )
627 || isDisallowToken( partial )
628 || partial == wxT(
"min_resolved_spokes" )
629 || partial == wxT(
"zone_connection" ) )
632 sexprs.push( partial );
634 partial = wxEmptyString;
635 context = SEXPR_TOKEN;
638 else if( partial == wxT(
"assertion" ) )
641 sexprs.push( partial );
643 partial = wxEmptyString;
644 context = SEXPR_STRING;
647 else if( isConstraintTypeToken( partial ) )
649 constraintType = partial;
652 context = NO_CONTEXT;
662 if( context == SEXPR_OPEN )
666 tokens = wxT(
"rule|"
669 else if( sexprs.top() == wxT(
"rule" ) )
671 tokens = wxT(
"condition|"
676 else if( sexprs.top() == wxT(
"constraint" ) )
678 if( constraintType == wxT(
"skew" ) )
679 tokens = wxT(
"max|min|opt|within_diff_pairs" );
681 tokens = wxT(
"max|min|opt" );
684 else if( context == SEXPR_TOKEN )
690 else if( sexprs.top() == wxT(
"constraint" ) )
692 tokens = wxT(
"annular_width|"
697 "courtyard_clearance|"
700 "diff_pair_uncoupled|"
707 "min_resolved_spokes|"
708 "physical_clearance|"
709 "physical_hole_clearance|"
712 "solder_mask_expansion|"
713 "solder_paste_abs_margin|"
714 "solder_paste_rel_margin|"
717 "thermal_relief_gap|"
718 "thermal_spoke_width|"
721 "track_segment_length|"
726 else if( sexprs.top() == wxT(
"disallow" ) || isDisallowToken( sexprs.top() ) )
728 tokens = wxT(
"blind_via|"
741 else if( sexprs.top() == wxT(
"zone_connection" ) )
743 tokens = wxT(
"none|solid|thermal_reliefs" );
745 else if( sexprs.top() == wxT(
"min_resolved_spokes" ) )
747 tokens = wxT(
"0|1|2|3|4" );
749 else if( sexprs.top() == wxT(
"layer" ) )
751 tokens = wxT(
"inner|outer|\"x\"" );
753 else if( sexprs.top() == wxT(
"severity" ) )
755 tokens = wxT(
"warning|error|ignore|exclusion" );
758 else if( context == SEXPR_STRING && !sexprs.empty()
759 && ( sexprs.top() == wxT(
"condition" ) || sexprs.top() == wxT(
"assertion" ) ) )
763 else if( context ==
STRING && !sexprs.empty()
764 && ( sexprs.top() == wxT(
"condition" ) || sexprs.top() == wxT(
"assertion" ) ) )
766 if( expr_context == STRUCT_REF )
769 std::set<wxString> propNames;
773 const std::vector<PROPERTY_BASE*>& props = propMgr.
GetProperties( cls.type );
780 if( prop->IsHiddenFromRulesEditor() )
783 wxString ref( prop->Name() );
784 ref.Replace( wxT(
" " ), wxT(
"_" ) );
785 propNames.insert( ref );
789 for(
const wxString& propName : propNames )
790 tokens += wxT(
"|" ) + propName;
796 if( !funcSig.Contains(
"DEPRECATED" ) )
797 tokens += wxT(
"|" ) + funcSig;
800 else if( expr_context ==
STRING )
805 std::shared_ptr<NET_SETTINGS>& netSettings = bds.
m_NetSettings;
807 for(
const auto& [
name, netclass] : netSettings->GetNetclasses() )
808 tokens += wxT(
"|" ) +
name;
815 tokens += wxT(
"|" ) + netnameCandidate;
819 tokens = wxT(
"Bitmap|"
835 tokens = wxT(
"Through|"
842 tokens = wxT(
"Through-hole|"
845 "NPTH, mechanical" );
849 tokens = wxT(
"Input|"
864 tokens = wxT(
"None|"
866 "Fiducial, global to board|"
867 "Fiducial, local to footprint|"
874 tokens = wxT(
"Segment|"
883 tokens = wxT(
"Circle|"
888 "Chamfered rectangle|"
893 tokens = wxT(
"Inherited|"
897 "Thermal reliefs for PTH" );
901 tokens = wxT(
"Inherited|"
908 tokens = wxT(
"Default|"
917 tokens = wxT(
"Left|"
930 if( !tokens.IsEmpty() )
941 std::vector<std::shared_ptr<DRC_RULE>> dummyRules;
943 std::function<bool( wxString* )>
resolver =
944 [&]( wxString* token ) ->
bool
946 if(
m_frame->GetBoard()->ResolveTextVar( token, 0 ) )
953 rulesText =
m_frame->GetBoard()->ConvertCrossReferencesToKIIDs( rulesText );
963 m_errorsReport->Report( wxString::Format( wxT(
"%s <a href='%d:%d'>%s</a>%s" ),
983 std::map<std::pair<wxString, wxString>, wxString> seenConditions;
984 std::regex netclassPattern(
"NetClass\\s*[!=]=\\s*'\"?([^\"\\s]+)'\"?" );
986 for(
const auto& rule : aRules )
990 if( rule->m_Condition )
991 condition = rule->m_Condition->GetExpression();
993 condition.Trim(
true ).Trim(
false );
995 auto key = std::make_pair( condition, rule->m_LayerSource );
997 if( seenConditions.count( key ) )
999 m_errorsReport->Report( wxString::Format(
_(
"Rules '%s' and '%s' share the same condition." ),
1001 seenConditions[key] ),
1006 seenConditions[key] = rule->m_Name;
1009 std::string condUtf8 = condition.ToStdString();
1010 std::sregex_iterator it( condUtf8.begin(), condUtf8.end(), netclassPattern );
1011 std::sregex_iterator
end;
1013 for( ; it !=
end; ++it )
1015 wxString ncName = wxString::FromUTF8( ( *it )[1].str() );
1019 m_errorsReport->Report( wxString::Format(
_(
"Rule '%s' references undefined netclass '%s'." ),
1026 const bool isInner = rule->m_LayerSource.IsSameAs( wxT(
"'inner'" ),
false );
1027 const bool isOuter = rule->m_LayerSource.IsSameAs( wxT(
"'outer'" ),
false );
1029 if( !rule->m_LayerSource.IsEmpty() && !isInner && !isOuter )
1031 LSET invalid = rule->m_LayerCondition & ~enabledLayers;
1039 if( !badLayers.IsEmpty() )
1045 m_errorsReport->Report( wxString::Format(
_(
"Rule '%s' references undefined layer(s): %s." ),
1057 wxString link =
event.GetLinkInfo().GetHref();
1058 wxArrayString parts;
1059 long line = 0, offset = 0;
1063 if( parts.size() > 1 )
1065 parts[0].ToLong( &line );
1066 parts[1].ToLong( &offset );
1069 int pos =
m_textEditor->PositionFromLine( line - 1 ) + ( offset - 1 );
1079 wxFileName rulesFile(
m_frame->GetDesignRulesPath() );
1081 if( rulesFile.FileExists() )
1083 wxTextFile file( rulesFile.GetFullPath() );
1087 for ( wxString str = file.GetFirstLine(); !file.Eof(); str = file.GetNextLine() )
1095 wxCommandEvent
dummy;
1106 if(
m_frame->Prj().IsNullProject() )
1109 m_textEditor->AddText(
_(
"Design rules cannot be added without a project" ) );
1122 if(
m_frame->Prj().IsNullProject() )
1125 wxString rulesFilepath =
m_frame->GetDesignRulesPath();
1128 std::string utf8 = std::string( content.mb_str( wxConvUTF8 ) );
1129 wxString writeError;
1133 wxLogError(
_(
"Cannot save design rules to '%s': %s" ), rulesFilepath, writeError );
1139 m_frame->GetBoard()->GetDesignSettings().m_DRCEngine->InitEngine( rulesFilepath );
1158 std::vector<wxString> msg;
1164 msg.emplace_back( t );
1168 msg.emplace_back( t );
1172 msg.emplace_back( t );
1176 msg.emplace_back( t );
1180 msg.emplace_back( t );
1184 msg.emplace_back( t );
1188 msg.emplace_back( t );
1192 msg.emplace_back( t );
1196 msg.emplace_back( t );
1200 msg.emplace_back( t );
1202 wxString msg_txt = wxEmptyString;
1204 for( wxString i : msg )
1205 msg_txt << wxGetTranslation( i );
1208 msg_txt.Replace( wxT(
"Ctrl+" ), wxT(
"Cmd+" ) );
1210 const wxString& msGg_txt = msg_txt;
1215 wxString html_txt = wxEmptyString;
1218 html_txt.Replace( wxS(
"<td" ), wxS(
"<td valign=top" ) );
1227 if( !
m_frame->Prj().IsNullProject() )
1234 if( absFile.FileExists() )
1236 wxTextFile file( absFile.GetFullPath() );
1242 for ( wxString str = file.GetFirstLine(); !file.Eof(); str = file.GetNextLine() )
1250 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.