KiCad PCB EDA Suite
Loading...
Searching...
No Matches
panel_drc_rule_editor.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
23#include <wx/log.h>
24#include <wx/regex.h>
25
26#include <pgm_base.h>
29#include <template_fieldnames.h>
30#include <grid_tricks.h>
31#include <eda_text.h>
33#include <bitmaps.h>
34#include <confirm.h>
35#include <kidialog.h>
36#include <layer_ids.h>
37#include <layer_range.h>
38#include <board.h>
40#include <idf_parser.h>
41#include <scintilla_tricks.h>
42#include <wx/stc/stc.h>
45#include <tools/drc_tool.h>
46#include <pcbexpr_evaluator.h>
47#include <string_utils.h>
48#include <drc_rules_lexer.h>
49
50#include <drc/drc_rule_parser.h>
65
66#include <eda_units.h>
79#include <properties/property.h>
81
82static constexpr const wxChar* KI_TRACE_DRC_RULE_EDITOR = wxT( "KI_TRACE_DRC_RULE_EDITOR" );
83
85{
86 switch( aConstraintType )
87 {
92 case HOLE_TO_HOLE_DISTANCE: return true;
93 default: return false;
94 }
95}
96
98 DRC_RULE_EDITOR_CONSTRAINT_NAME aConstraintType,
99 wxString* aConstraintTitle,
100 std::shared_ptr<DRC_RE_BASE_CONSTRAINT_DATA> aConstraintData ) :
102 m_board( aBoard ),
103 m_constraintTitle( aConstraintTitle ),
104 m_validationSucceeded( false ),
105 m_constraintData( aConstraintData ),
106 m_helpWindow( nullptr )
107{
108 wxLogTrace( KI_TRACE_DRC_RULE_EDITOR, wxS( "[PANEL_DRC_RULE_EDITOR] ctor START" ) );
109
110 SetBorders( true, false, false, false );
111
112 m_constraintType = aConstraintType;
113 m_constraintPanel = getConstraintPanel( this, aConstraintType );
114 wxLogTrace( KI_TRACE_DRC_RULE_EDITOR, wxS( "[PANEL_DRC_RULE_EDITOR] adding constraint panel to sizer" ) );
115 m_constraintContentSizer->Add( dynamic_cast<wxPanel*>( m_constraintPanel ), 0, wxEXPAND | wxTOP, 5 );
116 m_layerList = m_board->GetEnabledLayers().UIOrder();
117 m_constraintHeaderTitle->SetLabelText( *aConstraintTitle + " Constraint" );
118
119 m_layerListChoiceCtrl = new wxChoice( this, wxID_ANY );
122 m_LayersComboBoxSizer->Add( m_layerListChoiceCtrl, 0, wxALL | wxEXPAND, 5 );
123 m_layerListChoiceCtrl->Bind( wxEVT_CHOICE,
124 [this]( wxCommandEvent& )
125 {
127
128 if( dlg )
129 dlg->SetModified();
130 } );
131
132 // Hide layer selector for constraints where it doesn't apply
134 {
135 m_LayersComboBoxSizer->Show( false );
136 m_staticText711->Show( false );
137 m_staticline111->Show( false );
138 }
139
140 wxBoxSizer* buttonSizer = new wxBoxSizer( wxHORIZONTAL );
141 m_btnShowMatches = new wxButton( this, wxID_ANY, "Show Matches" );
142 buttonSizer->Add( m_btnShowMatches, 0, wxALL, 5 );
143
144 bContentSizer->Add( buttonSizer, 0, wxALIGN_RIGHT | wxALL, 2 );
145
147
148 m_btnShowMatches->Enable( true );
149
151
152 m_scintillaTricks = std::make_unique<SCINTILLA_TRICKS>(
153 m_textConditionCtrl, wxT( "()" ), false,
154 // onAcceptFn
155 [this]( wxKeyEvent& aEvent )
156 {
157 wxPostEvent( PAGED_DIALOG::GetDialog( this ), wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
158 },
159 // onCharFn
160 [this]( wxStyledTextEvent& aEvent )
161 {
162 onScintillaCharAdded( aEvent );
163 } );
164
165 m_textConditionCtrl->AutoCompSetSeparator( '|' );
166
167 // Hide the base class text control since we use the condition group panel
168 m_textConditionCtrl->Hide();
169
170 bool twoObjects = constraintNeedsTwoObjects( aConstraintType );
171
172 // Create condition group panel (multi-condition UI)
173 // Each condition row has its own custom query text control
174 wxLogTrace( KI_TRACE_DRC_RULE_EDITOR, wxS( "[PANEL_DRC_RULE_EDITOR] creating conditionGroupPanel" ) );
176 m_conditionGroupPanel->SetChangeCallback(
177 [this]()
178 {
180
182
183 if( dlg )
184 {
185 dlg->SetModified();
187 }
188 } );
189
190 wxLogTrace( KI_TRACE_DRC_RULE_EDITOR, wxS( "[PANEL_DRC_RULE_EDITOR] inserting conditionGroupPanel" ) );
191 m_conditionControlsSizer->Insert( 0, m_conditionGroupPanel, 0, wxEXPAND | wxBOTTOM, 5 );
192
193 if( aConstraintType == CUSTOM_RULE )
194 {
195 m_conditionGroupPanel->Hide();
197 m_syntaxHelp->Hide();
198 m_staticline8->Hide();
199 m_LayersComboBoxSizer->Show( false );
200 m_staticText711->Hide();
201 m_staticline111->Hide();
202 }
203
204 m_commentCtrl->Bind( wxEVT_TEXT,
205 [this]( wxCommandEvent& )
206 {
208
209 if( dlg )
210 dlg->SetModified();
211 });
212
213 // Hide the base class syntax check controls since we use inline validation
214 m_checkSyntaxBtnCtrl->Hide();
215 m_syntaxErrorReport->Hide();
216
217 m_nameCtrl->Bind( wxEVT_TEXT,
218 [this]( wxCommandEvent& )
219 {
221 if( dlg )
222 dlg->SetModified();
223
225 {
226 auto* customPanel = dynamic_cast<DRC_RE_CUSTOM_RULE_PANEL*>( m_constraintPanel );
227
228 if( customPanel )
229 customPanel->UpdateRuleName( m_nameCtrl->GetValue() );
230 }
231 } );
232
233 // Regex patterns are compiled as function-local statics in onScintillaCharAdded()
234 // to avoid recompilation on every panel creation.
235}
236
237
245
246
248{
249 if( m_constraintData )
250 {
251 m_nameCtrl->ChangeValue( m_constraintData->GetRuleName() );
252 m_commentCtrl->ChangeValue( m_constraintData->GetComment() );
253 setSelectedLayers( m_constraintData->GetLayers(), m_constraintData->GetLayerSource() );
254 wxString cond = m_constraintData->GetRuleCondition();
255
256 // Use the new condition group panel
257 // Each row manages its own custom query text control visibility
258 m_conditionGroupPanel->ParseCondition( cond );
259 }
260
262 return m_constraintPanel->TransferDataToWindow();
263
264 return true;
265}
266
267
269{
271 return wxEmptyString;
272
273 int sel = m_layerListChoiceCtrl->GetSelection();
274 if( sel <= 0 )
275 return wxEmptyString;
276
277 int layerValue = m_layerIDs[sel - 1];
278
279 switch( layerValue )
280 {
281 case LAYER_SEL_OUTER: return DRC_RULES_LEXER::TokenName( DRCRULE_T::T_outer );
282 case LAYER_SEL_INNER: return DRC_RULES_LEXER::TokenName( DRCRULE_T::T_inner );
283 case LAYER_SEL_TOP:
284 switch( m_constraintType )
285 {
286 case COURTYARD_CLEARANCE: return wxS( "F.CrtYd" );
287 case SILK_TO_SOLDERMASK_CLEARANCE: return wxS( "F.SilkS" );
288 case VIAS_UNDER_SMD:
289 case ALLOWED_ORIENTATION: return wxS( "F.Cu" );
290 default: return m_board ? m_board->GetLayerName( F_Cu ) : wxString();
291 }
292
293 case LAYER_SEL_BOTTOM:
294 switch( m_constraintType )
295 {
296 case COURTYARD_CLEARANCE: return wxS( "B.CrtYd" );
297 case SILK_TO_SOLDERMASK_CLEARANCE: return wxS( "B.SilkS" );
298 case VIAS_UNDER_SMD:
299 case ALLOWED_ORIENTATION: return wxS( "B.Cu" );
300 default: return m_board ? m_board->GetLayerName( B_Cu ) : wxString();
301 }
302 default:
303 if( layerValue >= 0 && m_board )
304 return m_board->GetLayerName( static_cast<PCB_LAYER_ID>( layerValue ) );
305 return wxEmptyString;
306 }
307}
308
309
311{
313 m_constraintPanel->TransferDataFromWindow();
314
315 m_constraintData->SetRuleName( m_nameCtrl->GetValue() );
316 m_constraintData->SetComment( m_commentCtrl->GetValue() );
317 m_constraintData->SetLayers( getSelectedLayers() );
318 m_constraintData->SetLayerSource( getSelectedLayerSource() );
319
320 // Use the new condition group panel
321 wxString combined = m_conditionGroupPanel->BuildCondition();
322 m_constraintData->SetRuleCondition( combined );
323
325 context.ruleName = m_nameCtrl->GetValue();
326 context.comment = m_commentCtrl->GetValue();
328 context.layerClause = buildLayerClause();
329 context.constraintCode = m_constraintData->GetConstraintCode();
330
331 wxString generatedRule = m_constraintPanel->GenerateRule( context );
332 m_constraintData->SetGeneratedRule( generatedRule );
333
334 wxLogTrace( KI_TRACE_DRC_RULE_EDITOR, wxS( "Generated rule '%s':\n%s" ),
335 context.ruleName, generatedRule );
336
337 return true;
338}
339
340
343{
344 EDA_UNITS units = EDA_UNITS::MM;
345
346 for( wxWindow* win = GetParent(); win; win = win->GetParent() )
347 {
348 if( auto* frame = dynamic_cast<EDA_BASE_FRAME*>( win ) )
349 {
350 units = frame->GetUserUnits();
351 break;
352 }
353 }
354
355 switch( aConstraintType )
356 {
357 case VIA_STYLE:
359 aParent,
360 dynamic_pointer_cast<DRC_RE_VIA_STYLE_CONSTRAINT_DATA>( m_constraintData ).get(),
361 units );
362
365 aParent,
366 dynamic_pointer_cast<DRC_RE_MINIMUM_TEXT_HEIGHT_THICKNESS_CONSTRAINT_DATA>( m_constraintData ).get(),
367 units );
368
371 aParent,
372 dynamic_pointer_cast<DRC_RE_ROUTING_DIFF_PAIR_CONSTRAINT_DATA>( m_constraintData ).get(),
373 units );
374
375 case ROUTING_WIDTH:
377 aParent,
378 dynamic_pointer_cast<DRC_RE_ROUTING_WIDTH_CONSTRAINT_DATA>( m_constraintData ).get(),
379 units );
380
381 case PERMITTED_LAYERS:
383 aParent,
384 dynamic_pointer_cast<DRC_RE_PERMITTED_LAYERS_CONSTRAINT_DATA>( m_constraintData ).get() );
385
388 aParent,
389 dynamic_pointer_cast<DRC_RE_ALLOWED_ORIENTATION_CONSTRAINT_DATA>( m_constraintData ).get() );
390
391 case VIAS_UNDER_SMD:
393 aParent, dynamic_pointer_cast<DRC_RE_VIAS_UNDER_SMD_CONSTRAINT_DATA>( m_constraintData ).get() );
394
395 case CUSTOM_RULE:
396 return new DRC_RE_CUSTOM_RULE_PANEL(
397 aParent, dynamic_pointer_cast<DRC_RE_CUSTOM_RULE_CONSTRAINT_DATA>( m_constraintData ) );
398
401 aParent,
402 dynamic_pointer_cast<DRC_RE_MATCHED_LENGTH_DIFF_PAIR_CONSTRAINT_DATA>( m_constraintData ).get(),
403 units );
404
405 case ABSOLUTE_LENGTH:
407 aParent,
408 dynamic_pointer_cast<DRC_RE_ABSOLUTE_LENGTH_TWO_CONSTRAINT_DATA>( m_constraintData ).get(),
409 units );
410
411 default:
412 if( DRC_RULE_EDITOR_UTILS::IsNumericInputType( aConstraintType ) )
413 {
415 aParent,
416 dynamic_pointer_cast<DRC_RE_NUMERIC_INPUT_CONSTRAINT_DATA>( m_constraintData ).get(),
417 units );
418 }
419 else if( DRC_RULE_EDITOR_UTILS::IsBoolInputType( aConstraintType ) )
420 {
422 aParent,
423 dynamic_pointer_cast<DRC_RE_BOOL_INPUT_CONSTRAINT_DATA>( m_constraintData ).get() );
424 }
425 else
426 {
427 return nullptr;
428 };
429 }
430}
431
432
433bool PANEL_DRC_RULE_EDITOR::ValidateInputs( int* aErrorCount, wxString* aValidationMessage )
434{
435 if( !m_callBackRuleNameValidation( m_constraintData->GetId(), m_nameCtrl->GetValue() ) )
436 {
437 m_validationSucceeded = false;
438 ( *aErrorCount )++;
440 DRC_RULE_EDITOR_UTILS::FormatErrorMessage( *aErrorCount, _( "Rule name must be unique." ) );
441 }
442
443 if( m_layerListChoiceCtrl->GetSelection() == wxNOT_FOUND )
444 {
445 m_validationSucceeded = false;
446 ( *aErrorCount )++;
448 DRC_RULE_EDITOR_UTILS::FormatErrorMessage( *aErrorCount, _( "Layer selection is required." ) );
449 }
450
452}
453
454
456{
457 return m_constraintPanel->GenerateRule( aContext );
458}
459
460
461void PANEL_DRC_RULE_EDITOR::onSaveButtonClicked( wxCommandEvent& aEvent )
462{
464 int errorCount = 0;
465 m_validationMessage.Clear();
466
467 ValidateInputs( &errorCount, &m_validationMessage );
468
469 if( !m_constraintPanel->ValidateInputs( &errorCount, &m_validationMessage ) )
470 {
471 m_validationSucceeded = false;
472 }
473
474 if( m_callBackSave )
475 {
477 }
478
480 m_btnShowMatches->Enable( true );
481}
482
483
484void PANEL_DRC_RULE_EDITOR::OnEnterKey( wxCommandEvent& aEvent )
485{
486 onSaveButtonClicked( aEvent );
487}
488
489
490void PANEL_DRC_RULE_EDITOR::Save( wxCommandEvent& aEvent )
491{
492 onSaveButtonClicked( aEvent );
493}
494
495
497{
500}
501
502
503void PANEL_DRC_RULE_EDITOR::Cancel( wxCommandEvent& aEvent )
504{
505 if( m_constraintData && m_constraintData->IsNew() )
506 onRemoveButtonClicked( aEvent );
507 else
508 onCloseButtonClicked( aEvent );
509}
510
511
513{
516}
517
518
519void PANEL_DRC_RULE_EDITOR::onScintillaCharAdded( wxStyledTextEvent& aEvent )
520{
521 if( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKey() == ' ' )
522 {
523 // This is just a short-cut for do-auto-complete
524 }
525
526 m_textConditionCtrl->SearchAnchor();
527
528 int currentPos = m_textConditionCtrl->GetCurrentPos();
529 int startPos = 0;
530
531 for( int line = m_textConditionCtrl->LineFromPosition( currentPos ); line > 0; line-- )
532 {
533 int lineStart = m_textConditionCtrl->PositionFromLine( line );
534 wxString beginning = m_textConditionCtrl->GetTextRange( lineStart, lineStart + 10 );
535
536 if( beginning.StartsWith( wxT( "(condition " ) ) )
537 {
538 startPos = lineStart;
539 break;
540 }
541 }
542
543 enum class EXPR_CONTEXT_T : int
544 {
545 NONE,
546 STRING,
547 SEXPR_OPEN,
548 SEXPR_TOKEN,
549 SEXPR_STRING,
550 STRUCT_REF
551 };
552
553 std::stack<wxString> sexprs;
554 wxString partial;
555 wxString last;
556 EXPR_CONTEXT_T context = EXPR_CONTEXT_T::NONE;
557 EXPR_CONTEXT_T expr_context = EXPR_CONTEXT_T::NONE;
558
559 for( int i = startPos; i < currentPos; ++i )
560 {
561 wxChar c = m_textConditionCtrl->GetCharAt( i );
562
563 if( c == '\\' )
564 {
565 i++; // skip escaped char
566 }
567 else if( context == EXPR_CONTEXT_T::STRING )
568 {
569 if( c == '"' )
570 {
571 context = EXPR_CONTEXT_T::NONE;
572 }
573 else
574 {
575 if( expr_context == EXPR_CONTEXT_T::STRING )
576 {
577 if( c == '\'' )
578 expr_context = EXPR_CONTEXT_T::NONE;
579 else
580 partial += c;
581 }
582 else if( c == '\'' )
583 {
584 last = partial;
585 partial = wxEmptyString;
586 expr_context = EXPR_CONTEXT_T::STRING;
587 }
588 else if( c == '.' )
589 {
590 partial = wxEmptyString;
591 expr_context = EXPR_CONTEXT_T::STRUCT_REF;
592 }
593 else
594 {
595 partial += c;
596 }
597 }
598 }
599 else if( c == '"' )
600 {
601 last = partial;
602 partial = wxEmptyString;
603 context = EXPR_CONTEXT_T::STRING;
604 }
605 else if( c == '(' )
606 {
607 if( context == EXPR_CONTEXT_T::SEXPR_OPEN && !partial.IsEmpty() )
608 {
609 m_textConditionCtrl->AutoCompCancel();
610 sexprs.push( partial );
611 }
612
613 partial = wxEmptyString;
614 context = EXPR_CONTEXT_T::SEXPR_OPEN;
615 }
616 else if( c == ')' )
617 {
618 context = EXPR_CONTEXT_T::NONE;
619 }
620 else if( c == ' ' )
621 {
622 if( context == EXPR_CONTEXT_T::SEXPR_OPEN && !partial.IsEmpty() )
623 {
624 m_textConditionCtrl->AutoCompCancel();
625 sexprs.push( partial );
626
627 if( partial == wxT( "condition" ) )
628 {
629 context = EXPR_CONTEXT_T::SEXPR_STRING;
630 }
631 else
632 {
633 context = EXPR_CONTEXT_T::NONE;
634 }
635
636 partial = wxEmptyString;
637 continue;
638 }
639
640 context = EXPR_CONTEXT_T::NONE;
641 }
642 else
643 {
644 partial += c;
645 }
646 }
647
648 wxString tokens;
649
650 if( context == EXPR_CONTEXT_T::SEXPR_OPEN )
651 {
652 if( sexprs.empty() )
653 {
654 tokens = wxT( "condition" );
655 }
656 }
657 else if( context == EXPR_CONTEXT_T::SEXPR_TOKEN )
658 {
659 if( sexprs.empty() )
660 {
661 /* badly formed grammar */
662 }
663 }
664 else if( context == EXPR_CONTEXT_T::SEXPR_STRING && !sexprs.empty() && sexprs.top() == wxT( "condition" ) )
665 {
666 m_textConditionCtrl->AddText( wxT( "\"" ) );
667 }
668 else if( context == EXPR_CONTEXT_T::STRING && !sexprs.empty() && sexprs.top() == wxT( "condition" ) )
669 {
670 if( expr_context == EXPR_CONTEXT_T::STRUCT_REF )
671 {
673 std::set<wxString> propNames;
674
675 for( const PROPERTY_MANAGER::CLASS_INFO& cls : propMgr.GetAllClasses() )
676 {
677 const std::vector<PROPERTY_BASE*>& props = propMgr.GetProperties( cls.type );
678
679 for( PROPERTY_BASE* prop : props )
680 {
681 // TODO: It would be nice to replace IsHiddenFromRulesEditor with a nickname
682 // system, so that two different properies don't need to be created. This is
683 // a bigger change than I want to make right now, though.
684 if( prop->IsHiddenFromRulesEditor() )
685 continue;
686
687 wxString ref( prop->Name() );
688 ref.Replace( wxT( " " ), wxT( "_" ) );
689 propNames.insert( ref );
690 }
691 }
692
693 for( const wxString& propName : propNames )
694 tokens += wxT( "|" ) + propName;
695
697
698 for( const wxString& funcSig : functions.GetSignatures() )
699 {
700 if( !funcSig.Contains( "DEPRECATED" ) )
701 tokens += wxT( "|" ) + funcSig;
702 }
703 }
704 else if( expr_context == EXPR_CONTEXT_T::STRING )
705 {
706 static wxRegEx netClassRegex( wxS( "^NetClass\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
707 static wxRegEx netNameRegex( wxS( "^NetName\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
708 static wxRegEx typeRegex( wxS( "^Type\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
709 static wxRegEx viaTypeRegex( wxS( "^Via_Type\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
710 static wxRegEx padTypeRegex( wxS( "^Pad_Type\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
711 static wxRegEx pinTypeRegex( wxS( "^Pin_Type\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
712 static wxRegEx fabPropRegex( wxS( "^Fabrication_Property\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
713 static wxRegEx shapeRegex( wxS( "^Shape\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
714 static wxRegEx padShapeRegex( wxS( "^Pad_Shape\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
715 static wxRegEx padConnectionsRegex( wxS( "^Pad_Connections\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
716 static wxRegEx zoneConnStyleRegex( wxS( "^Zone_Connection_Style\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
717 static wxRegEx lineStyleRegex( wxS( "^Line_Style\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
718 static wxRegEx hJustRegex( wxS( "^Horizontal_Justification\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
719 static wxRegEx vJustRegex( wxS( "^Vertical_Justification\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
720
721 if( netClassRegex.Matches( last ) )
722 {
723 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
724 std::shared_ptr<NET_SETTINGS>& netSettings = bds.m_NetSettings;
725
726 for( const auto& [name, netclass] : netSettings->GetNetclasses() )
727 tokens += wxT( "|" ) + name;
728 }
729 else if( netNameRegex.Matches( last ) )
730 {
731 for( const wxString& netnameCandidate : m_board->GetNetClassAssignmentCandidates() )
732 tokens += wxT( "|" ) + netnameCandidate;
733 }
734 else if( typeRegex.Matches( last ) )
735 {
736 tokens = wxT( "Bitmap|"
737 "Dimension|"
738 "Footprint|"
739 "Graphic|"
740 "Group|"
741 "Leader|"
742 "Pad|"
743 "Target|"
744 "Text|"
745 "Text Box|"
746 "Track|"
747 "Via|"
748 "Zone" );
749 }
750 else if( viaTypeRegex.Matches( last ) )
751 {
752 tokens = wxT( "Through|"
753 "Blind/buried|"
754 "Micro" );
755 }
756 else if( padTypeRegex.Matches( last ) )
757 {
758 tokens = wxT( "Through-hole|"
759 "SMD|"
760 "Edge connector|"
761 "NPTH, mechanical" );
762 }
763 else if( pinTypeRegex.Matches( last ) )
764 {
765 tokens = wxT( "Input|"
766 "Output|"
767 "Bidirectional|"
768 "Tri-state|"
769 "Passive|"
770 "Free|"
771 "Unspecified|"
772 "Power input|"
773 "Power output|"
774 "Open collector|"
775 "Open emitter|"
776 "Unconnected" );
777 }
778 else if( fabPropRegex.Matches( last ) )
779 {
780 tokens = wxT( "None|"
781 "BGA pad|"
782 "Fiducial, global to board|"
783 "Fiducial, local to footprint|"
784 "Test point pad|"
785 "Heatsink pad|"
786 "Castellated pad" );
787 }
788 else if( shapeRegex.Matches( last ) )
789 {
790 tokens = wxT( "Segment|"
791 "Rectangle|"
792 "Arc|"
793 "Circle|"
794 "Polygon|"
795 "Bezier" );
796 }
797 else if( padShapeRegex.Matches( last ) )
798 {
799 tokens = wxT( "Circle|"
800 "Rectangle|"
801 "Oval|"
802 "Trapezoid|"
803 "Rounded rectangle|"
804 "Chamfered rectangle|"
805 "Custom" );
806 }
807 else if( padConnectionsRegex.Matches( last ) )
808 {
809 tokens = wxT( "Inherited|"
810 "None|"
811 "Solid|"
812 "Thermal reliefs|"
813 "Thermal reliefs for PTH" );
814 }
815 else if( zoneConnStyleRegex.Matches( last ) )
816 {
817 tokens = wxT( "Inherited|"
818 "None|"
819 "Solid|"
820 "Thermal reliefs" );
821 }
822 else if( lineStyleRegex.Matches( last ) )
823 {
824 tokens = wxT( "Default|"
825 "Solid|"
826 "Dashed|"
827 "Dotted|"
828 "Dash-Dot|"
829 "Dash-Dot-Dot" );
830 }
831 else if( hJustRegex.Matches( last ) )
832 {
833 tokens = wxT( "Left|"
834 "Center|"
835 "Right" );
836 }
837 else if( vJustRegex.Matches( last ) )
838 {
839 tokens = wxT( "Top|"
840 "Center|"
841 "Bottom" );
842 }
843 }
844 }
845
846 if( !tokens.IsEmpty() )
847 m_scintillaTricks->DoAutocomplete( partial, wxSplit( tokens, '|' ) );
848}
849
850
851void PANEL_DRC_RULE_EDITOR::onSyntaxHelp( wxHyperlinkEvent& aEvent )
852{
853 if( m_helpWindow )
854 {
855 m_helpWindow->ShowModeless();
856 return;
857 }
858
859 std::vector<wxString> msg;
860 msg.clear();
861
862 wxString t =
864 ;
865 msg.emplace_back( t );
866
867 t =
869 ;
870 msg.emplace_back( t );
871
872 t =
874 ;
875 msg.emplace_back( t );
876
877 t =
879 ;
880 msg.emplace_back( t );
881
882 t =
884 ;
885 msg.emplace_back( t );
886
887 t =
889 ;
890 msg.emplace_back( t );
891
892 t =
894 ;
895 msg.emplace_back( t );
896
897 t =
899 ;
900 msg.emplace_back( t );
901
902
903 wxString msg_txt = wxEmptyString;
904
905 for( wxString i : msg )
906 msg_txt << wxGetTranslation( i );
907
908#ifdef __WXMAC__
909 msg_txt.Replace( wxT( "Ctrl+" ), wxT( "Cmd+" ) );
910#endif
911 const wxString& msGg_txt = msg_txt;
912
913 m_helpWindow = new HTML_MESSAGE_BOX( this, _( "Syntax Help" ) );
914 m_helpWindow->SetDialogSizeInDU( 420, 320 );
915
916 wxString html_txt = wxEmptyString;
917 ConvertMarkdown2Html( msGg_txt, html_txt );
918
919 html_txt.Replace( wxS( "<td" ), wxS( "<td valign=top" ) );
920 m_helpWindow->AddHTML_Text( html_txt );
921
922 m_helpWindow->ShowModeless();
923}
924
925
926void PANEL_DRC_RULE_EDITOR::onCheckSyntax( wxCommandEvent& event )
927{
928 m_syntaxErrorReport->Clear();
929
930 // Get the complete condition from the condition group panel
931 wxString condition = m_conditionGroupPanel->BuildCondition();
932
933 if( condition.IsEmpty() )
934 {
935 wxString msg = _( "ERROR: No condition text provided for validation." );
937 m_syntaxErrorReport->Flush();
938 return;
939 }
940
941 try
942 {
943 std::vector<std::shared_ptr<DRC_RULE>> dummyRules;
944 wxString ruleTemplate = L"(version 2)\n(rule default\n (condition \"%s\")\n)";
945 wxString formattedRule = wxString::Format( ruleTemplate, condition );
946 DRC_RULES_PARSER ruleParser( formattedRule, _( "DRC rule" ) );
947 ruleParser.Parse( dummyRules, m_syntaxErrorReport );
948 }
949 catch( PARSE_ERROR& pe )
950 {
951 wxString msg = wxString::Format( wxT( "%s <a href='%d:%d'>%s</a>" ),
952 _( "ERROR:" ),
953 pe.lineNumber, pe.byteIndex, pe.ParseProblem() );
954
956 }
957
958 m_syntaxErrorReport->Flush();
959}
960
961
962void PANEL_DRC_RULE_EDITOR::onErrorLinkClicked( wxHtmlLinkEvent& event )
963{
964 wxString link = event.GetLinkInfo().GetHref();
965 wxArrayString parts;
966 long line = 0, offset = 0;
967
968 wxStringSplit( link, parts, ':' );
969
970 if( parts.size() > 1 )
971 {
972 parts[0].ToLong( &line );
973 parts[1].ToLong( &offset );
974 }
975
976 int pos = m_textConditionCtrl->PositionFromLine( line - 1 ) + ( offset - 1 );
977
978 m_textConditionCtrl->GotoPos( pos );
979
980 m_textConditionCtrl->SetFocus();
981}
982
983
984void PANEL_DRC_RULE_EDITOR::onContextMenu( wxMouseEvent& event )
985{
986 wxMenu menu;
987
988 menu.Append( wxID_UNDO, _( "Undo" ) );
989 menu.Append( wxID_REDO, _( "Redo" ) );
990
991 menu.AppendSeparator();
992
993 menu.Append( 1, _( "Cut" ) ); // Don't use wxID_CUT, wxID_COPY, etc. On Mac (at least),
994 menu.Append( 2, _( "Copy" ) ); // wxWidgets never delivers them to us.
995 menu.Append( 3, _( "Paste" ) );
996 menu.Append( 4, _( "Delete" ) );
997
998 menu.AppendSeparator();
999
1000 menu.Append( 5, _( "Select All" ) );
1001
1002 menu.AppendSeparator();
1003
1004 menu.Append( wxID_ZOOM_IN, _( "Zoom In" ) );
1005 menu.Append( wxID_ZOOM_OUT, _( "Zoom Out" ) );
1006
1007
1008 switch( GetPopupMenuSelectionFromUser( menu ) )
1009 {
1010 case wxID_UNDO: m_textConditionCtrl->Undo(); break;
1011 case wxID_REDO: m_textConditionCtrl->Redo(); break;
1012
1013 case 1: m_textConditionCtrl->Cut(); break;
1014 case 2: m_textConditionCtrl->Copy(); break;
1015 case 3: m_textConditionCtrl->Paste(); break;
1016 case 4:
1017 {
1018 long from, to;
1019 m_textConditionCtrl->GetSelection( &from, &to );
1020
1021 if( to > from )
1022 m_textConditionCtrl->DeleteRange( from, to );
1023
1024 break;
1025 }
1026
1027 case 5: m_textConditionCtrl->SelectAll(); break;
1028
1029 case wxID_ZOOM_IN: m_textConditionCtrl->ZoomIn(); break;
1030 case wxID_ZOOM_OUT: m_textConditionCtrl->ZoomOut(); break;
1031 }
1032}
1033
1034
1036{
1037 if( !m_constraintData )
1038 {
1039 wxLogTrace( KI_TRACE_DRC_RULE_EDITOR, wxS( "Show Matches clicked: no constraint data" ) );
1040 return;
1041 }
1042
1043 wxLogTrace( KI_TRACE_DRC_RULE_EDITOR,
1044 wxS( "Show Matches clicked: nodeId=%d, rule='%s', code='%s'" ),
1045 m_constraintData->GetId(), m_constraintData->GetRuleName(),
1046 m_constraintData->GetConstraintCode() );
1047
1049 {
1050 int matchCount = m_callBackShowMatches( m_constraintData->GetId() );
1051
1052 if( matchCount < 0 )
1053 {
1054 m_btnShowMatches->SetLabel( _( "Show Matches (error)" ) );
1055 }
1056 else
1057 {
1058 m_btnShowMatches->SetLabel( wxString::Format( _( "Show Matches (%d)" ), matchCount ) );
1059 }
1060 }
1061}
1062
1063
1065{
1066 m_btnShowMatches->SetLabel( _( "Show Matches" ) );
1067}
1068
1069
1071{
1072 m_layerListChoiceCtrl->Clear();
1073 m_layerIDs.clear();
1074
1075 m_layerListChoiceCtrl->Append( _( "Any" ) );
1076
1077 switch( aCategory )
1078 {
1080 m_layerListChoiceCtrl->Append( DRC_RULES_LEXER::TokenName( DRCRULE_T::T_outer ) );
1081 m_layerIDs.push_back( LAYER_SEL_OUTER );
1082 m_layerListChoiceCtrl->Append( DRC_RULES_LEXER::TokenName( DRCRULE_T::T_inner ) );
1083 m_layerIDs.push_back( LAYER_SEL_INNER );
1084
1085 for( PCB_LAYER_ID id : m_board->GetEnabledLayers().CuStack() )
1086 {
1087 m_layerListChoiceCtrl->Append( m_board->GetLayerName( id ) );
1088 m_layerIDs.push_back( static_cast<int>( id ) );
1089 }
1090
1091 break;
1092
1094 m_layerListChoiceCtrl->Append( m_board->GetLayerName( F_SilkS ) );
1095 m_layerIDs.push_back( static_cast<int>( F_SilkS ) );
1096 m_layerListChoiceCtrl->Append( m_board->GetLayerName( B_SilkS ) );
1097 m_layerIDs.push_back( static_cast<int>( B_SilkS ) );
1098 break;
1099
1101 m_layerListChoiceCtrl->Append( m_board->GetLayerName( F_Mask ) );
1102 m_layerIDs.push_back( static_cast<int>( F_Mask ) );
1103 m_layerListChoiceCtrl->Append( m_board->GetLayerName( B_Mask ) );
1104 m_layerIDs.push_back( static_cast<int>( B_Mask ) );
1105 break;
1106
1108 m_layerListChoiceCtrl->Append( m_board->GetLayerName( F_Paste ) );
1109 m_layerIDs.push_back( static_cast<int>( F_Paste ) );
1110 m_layerListChoiceCtrl->Append( m_board->GetLayerName( B_Paste ) );
1111 m_layerIDs.push_back( static_cast<int>( B_Paste ) );
1112 break;
1113
1115 m_layerListChoiceCtrl->Append( _( "Top" ) );
1116 m_layerIDs.push_back( LAYER_SEL_TOP );
1117 m_layerListChoiceCtrl->Append( _( "Bottom" ) );
1118 m_layerIDs.push_back( LAYER_SEL_BOTTOM );
1119 break;
1120
1122 m_layerListChoiceCtrl->Append( _( "outer" ) );
1123 m_layerIDs.push_back( LAYER_SEL_OUTER );
1124 m_layerListChoiceCtrl->Append( _( "inner" ) );
1125 m_layerIDs.push_back( LAYER_SEL_INNER );
1126
1127 for( PCB_LAYER_ID id : m_board->GetEnabledLayers().UIOrder() )
1128 {
1129 m_layerListChoiceCtrl->Append( m_board->GetLayerName( id ) );
1130 m_layerIDs.push_back( static_cast<int>( id ) );
1131 }
1132
1133 break;
1134
1136 // No layers to add - selector will be hidden
1137 break;
1138 }
1139
1140 m_layerListChoiceCtrl->SetSelection( 0 );
1141}
1142
1143
1145{
1147 return wxEmptyString;
1148
1149 int selection = m_layerListChoiceCtrl->GetSelection();
1150
1151 if( selection <= 0 )
1152 return wxEmptyString;
1153
1154 size_t index = static_cast<size_t>( selection - 1 );
1155
1156 if( index >= m_layerIDs.size() )
1157 return wxEmptyString;
1158
1159 int layerValue = m_layerIDs[index];
1160
1161 // Handle synthetic layers
1162 if( layerValue < 0 )
1163 {
1164 switch( layerValue )
1165 {
1166 case LAYER_SEL_OUTER:
1167 return wxString::Format( wxS( "(layer %s)" ), DRC_RULES_LEXER::TokenName( DRCRULE_T::T_outer ) );
1168
1169 case LAYER_SEL_INNER:
1170 return wxString::Format( wxS( "(layer %s)" ), DRC_RULES_LEXER::TokenName( DRCRULE_T::T_inner ) );
1171
1172 case LAYER_SEL_TOP:
1174
1175 case LAYER_SEL_BOTTOM:
1177
1178 default:
1179 return wxEmptyString;
1180 }
1181 }
1182
1183 // Real layer ID
1184 PCB_LAYER_ID layerId = static_cast<PCB_LAYER_ID>( layerValue );
1185 wxString clause = wxString::Format( wxS( "(layer \"%s\")" ), m_board->GetLayerName( layerId ) );
1186 wxLogTrace( KI_TRACE_DRC_RULE_EDITOR, wxS( "Layer clause: %s" ), clause );
1187 return clause;
1188}
1189
1191{
1192 int sel = m_layerListChoiceCtrl->GetSelection();
1193
1194 if( sel <= 0 )
1195 {
1197 return { F_SilkS, B_SilkS };
1198
1199 return {};
1200 }
1201
1202 int layerValue = m_layerIDs[sel - 1];
1203
1204 // Translate pseudo-IDs to real layer IDs
1205 if( layerValue < 0 )
1206 {
1207 switch( layerValue )
1208 {
1209 case LAYER_SEL_OUTER: return { F_Cu, B_Cu };
1210 case LAYER_SEL_INNER: return { In1_Cu };
1211 case LAYER_SEL_TOP: return { F_Cu };
1212 case LAYER_SEL_BOTTOM: return { B_Cu };
1213 default: return {};
1214 }
1215 }
1216
1217 return { static_cast<PCB_LAYER_ID>( layerValue ) };
1218}
1219
1220
1221void PANEL_DRC_RULE_EDITOR::setSelectedLayers( const std::vector<PCB_LAYER_ID>& aLayers,
1222 const wxString& aLayerSource )
1223{
1224 // Check for synthetic layer keywords first (for round-trip preservation)
1225 if( aLayerSource == DRC_RULES_LEXER::TokenName( DRCRULE_T::T_outer ) )
1226 {
1227 for( size_t i = 0; i < m_layerIDs.size(); ++i )
1228 {
1229 if( m_layerIDs[i] == LAYER_SEL_OUTER )
1230 {
1231 m_layerListChoiceCtrl->SetSelection( static_cast<int>( i ) + 1 );
1232 return;
1233 }
1234 }
1235 }
1236
1237 if( aLayerSource == DRC_RULES_LEXER::TokenName( DRCRULE_T::T_inner ) )
1238 {
1239 for( size_t i = 0; i < m_layerIDs.size(); ++i )
1240 {
1241 if( m_layerIDs[i] == LAYER_SEL_INNER )
1242 {
1243 m_layerListChoiceCtrl->SetSelection( static_cast<int>( i ) + 1 );
1244 return;
1245 }
1246 }
1247 }
1248
1249 // Map real layer IDs to Top/Bottom pseudo-entries (for TOP_BOTTOM_ANY dropdowns)
1250 if( !aLayers.empty() )
1251 {
1252 PCB_LAYER_ID target = aLayers.front();
1253 bool isFront = IsFrontLayer( target );
1254 bool isBack = IsBackLayer( target );
1255
1256 if( isFront || isBack )
1257 {
1258 int pseudoId = isFront ? LAYER_SEL_TOP : LAYER_SEL_BOTTOM;
1259
1260 for( size_t i = 0; i < m_layerIDs.size(); ++i )
1261 {
1262 if( m_layerIDs[i] == pseudoId )
1263 {
1264 m_layerListChoiceCtrl->SetSelection( static_cast<int>( i ) + 1 );
1265 return;
1266 }
1267 }
1268 }
1269 }
1270
1271 // Handle empty layers (default to "Any")
1272 if( aLayers.empty() )
1273 {
1274 m_layerListChoiceCtrl->SetSelection( 0 );
1275 return;
1276 }
1277
1278 // Find and select the matching layer
1279 int target = static_cast<int>( aLayers.front() );
1280
1281 for( size_t i = 0; i < m_layerIDs.size(); ++i )
1282 {
1283 if( m_layerIDs[i] == target )
1284 {
1285 m_layerListChoiceCtrl->SetSelection( static_cast<int>( i ) + 1 );
1286 return;
1287 }
1288 }
1289
1290 m_layerListChoiceCtrl->SetSelection( 0 );
1291}
int index
const char * name
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
Container for design settings for a BOARD object.
std::shared_ptr< NET_SETTINGS > m_NetSettings
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:372
Overlay panel for absolute length constraints showing min, opt, and max length fields positioned over...
Overlay panel for allowed orientation constraints showing checkboxes for each orientation angle posit...
Overlay panel for boolean input constraints showing a single checkbox positioned over a diagram.
Container panel that manages multiple condition rows with boolean operators.
Simple panel used for editing custom rule text.
void UpdateRuleName(const wxString &aName)
Overlay panel for minimum text height and thickness constraints showing text dimension fields positio...
Overlay panel for numeric input constraints showing a single numeric value field positioned over a di...
Overlay panel for permitted layers constraints showing checkboxes for top and bottom layer selection ...
Overlay panel for differential pair routing constraints showing gap, width, and uncoupled length fiel...
Overlay panel for routing width constraints showing min/preferred/max width fields positioned over a ...
Overlay panel for via style constraints showing via diameter and hole size fields positioned over a d...
void Parse(std::vector< std::shared_ptr< DRC_RULE > > &aRules, REPORTER *aReporter)
static bool IsNumericInputType(const DRC_RULE_EDITOR_CONSTRAINT_NAME &aConstraintType)
static wxString FormatErrorMessage(int aErrorCount, const wxString &aErrorMessage)
static wxString TranslateTopBottomLayer(DRC_RULE_EDITOR_CONSTRAINT_NAME aConstraintType, bool aIsTop)
Translate a top/bottom selection to the appropriate layer clause or condition.
static DRC_LAYER_CATEGORY GetLayerCategoryForConstraint(DRC_RULE_EDITOR_CONSTRAINT_NAME aConstraintType)
Get the layer category for a constraint type.
static bool IsBoolInputType(const DRC_RULE_EDITOR_CONSTRAINT_NAME &aConstraintType)
The base frame for deriving all KiCad main window classes.
static PAGED_DIALOG * GetDialog(wxWindow *aWindow)
PANEL_DRC_RULE_EDITOR_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxBORDER_NONE|wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
std::shared_ptr< DRC_RE_BASE_CONSTRAINT_DATA > m_constraintData
void onCloseButtonClicked(wxCommandEvent &aEvent)
Handles the close button click event, invoking the close callback.
wxString GenerateRule(const RULE_GENERATION_CONTEXT &aContext) override
PANEL_DRC_RULE_EDITOR(wxWindow *aParent, BOARD *aBoard, DRC_RULE_EDITOR_CONSTRAINT_NAME aConstraintType, wxString *aConstraintTitle, std::shared_ptr< DRC_RE_BASE_CONSTRAINT_DATA > aConstraintData)
void onScintillaCharAdded(wxStyledTextEvent &aEvent)
Handles character addition in the Scintilla text control, performing auto-complete and context-sensit...
void onShowMatchesButtonClicked(wxCommandEvent &aEvent)
void onCheckSyntax(wxCommandEvent &aEvent) override
Checks the syntax of the DRC rule condition and reports any errors.
std::function< void(int aNodeId)> m_callBackSave
HTML_MESSAGE_BOX * m_helpWindow
wxString getSelectedLayerSource() const
void onContextMenu(wxMouseEvent &aEvent) override
Handles right-click context menu actions for text editing (undo, redo, cut, copy, paste,...
void onRemoveButtonClicked(wxCommandEvent &aEvent)
Handles the remove button click event, invoking the remove callback.
void setSelectedLayers(const std::vector< PCB_LAYER_ID > &aLayers, const wxString &aLayerSource=wxEmptyString)
DRC_LAYER_CATEGORY m_layerCategory
void onSyntaxHelp(wxHyperlinkEvent &aEvent) override
Displays a modeless help window with syntax and rule documentation.
std::vector< int > m_layerIDs
void populateLayerSelector(DRC_LAYER_CATEGORY aCategory)
DRC_RULE_EDITOR_CONSTRAINT_NAME m_constraintType
void Cancel(wxCommandEvent &aEvent)
void onErrorLinkClicked(wxHtmlLinkEvent &aEvent) override
Handles clicks on error links in the syntax error report and navigates to the error location.
std::function< void(int aNodeId)> m_callBackRemove
void OnEnterKey(wxCommandEvent &aEvent) override
std::function< int(int aNodeId)> m_callBackShowMatches
std::function< bool(int aNodeId, const wxString &aRuleName)> m_callBackRuleNameValidation
DRC_RE_CONDITION_GROUP_PANEL * m_conditionGroupPanel
std::unique_ptr< SCINTILLA_TRICKS > m_scintillaTricks
void Save(wxCommandEvent &aEvent)
void onSaveButtonClicked(wxCommandEvent &aEvent)
Handles the save button click event, validating inputs and invoking the save callback if valid.
DRC_RULE_EDITOR_CONTENT_PANEL_BASE * getConstraintPanel(wxWindow *aParent, const DRC_RULE_EDITOR_CONSTRAINT_NAME &aConstraintType)
std::vector< PCB_LAYER_ID > getSelectedLayers()
std::function< void(int aNodeId)> m_callBackClose
bool ValidateInputs(int *aErrorCount, wxString *aValidationMessage) override
DRC_RULE_EDITOR_CONTENT_PANEL_BASE * m_constraintPanel
const wxArrayString GetSignatures() const
static PCBEXPR_BUILTIN_FUNCTIONS & Instance()
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()
void SetModified()
Marks the dialog as modified, indicating unsaved changes.
void RefreshContentScrollArea()
Recalculates the scrolled content area's virtual size based on the current content panel's best size,...
static RULE_EDITOR_DIALOG_BASE * GetDialog(wxWindow *aWindow)
Static method to retrieve the rule editor dialog instance associated with a given window.
void SetBorders(bool aLeft, bool aRight, bool aTop, bool aBottom)
Definition wx_panel.h:35
This file is part of the common library.
DRC_RULE_EDITOR_CONSTRAINT_NAME
@ ALLOWED_ORIENTATION
@ ROUTING_DIFF_PAIR
@ SILK_TO_SOLDERMASK_CLEARANCE
@ COURTYARD_CLEARANCE
@ PHYSICAL_CLEARANCE
@ CREEPAGE_DISTANCE
@ ABSOLUTE_LENGTH
@ MINIMUM_CLEARANCE
@ PERMITTED_LAYERS
@ MINIMUM_TEXT_HEIGHT_AND_THICKNESS
@ COPPER_TO_HOLE_CLEARANCE
@ HOLE_TO_HOLE_DISTANCE
@ ROUTING_WIDTH
@ MATCHED_LENGTH_DIFF_PAIR
DRC_LAYER_CATEGORY
Layer categories for filtering the layer selector dropdown.
@ NO_LAYER_SELECTOR
Hide layer selector entirely.
@ TOP_BOTTOM_ANY
Simplified top/bottom/any selector with custom translation.
@ SOLDERMASK_ONLY
F_Mask, B_Mask.
@ GENERAL_ANY_LAYER
All layers + inner/outer synthetic.
@ SOLDERPASTE_ONLY
F_Paste, B_Paste.
@ COPPER_ONLY
Copper layers + inner/outer synthetic.
@ SILKSCREEN_ONLY
F_SilkS, B_SilkS.
@ LAYER_SEL_BOTTOM
Context-dependent back/bottom layer.
@ LAYER_SEL_OUTER
External copper layers (F_Cu + B_Cu)
@ LAYER_SEL_TOP
Context-dependent front/top layer.
@ LAYER_SEL_INNER
Internal copper layers (In1_Cu through In30_Cu)
#define _(s)
@ NONE
Definition eda_shape.h:72
EDA_UNITS
Definition eda_units.h:44
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition layer_ids.h:778
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition layer_ids.h:801
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_Paste
Definition layer_ids.h:100
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ F_Mask
Definition layer_ids.h:93
@ B_Paste
Definition layer_ids.h:101
@ F_SilkS
Definition layer_ids.h:96
@ In1_Cu
Definition layer_ids.h:62
@ B_SilkS
Definition layer_ids.h:97
@ F_Cu
Definition layer_ids.h:60
static bool constraintNeedsTwoObjects(DRC_RULE_EDITOR_CONSTRAINT_NAME aConstraintType)
static constexpr const wxChar * KI_TRACE_DRC_RULE_EDITOR
see class PGM_BASE
@ RPT_SEVERITY_ERROR
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