49#include <unordered_map>
61 [
this]( wxKeyEvent& aEvent )
64 wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
67 [
this]( wxStyledTextEvent& aEvent )
76 m_typeRegex.Compile(
"^Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
77 m_viaTypeRegex.Compile(
"^Via_Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
78 m_padTypeRegex.Compile(
"^Pad_Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
79 m_pinTypeRegex.Compile(
"^Pin_Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
80 m_fabPropRegex.Compile(
"^Fabrication_Property\\s*[!=]=\\s*$", wxRE_ADVANCED );
81 m_shapeRegex.Compile(
"^Shape\\s*[!=]=\\s*$", wxRE_ADVANCED );
86 m_hJustRegex.Compile(
"^Horizontal_Justification\\s*[!=]=\\s*$", wxRE_ADVANCED );
87 m_vJustRegex.Compile(
"^Vertical_Justification\\s*[!=]=\\s*$", wxRE_ADVANCED );
91 m_textEditor->SetZoom(
Pgm().GetCommonSettings()->m_Appearance.text_editor_zoom );
113 if( aEvent.GetKeyCode() == WXK_ESCAPE && !
m_textEditor->AutoCompActive() )
117 if(
IsOK( wxGetTopLevelParent(
this ),
_(
"Cancel Changes?" ) ) )
135 menu.Append( wxID_UNDO,
_(
"Undo" ) );
136 menu.Append( wxID_REDO,
_(
"Redo" ) );
138 menu.AppendSeparator();
140 menu.Append( 1,
_(
"Cut" ) );
141 menu.Append( 2,
_(
"Copy" ) );
142 menu.Append( 3,
_(
"Paste" ) );
143 menu.Append( 4,
_(
"Delete" ) );
145 menu.AppendSeparator();
147 menu.Append( 5,
_(
"Select All" ) );
149 menu.AppendSeparator();
151 menu.Append( wxID_ZOOM_IN,
_(
"Zoom In" ) );
152 menu.Append( wxID_ZOOM_OUT,
_(
"Zoom Out" ) );
155 switch( GetPopupMenuSelectionFromUser( menu ) )
200 if( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKey() ==
' ' )
214 for(
int line =
m_textEditor->LineFromPosition( currentPos ); line > 0; line-- )
217 wxString beginning =
m_textEditor->GetTextRange( lineStart, lineStart + 10 );
219 if( beginning.StartsWith( wxT(
"(rule " ) ) )
221 startPos = lineStart;
236 auto isDisallowToken =
237 [](
const wxString& token ) ->
bool
239 return token == wxT(
"blind_via" )
240 || token == wxT(
"buried_via" )
241 || token == wxT(
"graphic" )
242 || token == wxT(
"hole" )
243 || token == wxT(
"micro_via" )
244 || token == wxT(
"pad" )
245 || token == wxT(
"text" )
246 || token == wxT(
"through_via" )
247 || token == wxT(
"track" )
248 || token == wxT(
"via" )
249 || token == wxT(
"zone" );
252 auto isConstraintTypeToken =
253 [](
const wxString& token ) ->
bool
255 return token == wxT(
"annular_width" )
256 || token == wxT(
"assertion" )
257 || token == wxT(
"clearance" )
258 || token == wxT(
"connection_width" )
259 || token == wxT(
"courtyard_clearance" )
260 || token == wxT(
"diff_pair_gap" )
261 || token == wxT(
"diff_pair_uncoupled" )
262 || token == wxT(
"disallow" )
263 || token == wxT(
"edge_clearance" )
264 || token == wxT(
"length" )
265 || token == wxT(
"hole_clearance" )
266 || token == wxT(
"hole_size" )
267 || token == wxT(
"hole_to_hole" )
268 || token == wxT(
"min_resolved_spokes" )
269 || token == wxT(
"physical_clearance" )
270 || token == wxT(
"physical_hole_clearance" )
271 || token == wxT(
"silk_clearance" )
272 || token == wxT(
"skew" )
273 || token == wxT(
"solder_mask_expansion" )
274 || token == wxT(
"solder_paste_abs_margin" )
275 || token == wxT(
"solder_paste_rel_margin" )
276 || token == wxT(
"text_height" )
277 || token == wxT(
"text_thickness" )
278 || token == wxT(
"thermal_relief_gap" )
279 || token == wxT(
"thermal_spoke_width" )
280 || token == wxT(
"track_width" )
281 || token == wxT(
"track_angle" )
282 || token == wxT(
"track_segment_length" )
283 || token == wxT(
"via_count" )
284 || token == wxT(
"via_diameter" )
285 || token == wxT(
"zone_connection" );
288 std::stack<wxString> sexprs;
291 wxString constraintType;
292 int context = NO_CONTEXT;
293 int expr_context = NO_CONTEXT;
295 for(
int i = startPos; i < currentPos; ++i )
303 else if( context ==
STRING )
307 context = NO_CONTEXT;
311 if( expr_context ==
STRING )
314 expr_context = NO_CONTEXT;
321 partial = wxEmptyString;
326 partial = wxEmptyString;
327 expr_context = STRUCT_REF;
338 partial = wxEmptyString;
343 if( context == SEXPR_OPEN && !partial.IsEmpty() )
346 sexprs.push( partial );
349 partial = wxEmptyString;
350 context = SEXPR_OPEN;
354 while( !sexprs.empty() && ( sexprs.top() == wxT(
"assertion" )
355 || sexprs.top() == wxT(
"disallow" )
356 || isDisallowToken( sexprs.top() )
357 || sexprs.top() == wxT(
"min_resolved_spokes" )
358 || sexprs.top() == wxT(
"zone_connection" ) ) )
363 if( !sexprs.empty() )
366 if( partial == wxT(
"within_diff_pairs" ) )
368 partial = wxEmptyString;
372 if( sexprs.top() == wxT(
"constraint" ) )
374 constraintType = wxEmptyString;
381 context = NO_CONTEXT;
385 if( context == SEXPR_OPEN && !partial.IsEmpty() )
388 sexprs.push( partial );
390 if( partial == wxT(
"constraint" )
391 || partial == wxT(
"layer" )
392 || partial == wxT(
"severity" ) )
394 context = SEXPR_TOKEN;
396 else if( partial == wxT(
"rule" )
397 || partial == wxT(
"condition" ) )
399 context = SEXPR_STRING;
403 context = NO_CONTEXT;
406 partial = wxEmptyString;
409 else if( partial == wxT(
"disallow" )
410 || isDisallowToken( partial )
411 || partial == wxT(
"min_resolved_spokes" )
412 || partial == wxT(
"zone_connection" ) )
415 sexprs.push( partial );
417 partial = wxEmptyString;
418 context = SEXPR_TOKEN;
421 else if( partial == wxT(
"assertion" ) )
424 sexprs.push( partial );
426 partial = wxEmptyString;
427 context = SEXPR_STRING;
430 else if( isConstraintTypeToken( partial ) )
432 constraintType = partial;
435 context = NO_CONTEXT;
445 if( context == SEXPR_OPEN )
449 tokens = wxT(
"rule|"
452 else if( sexprs.top() == wxT(
"rule" ) )
454 tokens = wxT(
"condition|"
459 else if( sexprs.top() == wxT(
"constraint" ) )
461 if( constraintType == wxT(
"skew" ) )
462 tokens = wxT(
"max|min|opt|within_diff_pairs" );
464 tokens = wxT(
"max|min|opt" );
467 else if( context == SEXPR_TOKEN )
473 else if( sexprs.top() == wxT(
"constraint" ) )
475 tokens = wxT(
"annular_width|"
480 "courtyard_clearance|"
483 "diff_pair_uncoupled|"
490 "min_resolved_spokes|"
491 "physical_clearance|"
492 "physical_hole_clearance|"
495 "solder_mask_expansion|"
496 "solder_paste_abs_margin|"
497 "solder_paste_rel_margin|"
500 "thermal_relief_gap|"
501 "thermal_spoke_width|"
504 "track_segment_length|"
509 else if( sexprs.top() == wxT(
"disallow" ) || isDisallowToken( sexprs.top() ) )
511 tokens = wxT(
"buried_via|"
522 else if( sexprs.top() == wxT(
"zone_connection" ) )
524 tokens = wxT(
"none|solid|thermal_reliefs" );
526 else if( sexprs.top() == wxT(
"min_resolved_spokes" ) )
528 tokens = wxT(
"0|1|2|3|4" );
530 else if( sexprs.top() == wxT(
"layer" ) )
532 tokens = wxT(
"inner|outer|\"x\"" );
534 else if( sexprs.top() == wxT(
"severity" ) )
536 tokens = wxT(
"warning|error|ignore|exclusion" );
539 else if( context == SEXPR_STRING && !sexprs.empty()
540 && ( sexprs.top() == wxT(
"condition" ) || sexprs.top() == wxT(
"assertion" ) ) )
544 else if( context ==
STRING && !sexprs.empty()
545 && ( sexprs.top() == wxT(
"condition" ) || sexprs.top() == wxT(
"assertion" ) ) )
547 if( expr_context == STRUCT_REF )
550 std::set<wxString> propNames;
554 const std::vector<PROPERTY_BASE*>& props = propMgr.
GetProperties( cls.type );
561 if( prop->IsHiddenFromRulesEditor() )
564 wxString ref( prop->Name() );
565 ref.Replace( wxT(
" " ), wxT(
"_" ) );
566 propNames.insert( ref );
570 for(
const wxString& propName : propNames )
571 tokens += wxT(
"|" ) + propName;
577 if( !funcSig.Contains(
"DEPRECATED" ) )
578 tokens += wxT(
"|" ) + funcSig;
581 else if( expr_context ==
STRING )
586 std::shared_ptr<NET_SETTINGS>& netSettings = bds.
m_NetSettings;
588 for(
const auto& [
name, netclass] : netSettings->GetNetclasses() )
589 tokens += wxT(
"|" ) +
name;
596 tokens += wxT(
"|" ) + netnameCandidate;
600 tokens = wxT(
"Bitmap|"
616 tokens = wxT(
"Through|"
623 tokens = wxT(
"Through-hole|"
626 "NPTH, mechanical" );
630 tokens = wxT(
"Input|"
645 tokens = wxT(
"None|"
647 "Fiducial, global to board|"
648 "Fiducial, local to footprint|"
655 tokens = wxT(
"Segment|"
664 tokens = wxT(
"Circle|"
669 "Chamfered rectangle|"
674 tokens = wxT(
"Inherited|"
678 "Thermal reliefs for PTH" );
682 tokens = wxT(
"Inherited|"
689 tokens = wxT(
"Default|"
698 tokens = wxT(
"Left|"
711 if( !tokens.IsEmpty() )
722 std::vector<std::shared_ptr<DRC_RULE>> dummyRules;
724 std::function<bool( wxString* )>
resolver =
725 [&]( wxString* token ) ->
bool
727 if(
m_frame->GetBoard()->ResolveTextVar( token, 0 ) )
734 rulesText =
m_frame->GetBoard()->ConvertCrossReferencesToKIIDs( rulesText );
744 m_errorsReport->Report( wxString::Format( wxT(
"%s <a href='%d:%d'>%s</a>%s" ),
763 std::unordered_map<wxString, wxString> seenConditions;
764 std::regex netclassPattern(
"NetClass\\s*[!=]=\\s*'\"?([^\"\\s]+)'\"?" );
766 for(
const auto& rule : aRules )
770 if( rule->m_Condition )
771 condition = rule->m_Condition->GetExpression();
773 condition.Trim(
true ).Trim(
false );
775 if( seenConditions.count( condition ) )
777 m_errorsReport->Report( wxString::Format(
_(
"Rules '%s' and '%s' share the same condition." ),
779 seenConditions[condition] ),
784 seenConditions[condition] = rule->m_Name;
787 std::string condUtf8 = condition.ToStdString();
788 std::sregex_iterator it( condUtf8.begin(), condUtf8.end(), netclassPattern );
789 std::sregex_iterator
end;
791 for( ; it !=
end; ++it )
793 wxString ncName = wxString::FromUTF8( ( *it )[1].str() );
797 m_errorsReport->Report( wxString::Format(
_(
"Rule '%s' references undefined netclass '%s'." ),
804 if( !rule->m_LayerSource.IsEmpty() )
806 LSET invalid = rule->m_LayerCondition & ~enabledLayers;
814 if( !badLayers.IsEmpty() )
820 m_errorsReport->Report( wxString::Format(
_(
"Rule '%s' references undefined layer(s): %s." ),
832 wxString link =
event.GetLinkInfo().GetHref();
834 long line = 0, offset = 0;
838 if( parts.size() > 1 )
840 parts[0].ToLong( &line );
841 parts[1].ToLong( &offset );
844 int pos =
m_textEditor->PositionFromLine( line - 1 ) + ( offset - 1 );
854 wxFileName rulesFile(
m_frame->GetDesignRulesPath() );
856 if( rulesFile.FileExists() )
858 wxTextFile file( rulesFile.GetFullPath() );
862 for ( wxString str = file.GetFirstLine(); !file.Eof(); str = file.GetNextLine() )
870 wxCommandEvent
dummy;
881 if(
m_frame->Prj().IsNullProject() )
884 m_textEditor->AddText(
_(
"Design rules cannot be added without a project" ) );
897 if(
m_frame->Prj().IsNullProject() )
900 wxString rulesFilepath =
m_frame->GetDesignRulesPath();
906 m_frame->GetBoard()->GetDesignSettings().m_DRCEngine->InitEngine( rulesFilepath );
928 std::vector<wxString> msg;
934 msg.emplace_back( t );
938 msg.emplace_back( t );
942 msg.emplace_back( t );
946 msg.emplace_back( t );
950 msg.emplace_back( t );
954 msg.emplace_back( t );
958 msg.emplace_back( t );
962 msg.emplace_back( t );
966 msg.emplace_back( t );
970 msg.emplace_back( t );
972 wxString msg_txt = wxEmptyString;
974 for( wxString i : msg )
975 msg_txt << wxGetTranslation( i );
978 msg_txt.Replace( wxT(
"Ctrl+" ), wxT(
"Cmd+" ) );
980 const wxString& msGg_txt = msg_txt;
985 wxString html_txt = wxEmptyString;
988 html_txt.Replace( wxS(
"<td" ), wxS(
"<td valign=top" ) );
997 if( !
m_frame->Prj().IsNullProject() )
1004 if( absFile.FileExists() )
1006 wxTextFile file( absFile.GetFullPath() );
1012 for ( wxString str = file.GetFirstLine(); !file.Eof(); str = file.GetNextLine() )
1020 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.