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" ) );
156 switch( GetPopupMenuSelectionFromUser( menu ) )
201 if( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKey() ==
' ' )
215 for(
int line =
m_textEditor->LineFromPosition( currentPos ); line > 0; line-- )
218 wxString beginning =
m_textEditor->GetTextRange( lineStart, lineStart + 10 );
220 if( beginning.StartsWith( wxT(
"(rule " ) ) )
222 startPos = lineStart;
237 auto isDisallowToken =
238 [](
const wxString& token ) ->
bool
240 return token == wxT(
"blind_via" )
241 || token == wxT(
"buried_via" )
242 || token == wxT(
"graphic" )
243 || token == wxT(
"hole" )
244 || token == wxT(
"micro_via" )
245 || token == wxT(
"pad" )
246 || token == wxT(
"text" )
247 || token == wxT(
"through_via" )
248 || token == wxT(
"track" )
249 || token == wxT(
"via" )
250 || token == wxT(
"zone" );
253 auto isConstraintTypeToken =
254 [](
const wxString& token ) ->
bool
256 return token == wxT(
"annular_width" )
257 || token == wxT(
"assertion" )
258 || token == wxT(
"clearance" )
259 || token == wxT(
"connection_width" )
260 || token == wxT(
"courtyard_clearance" )
261 || token == wxT(
"diff_pair_gap" )
262 || token == wxT(
"diff_pair_uncoupled" )
263 || token == wxT(
"disallow" )
264 || token == wxT(
"edge_clearance" )
265 || token == wxT(
"length" )
266 || token == wxT(
"hole_clearance" )
267 || token == wxT(
"hole_size" )
268 || token == wxT(
"hole_to_hole" )
269 || token == wxT(
"min_resolved_spokes" )
270 || token == wxT(
"physical_clearance" )
271 || token == wxT(
"physical_hole_clearance" )
272 || token == wxT(
"silk_clearance" )
273 || token == wxT(
"skew" )
274 || token == wxT(
"solder_mask_expansion" )
275 || token == wxT(
"solder_paste_abs_margin" )
276 || token == wxT(
"solder_paste_rel_margin" )
277 || token == wxT(
"text_height" )
278 || token == wxT(
"text_thickness" )
279 || token == wxT(
"thermal_relief_gap" )
280 || token == wxT(
"thermal_spoke_width" )
281 || token == wxT(
"track_width" )
282 || token == wxT(
"track_angle" )
283 || token == wxT(
"track_segment_length" )
284 || token == wxT(
"via_count" )
285 || token == wxT(
"via_diameter" )
286 || token == wxT(
"zone_connection" );
289 std::stack<wxString> sexprs;
292 wxString constraintType;
293 int context = NO_CONTEXT;
294 int expr_context = NO_CONTEXT;
296 for(
int i = startPos; i < currentPos; ++i )
304 else if( context ==
STRING )
308 context = NO_CONTEXT;
312 if( expr_context ==
STRING )
315 expr_context = NO_CONTEXT;
322 partial = wxEmptyString;
327 partial = wxEmptyString;
328 expr_context = STRUCT_REF;
339 partial = wxEmptyString;
344 if( context == SEXPR_OPEN && !partial.IsEmpty() )
347 sexprs.push( partial );
350 partial = wxEmptyString;
351 context = SEXPR_OPEN;
355 while( !sexprs.empty() && ( sexprs.top() == wxT(
"assertion" )
356 || sexprs.top() == wxT(
"disallow" )
357 || isDisallowToken( sexprs.top() )
358 || sexprs.top() == wxT(
"min_resolved_spokes" )
359 || sexprs.top() == wxT(
"zone_connection" ) ) )
364 if( !sexprs.empty() )
367 if( partial == wxT(
"within_diff_pairs" ) )
369 partial = wxEmptyString;
373 if( sexprs.top() == wxT(
"constraint" ) )
375 constraintType = wxEmptyString;
382 context = NO_CONTEXT;
386 if( context == SEXPR_OPEN && !partial.IsEmpty() )
389 sexprs.push( partial );
391 if( partial == wxT(
"constraint" )
392 || partial == wxT(
"layer" )
393 || partial == wxT(
"severity" ) )
395 context = SEXPR_TOKEN;
397 else if( partial == wxT(
"rule" )
398 || partial == wxT(
"condition" ) )
400 context = SEXPR_STRING;
404 context = NO_CONTEXT;
407 partial = wxEmptyString;
410 else if( partial == wxT(
"disallow" )
411 || isDisallowToken( partial )
412 || partial == wxT(
"min_resolved_spokes" )
413 || partial == wxT(
"zone_connection" ) )
416 sexprs.push( partial );
418 partial = wxEmptyString;
419 context = SEXPR_TOKEN;
422 else if( partial == wxT(
"assertion" ) )
425 sexprs.push( partial );
427 partial = wxEmptyString;
428 context = SEXPR_STRING;
431 else if( isConstraintTypeToken( partial ) )
433 constraintType = partial;
436 context = NO_CONTEXT;
446 if( context == SEXPR_OPEN )
450 tokens = wxT(
"rule|"
453 else if( sexprs.top() == wxT(
"rule" ) )
455 tokens = wxT(
"condition|"
460 else if( sexprs.top() == wxT(
"constraint" ) )
462 if( constraintType == wxT(
"skew" ) )
463 tokens = wxT(
"max|min|opt|within_diff_pairs" );
465 tokens = wxT(
"max|min|opt" );
468 else if( context == SEXPR_TOKEN )
474 else if( sexprs.top() == wxT(
"constraint" ) )
476 tokens = wxT(
"annular_width|"
481 "courtyard_clearance|"
484 "diff_pair_uncoupled|"
491 "min_resolved_spokes|"
492 "physical_clearance|"
493 "physical_hole_clearance|"
496 "solder_mask_expansion|"
497 "solder_paste_abs_margin|"
498 "solder_paste_rel_margin|"
501 "thermal_relief_gap|"
502 "thermal_spoke_width|"
505 "track_segment_length|"
510 else if( sexprs.top() == wxT(
"disallow" ) || isDisallowToken( sexprs.top() ) )
512 tokens = wxT(
"blind_via|"
525 else if( sexprs.top() == wxT(
"zone_connection" ) )
527 tokens = wxT(
"none|solid|thermal_reliefs" );
529 else if( sexprs.top() == wxT(
"min_resolved_spokes" ) )
531 tokens = wxT(
"0|1|2|3|4" );
533 else if( sexprs.top() == wxT(
"layer" ) )
535 tokens = wxT(
"inner|outer|\"x\"" );
537 else if( sexprs.top() == wxT(
"severity" ) )
539 tokens = wxT(
"warning|error|ignore|exclusion" );
542 else if( context == SEXPR_STRING && !sexprs.empty()
543 && ( sexprs.top() == wxT(
"condition" ) || sexprs.top() == wxT(
"assertion" ) ) )
547 else if( context ==
STRING && !sexprs.empty()
548 && ( sexprs.top() == wxT(
"condition" ) || sexprs.top() == wxT(
"assertion" ) ) )
550 if( expr_context == STRUCT_REF )
553 std::set<wxString> propNames;
557 const std::vector<PROPERTY_BASE*>& props = propMgr.
GetProperties( cls.type );
564 if( prop->IsHiddenFromRulesEditor() )
567 wxString ref( prop->Name() );
568 ref.Replace( wxT(
" " ), wxT(
"_" ) );
569 propNames.insert( ref );
573 for(
const wxString& propName : propNames )
574 tokens += wxT(
"|" ) + propName;
580 if( !funcSig.Contains(
"DEPRECATED" ) )
581 tokens += wxT(
"|" ) + funcSig;
584 else if( expr_context ==
STRING )
589 std::shared_ptr<NET_SETTINGS>& netSettings = bds.
m_NetSettings;
591 for(
const auto& [
name, netclass] : netSettings->GetNetclasses() )
592 tokens += wxT(
"|" ) +
name;
599 tokens += wxT(
"|" ) + netnameCandidate;
603 tokens = wxT(
"Bitmap|"
619 tokens = wxT(
"Through|"
626 tokens = wxT(
"Through-hole|"
629 "NPTH, mechanical" );
633 tokens = wxT(
"Input|"
648 tokens = wxT(
"None|"
650 "Fiducial, global to board|"
651 "Fiducial, local to footprint|"
658 tokens = wxT(
"Segment|"
667 tokens = wxT(
"Circle|"
672 "Chamfered rectangle|"
677 tokens = wxT(
"Inherited|"
681 "Thermal reliefs for PTH" );
685 tokens = wxT(
"Inherited|"
692 tokens = wxT(
"Default|"
701 tokens = wxT(
"Left|"
714 if( !tokens.IsEmpty() )
725 std::vector<std::shared_ptr<DRC_RULE>> dummyRules;
727 std::function<bool( wxString* )>
resolver =
728 [&]( wxString* token ) ->
bool
730 if(
m_frame->GetBoard()->ResolveTextVar( token, 0 ) )
737 rulesText =
m_frame->GetBoard()->ConvertCrossReferencesToKIIDs( rulesText );
747 m_errorsReport->Report( wxString::Format( wxT(
"%s <a href='%d:%d'>%s</a>%s" ),
767 std::map<std::pair<wxString, wxString>, wxString> seenConditions;
768 std::regex netclassPattern(
"NetClass\\s*[!=]=\\s*'\"?([^\"\\s]+)'\"?" );
770 for(
const auto& rule : aRules )
774 if( rule->m_Condition )
775 condition = rule->m_Condition->GetExpression();
777 condition.Trim(
true ).Trim(
false );
779 auto key = std::make_pair( condition, rule->m_LayerSource );
781 if( seenConditions.count( key ) )
783 m_errorsReport->Report( wxString::Format(
_(
"Rules '%s' and '%s' share the same condition." ),
785 seenConditions[key] ),
790 seenConditions[key] = rule->m_Name;
793 std::string condUtf8 = condition.ToStdString();
794 std::sregex_iterator it( condUtf8.begin(), condUtf8.end(), netclassPattern );
795 std::sregex_iterator
end;
797 for( ; it !=
end; ++it )
799 wxString ncName = wxString::FromUTF8( ( *it )[1].str() );
803 m_errorsReport->Report( wxString::Format(
_(
"Rule '%s' references undefined netclass '%s'." ),
810 const bool isInner = rule->m_LayerSource.IsSameAs( wxT(
"'inner'" ),
false );
811 const bool isOuter = rule->m_LayerSource.IsSameAs( wxT(
"'outer'" ),
false );
813 if( !rule->m_LayerSource.IsEmpty() && !isInner && !isOuter )
815 LSET invalid = rule->m_LayerCondition & ~enabledLayers;
823 if( !badLayers.IsEmpty() )
829 m_errorsReport->Report( wxString::Format(
_(
"Rule '%s' references undefined layer(s): %s." ),
841 wxString link =
event.GetLinkInfo().GetHref();
843 long line = 0, offset = 0;
847 if( parts.size() > 1 )
849 parts[0].ToLong( &line );
850 parts[1].ToLong( &offset );
853 int pos =
m_textEditor->PositionFromLine( line - 1 ) + ( offset - 1 );
863 wxFileName rulesFile(
m_frame->GetDesignRulesPath() );
865 if( rulesFile.FileExists() )
867 wxTextFile file( rulesFile.GetFullPath() );
871 for ( wxString str = file.GetFirstLine(); !file.Eof(); str = file.GetNextLine() )
879 wxCommandEvent
dummy;
890 if(
m_frame->Prj().IsNullProject() )
893 m_textEditor->AddText(
_(
"Design rules cannot be added without a project" ) );
906 if(
m_frame->Prj().IsNullProject() )
909 wxString rulesFilepath =
m_frame->GetDesignRulesPath();
912 std::string utf8 = std::string( content.mb_str( wxConvUTF8 ) );
917 wxLogError(
_(
"Cannot save design rules to '%s': %s" ), rulesFilepath, writeError );
923 m_frame->GetBoard()->GetDesignSettings().m_DRCEngine->InitEngine( rulesFilepath );
942 std::vector<wxString> msg;
948 msg.emplace_back( t );
952 msg.emplace_back( t );
956 msg.emplace_back( t );
960 msg.emplace_back( t );
964 msg.emplace_back( t );
968 msg.emplace_back( t );
972 msg.emplace_back( t );
976 msg.emplace_back( t );
980 msg.emplace_back( t );
984 msg.emplace_back( t );
986 wxString msg_txt = wxEmptyString;
988 for( wxString i : msg )
989 msg_txt << wxGetTranslation( i );
992 msg_txt.Replace( wxT(
"Ctrl+" ), wxT(
"Cmd+" ) );
994 const wxString& msGg_txt = msg_txt;
999 wxString html_txt = wxEmptyString;
1002 html_txt.Replace( wxS(
"<td" ), wxS(
"<td valign=top" ) );
1011 if( !
m_frame->Prj().IsNullProject() )
1018 if( absFile.FileExists() )
1020 wxTextFile file( absFile.GetFullPath() );
1026 for ( wxString str = file.GetFirstLine(); !file.Eof(); str = file.GetNextLine() )
1034 wxCommandEvent
dummy;
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Container for design settings for a BOARD object.
std::shared_ptr< NET_SETTINGS > m_NetSettings
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)
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
Definition of file extensions used in Kicad.