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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
27#include <wx/log.h>
28#include <wx/regex.h>
29
30#include <pgm_base.h>
33#include <template_fieldnames.h>
34#include <grid_tricks.h>
35#include <eda_text.h>
37#include <bitmaps.h>
38#include <confirm.h>
39#include <kidialog.h>
40#include <layer_ids.h>
41#include <layer_range.h>
42#include <board.h>
44#include <idf_parser.h>
45#include <scintilla_tricks.h>
46#include <wx/stc/stc.h>
49#include <tools/drc_tool.h>
50#include <pcbexpr_evaluator.h>
51#include <string_utils.h>
52#include <drc_rules_lexer.h>
53
54#include <drc/drc_rule_parser.h>
69
70#include <eda_units.h>
83#include <properties/property.h>
85
86static constexpr const wxChar* KI_TRACE_DRC_RULE_EDITOR = wxT( "KI_TRACE_DRC_RULE_EDITOR" );
87
89{
90 switch( aConstraintType )
91 {
96 case HOLE_TO_HOLE_DISTANCE: return true;
97 default: return false;
98 }
99}
100
102 DRC_RULE_EDITOR_CONSTRAINT_NAME aConstraintType,
103 wxString* aConstraintTitle,
104 std::shared_ptr<DRC_RE_BASE_CONSTRAINT_DATA> aConstraintData ) :
106 m_board( aBoard ),
107 m_constraintTitle( aConstraintTitle ),
108 m_validationSucceeded( false ),
109 m_constraintData( aConstraintData ),
110 m_helpWindow( nullptr )
111{
112 wxLogTrace( KI_TRACE_DRC_RULE_EDITOR, wxS( "[PANEL_DRC_RULE_EDITOR] ctor START" ) );
113
114 SetBorders( true, false, false, false );
115
116 m_constraintType = aConstraintType;
117 m_constraintPanel = getConstraintPanel( this, aConstraintType );
118 wxLogTrace( KI_TRACE_DRC_RULE_EDITOR, wxS( "[PANEL_DRC_RULE_EDITOR] adding constraint panel to sizer" ) );
119 m_constraintContentSizer->Add( dynamic_cast<wxPanel*>( m_constraintPanel ), 0, wxEXPAND | wxTOP, 5 );
120 m_layerList = m_board->GetEnabledLayers().UIOrder();
121 m_constraintHeaderTitle->SetLabelText( *aConstraintTitle + " Constraint" );
122
123 m_layerListChoiceCtrl = new wxChoice( this, wxID_ANY );
126 m_LayersComboBoxSizer->Add( m_layerListChoiceCtrl, 0, wxALL | wxEXPAND, 5 );
127 m_layerListChoiceCtrl->Bind( wxEVT_CHOICE,
128 [this]( wxCommandEvent& )
129 {
131
132 if( dlg )
133 dlg->SetModified();
134 } );
135
136 // Hide layer selector for constraints where it doesn't apply
138 {
139 m_LayersComboBoxSizer->Show( false );
140 m_staticText711->Show( false );
141 m_staticline111->Show( false );
142 }
143
144 wxBoxSizer* buttonSizer = new wxBoxSizer( wxHORIZONTAL );
145 m_btnShowMatches = new wxButton( this, wxID_ANY, "Show Matches" );
146 buttonSizer->Add( m_btnShowMatches, 0, wxALL, 5 );
147
148 bContentSizer->Add( buttonSizer, 0, wxALIGN_RIGHT | wxALL, 2 );
149
151
152 m_btnShowMatches->Enable( true );
153
155
156 m_scintillaTricks = std::make_unique<SCINTILLA_TRICKS>(
157 m_textConditionCtrl, wxT( "()" ), false,
158 // onAcceptFn
159 [this]( wxKeyEvent& aEvent )
160 {
161 wxPostEvent( PAGED_DIALOG::GetDialog( this ), wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
162 },
163 // onCharFn
164 [this]( wxStyledTextEvent& aEvent )
165 {
166 onScintillaCharAdded( aEvent );
167 } );
168
169 m_textConditionCtrl->AutoCompSetSeparator( '|' );
170
171 // Hide the base class text control since we use the condition group panel
172 m_textConditionCtrl->Hide();
173
174 bool twoObjects = constraintNeedsTwoObjects( aConstraintType );
175
176 // Create condition group panel (multi-condition UI)
177 // Each condition row has its own custom query text control
178 wxLogTrace( KI_TRACE_DRC_RULE_EDITOR, wxS( "[PANEL_DRC_RULE_EDITOR] creating conditionGroupPanel" ) );
180 m_conditionGroupPanel->SetChangeCallback(
181 [this]()
182 {
184
186
187 if( dlg )
188 {
189 dlg->SetModified();
191 }
192 } );
193
194 wxLogTrace( KI_TRACE_DRC_RULE_EDITOR, wxS( "[PANEL_DRC_RULE_EDITOR] inserting conditionGroupPanel" ) );
195 m_conditionControlsSizer->Insert( 0, m_conditionGroupPanel, 0, wxEXPAND | wxBOTTOM, 5 );
196
197 if( aConstraintType == CUSTOM_RULE )
198 {
199 m_conditionGroupPanel->Hide();
201 m_syntaxHelp->Hide();
202 m_staticline8->Hide();
203 m_LayersComboBoxSizer->Show( false );
204 m_staticText711->Hide();
205 m_staticline111->Hide();
206 }
207
208 m_commentCtrl->Bind( wxEVT_TEXT,
209 [this]( wxCommandEvent& )
210 {
212
213 if( dlg )
214 dlg->SetModified();
215 });
216
217 // Hide the base class syntax check controls since we use inline validation
218 m_checkSyntaxBtnCtrl->Hide();
219 m_syntaxErrorReport->Hide();
220
221 m_nameCtrl->Bind( wxEVT_TEXT,
222 [this]( wxCommandEvent& )
223 {
225 if( dlg )
226 dlg->SetModified();
227
229 {
230 auto* customPanel = dynamic_cast<DRC_RE_CUSTOM_RULE_PANEL*>( m_constraintPanel );
231
232 if( customPanel )
233 customPanel->UpdateRuleName( m_nameCtrl->GetValue() );
234 }
235 } );
236
237 // Regex patterns are compiled as function-local statics in onScintillaCharAdded()
238 // to avoid recompilation on every panel creation.
239}
240
241
249
250
252{
253 if( m_constraintData )
254 {
255 m_nameCtrl->ChangeValue( m_constraintData->GetRuleName() );
256 m_commentCtrl->ChangeValue( m_constraintData->GetComment() );
257 setSelectedLayers( m_constraintData->GetLayers(), m_constraintData->GetLayerSource() );
258 wxString cond = m_constraintData->GetRuleCondition();
259
260 // Use the new condition group panel
261 // Each row manages its own custom query text control visibility
262 m_conditionGroupPanel->ParseCondition( cond );
263 }
264
266 return m_constraintPanel->TransferDataToWindow();
267
268 return true;
269}
270
271
273{
275 return wxEmptyString;
276
277 int sel = m_layerListChoiceCtrl->GetSelection();
278 if( sel <= 0 )
279 return wxEmptyString;
280
281 int layerValue = m_layerIDs[sel - 1];
282
283 switch( layerValue )
284 {
285 case LAYER_SEL_OUTER: return DRC_RULES_LEXER::TokenName( DRCRULE_T::T_outer );
286 case LAYER_SEL_INNER: return DRC_RULES_LEXER::TokenName( DRCRULE_T::T_inner );
287 case LAYER_SEL_TOP:
288 switch( m_constraintType )
289 {
290 case COURTYARD_CLEARANCE: return wxS( "F.CrtYd" );
291 case SILK_TO_SOLDERMASK_CLEARANCE: return wxS( "F.SilkS" );
292 case VIAS_UNDER_SMD:
293 case ALLOWED_ORIENTATION: return wxS( "F.Cu" );
294 default: return m_board ? m_board->GetLayerName( F_Cu ) : wxString();
295 }
296
297 case LAYER_SEL_BOTTOM:
298 switch( m_constraintType )
299 {
300 case COURTYARD_CLEARANCE: return wxS( "B.CrtYd" );
301 case SILK_TO_SOLDERMASK_CLEARANCE: return wxS( "B.SilkS" );
302 case VIAS_UNDER_SMD:
303 case ALLOWED_ORIENTATION: return wxS( "B.Cu" );
304 default: return m_board ? m_board->GetLayerName( B_Cu ) : wxString();
305 }
306 default:
307 if( layerValue >= 0 && m_board )
308 return m_board->GetLayerName( static_cast<PCB_LAYER_ID>( layerValue ) );
309 return wxEmptyString;
310 }
311}
312
313
315{
317 m_constraintPanel->TransferDataFromWindow();
318
319 m_constraintData->SetRuleName( m_nameCtrl->GetValue() );
320 m_constraintData->SetComment( m_commentCtrl->GetValue() );
321 m_constraintData->SetLayers( getSelectedLayers() );
322 m_constraintData->SetLayerSource( getSelectedLayerSource() );
323
324 // Use the new condition group panel
325 wxString combined = m_conditionGroupPanel->BuildCondition();
326 m_constraintData->SetRuleCondition( combined );
327
329 context.ruleName = m_nameCtrl->GetValue();
330 context.comment = m_commentCtrl->GetValue();
332 context.layerClause = buildLayerClause();
333 context.constraintCode = m_constraintData->GetConstraintCode();
334
335 wxString generatedRule = m_constraintPanel->GenerateRule( context );
336 m_constraintData->SetGeneratedRule( generatedRule );
337
338 wxLogTrace( KI_TRACE_DRC_RULE_EDITOR, wxS( "Generated rule '%s':\n%s" ),
339 context.ruleName, generatedRule );
340
341 return true;
342}
343
344
347{
348 EDA_UNITS units = EDA_UNITS::MM;
349
350 for( wxWindow* win = GetParent(); win; win = win->GetParent() )
351 {
352 if( auto* frame = dynamic_cast<EDA_BASE_FRAME*>( win ) )
353 {
354 units = frame->GetUserUnits();
355 break;
356 }
357 }
358
359 switch( aConstraintType )
360 {
361 case VIA_STYLE:
363 aParent,
364 dynamic_pointer_cast<DRC_RE_VIA_STYLE_CONSTRAINT_DATA>( m_constraintData ).get(),
365 units );
366
369 aParent,
370 dynamic_pointer_cast<DRC_RE_MINIMUM_TEXT_HEIGHT_THICKNESS_CONSTRAINT_DATA>( m_constraintData ).get(),
371 units );
372
375 aParent,
376 dynamic_pointer_cast<DRC_RE_ROUTING_DIFF_PAIR_CONSTRAINT_DATA>( m_constraintData ).get(),
377 units );
378
379 case ROUTING_WIDTH:
381 aParent,
382 dynamic_pointer_cast<DRC_RE_ROUTING_WIDTH_CONSTRAINT_DATA>( m_constraintData ).get(),
383 units );
384
385 case PERMITTED_LAYERS:
387 aParent,
388 dynamic_pointer_cast<DRC_RE_PERMITTED_LAYERS_CONSTRAINT_DATA>( m_constraintData ).get() );
389
392 aParent,
393 dynamic_pointer_cast<DRC_RE_ALLOWED_ORIENTATION_CONSTRAINT_DATA>( m_constraintData ).get() );
394
395 case VIAS_UNDER_SMD:
397 aParent, dynamic_pointer_cast<DRC_RE_VIAS_UNDER_SMD_CONSTRAINT_DATA>( m_constraintData ).get() );
398
399 case CUSTOM_RULE:
400 return new DRC_RE_CUSTOM_RULE_PANEL(
401 aParent, dynamic_pointer_cast<DRC_RE_CUSTOM_RULE_CONSTRAINT_DATA>( m_constraintData ) );
402
405 aParent,
406 dynamic_pointer_cast<DRC_RE_MATCHED_LENGTH_DIFF_PAIR_CONSTRAINT_DATA>( m_constraintData ).get(),
407 units );
408
409 case ABSOLUTE_LENGTH:
411 aParent,
412 dynamic_pointer_cast<DRC_RE_ABSOLUTE_LENGTH_TWO_CONSTRAINT_DATA>( m_constraintData ).get(),
413 units );
414
415 default:
416 if( DRC_RULE_EDITOR_UTILS::IsNumericInputType( aConstraintType ) )
417 {
419 aParent,
420 dynamic_pointer_cast<DRC_RE_NUMERIC_INPUT_CONSTRAINT_DATA>( m_constraintData ).get(),
421 units );
422 }
423 else if( DRC_RULE_EDITOR_UTILS::IsBoolInputType( aConstraintType ) )
424 {
426 aParent,
427 dynamic_pointer_cast<DRC_RE_BOOL_INPUT_CONSTRAINT_DATA>( m_constraintData ).get() );
428 }
429 else
430 {
431 return nullptr;
432 };
433 }
434}
435
436
437bool PANEL_DRC_RULE_EDITOR::ValidateInputs( int* aErrorCount, wxString* aValidationMessage )
438{
439 if( !m_callBackRuleNameValidation( m_constraintData->GetId(), m_nameCtrl->GetValue() ) )
440 {
441 m_validationSucceeded = false;
442 ( *aErrorCount )++;
444 DRC_RULE_EDITOR_UTILS::FormatErrorMessage( *aErrorCount, _( "Rule name must be unique." ) );
445 }
446
447 if( m_layerListChoiceCtrl->GetSelection() == wxNOT_FOUND )
448 {
449 m_validationSucceeded = false;
450 ( *aErrorCount )++;
452 DRC_RULE_EDITOR_UTILS::FormatErrorMessage( *aErrorCount, _( "Layer selection is required." ) );
453 }
454
456}
457
458
460{
461 return m_constraintPanel->GenerateRule( aContext );
462}
463
464
465void PANEL_DRC_RULE_EDITOR::onSaveButtonClicked( wxCommandEvent& aEvent )
466{
468 int errorCount = 0;
469 m_validationMessage.Clear();
470
471 ValidateInputs( &errorCount, &m_validationMessage );
472
473 if( !m_constraintPanel->ValidateInputs( &errorCount, &m_validationMessage ) )
474 {
475 m_validationSucceeded = false;
476 }
477
478 if( m_callBackSave )
479 {
481 }
482
484 m_btnShowMatches->Enable( true );
485}
486
487
488void PANEL_DRC_RULE_EDITOR::OnEnterKey( wxCommandEvent& aEvent )
489{
490 onSaveButtonClicked( aEvent );
491}
492
493
494void PANEL_DRC_RULE_EDITOR::Save( wxCommandEvent& aEvent )
495{
496 onSaveButtonClicked( aEvent );
497}
498
499
501{
504}
505
506
507void PANEL_DRC_RULE_EDITOR::Cancel( wxCommandEvent& aEvent )
508{
509 if( m_constraintData && m_constraintData->IsNew() )
510 onRemoveButtonClicked( aEvent );
511 else
512 onCloseButtonClicked( aEvent );
513}
514
515
517{
520}
521
522
523void PANEL_DRC_RULE_EDITOR::onScintillaCharAdded( wxStyledTextEvent& aEvent )
524{
525 if( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKey() == ' ' )
526 {
527 // This is just a short-cut for do-auto-complete
528 }
529
530 m_textConditionCtrl->SearchAnchor();
531
532 int currentPos = m_textConditionCtrl->GetCurrentPos();
533 int startPos = 0;
534
535 for( int line = m_textConditionCtrl->LineFromPosition( currentPos ); line > 0; line-- )
536 {
537 int lineStart = m_textConditionCtrl->PositionFromLine( line );
538 wxString beginning = m_textConditionCtrl->GetTextRange( lineStart, lineStart + 10 );
539
540 if( beginning.StartsWith( wxT( "(condition " ) ) )
541 {
542 startPos = lineStart;
543 break;
544 }
545 }
546
547 enum class EXPR_CONTEXT_T : int
548 {
549 NONE,
550 STRING,
551 SEXPR_OPEN,
552 SEXPR_TOKEN,
553 SEXPR_STRING,
554 STRUCT_REF
555 };
556
557 std::stack<wxString> sexprs;
558 wxString partial;
559 wxString last;
560 EXPR_CONTEXT_T context = EXPR_CONTEXT_T::NONE;
561 EXPR_CONTEXT_T expr_context = EXPR_CONTEXT_T::NONE;
562
563 for( int i = startPos; i < currentPos; ++i )
564 {
565 wxChar c = m_textConditionCtrl->GetCharAt( i );
566
567 if( c == '\\' )
568 {
569 i++; // skip escaped char
570 }
571 else if( context == EXPR_CONTEXT_T::STRING )
572 {
573 if( c == '"' )
574 {
575 context = EXPR_CONTEXT_T::NONE;
576 }
577 else
578 {
579 if( expr_context == EXPR_CONTEXT_T::STRING )
580 {
581 if( c == '\'' )
582 expr_context = EXPR_CONTEXT_T::NONE;
583 else
584 partial += c;
585 }
586 else if( c == '\'' )
587 {
588 last = partial;
589 partial = wxEmptyString;
590 expr_context = EXPR_CONTEXT_T::STRING;
591 }
592 else if( c == '.' )
593 {
594 partial = wxEmptyString;
595 expr_context = EXPR_CONTEXT_T::STRUCT_REF;
596 }
597 else
598 {
599 partial += c;
600 }
601 }
602 }
603 else if( c == '"' )
604 {
605 last = partial;
606 partial = wxEmptyString;
607 context = EXPR_CONTEXT_T::STRING;
608 }
609 else if( c == '(' )
610 {
611 if( context == EXPR_CONTEXT_T::SEXPR_OPEN && !partial.IsEmpty() )
612 {
613 m_textConditionCtrl->AutoCompCancel();
614 sexprs.push( partial );
615 }
616
617 partial = wxEmptyString;
618 context = EXPR_CONTEXT_T::SEXPR_OPEN;
619 }
620 else if( c == ')' )
621 {
622 context = EXPR_CONTEXT_T::NONE;
623 }
624 else if( c == ' ' )
625 {
626 if( context == EXPR_CONTEXT_T::SEXPR_OPEN && !partial.IsEmpty() )
627 {
628 m_textConditionCtrl->AutoCompCancel();
629 sexprs.push( partial );
630
631 if( partial == wxT( "condition" ) )
632 {
633 context = EXPR_CONTEXT_T::SEXPR_STRING;
634 }
635 else
636 {
637 context = EXPR_CONTEXT_T::NONE;
638 }
639
640 partial = wxEmptyString;
641 continue;
642 }
643
644 context = EXPR_CONTEXT_T::NONE;
645 }
646 else
647 {
648 partial += c;
649 }
650 }
651
652 wxString tokens;
653
654 if( context == EXPR_CONTEXT_T::SEXPR_OPEN )
655 {
656 if( sexprs.empty() )
657 {
658 tokens = wxT( "condition" );
659 }
660 }
661 else if( context == EXPR_CONTEXT_T::SEXPR_TOKEN )
662 {
663 if( sexprs.empty() )
664 {
665 /* badly formed grammar */
666 }
667 }
668 else if( context == EXPR_CONTEXT_T::SEXPR_STRING && !sexprs.empty() && sexprs.top() == wxT( "condition" ) )
669 {
670 m_textConditionCtrl->AddText( wxT( "\"" ) );
671 }
672 else if( context == EXPR_CONTEXT_T::STRING && !sexprs.empty() && sexprs.top() == wxT( "condition" ) )
673 {
674 if( expr_context == EXPR_CONTEXT_T::STRUCT_REF )
675 {
677 std::set<wxString> propNames;
678
679 for( const PROPERTY_MANAGER::CLASS_INFO& cls : propMgr.GetAllClasses() )
680 {
681 const std::vector<PROPERTY_BASE*>& props = propMgr.GetProperties( cls.type );
682
683 for( PROPERTY_BASE* prop : props )
684 {
685 // TODO: It would be nice to replace IsHiddenFromRulesEditor with a nickname
686 // system, so that two different properies don't need to be created. This is
687 // a bigger change than I want to make right now, though.
688 if( prop->IsHiddenFromRulesEditor() )
689 continue;
690
691 wxString ref( prop->Name() );
692 ref.Replace( wxT( " " ), wxT( "_" ) );
693 propNames.insert( ref );
694 }
695 }
696
697 for( const wxString& propName : propNames )
698 tokens += wxT( "|" ) + propName;
699
701
702 for( const wxString& funcSig : functions.GetSignatures() )
703 {
704 if( !funcSig.Contains( "DEPRECATED" ) )
705 tokens += wxT( "|" ) + funcSig;
706 }
707 }
708 else if( expr_context == EXPR_CONTEXT_T::STRING )
709 {
710 static wxRegEx netClassRegex( wxS( "^NetClass\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
711 static wxRegEx netNameRegex( wxS( "^NetName\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
712 static wxRegEx typeRegex( wxS( "^Type\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
713 static wxRegEx viaTypeRegex( wxS( "^Via_Type\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
714 static wxRegEx padTypeRegex( wxS( "^Pad_Type\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
715 static wxRegEx pinTypeRegex( wxS( "^Pin_Type\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
716 static wxRegEx fabPropRegex( wxS( "^Fabrication_Property\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
717 static wxRegEx shapeRegex( wxS( "^Shape\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
718 static wxRegEx padShapeRegex( wxS( "^Pad_Shape\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
719 static wxRegEx padConnectionsRegex( wxS( "^Pad_Connections\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
720 static wxRegEx zoneConnStyleRegex( wxS( "^Zone_Connection_Style\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
721 static wxRegEx lineStyleRegex( wxS( "^Line_Style\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
722 static wxRegEx hJustRegex( wxS( "^Horizontal_Justification\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
723 static wxRegEx vJustRegex( wxS( "^Vertical_Justification\\s*[!=]=\\s*$" ), wxRE_ADVANCED );
724
725 if( netClassRegex.Matches( last ) )
726 {
727 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
728 std::shared_ptr<NET_SETTINGS>& netSettings = bds.m_NetSettings;
729
730 for( const auto& [name, netclass] : netSettings->GetNetclasses() )
731 tokens += wxT( "|" ) + name;
732 }
733 else if( netNameRegex.Matches( last ) )
734 {
735 for( const wxString& netnameCandidate : m_board->GetNetClassAssignmentCandidates() )
736 tokens += wxT( "|" ) + netnameCandidate;
737 }
738 else if( typeRegex.Matches( last ) )
739 {
740 tokens = wxT( "Bitmap|"
741 "Dimension|"
742 "Footprint|"
743 "Graphic|"
744 "Group|"
745 "Leader|"
746 "Pad|"
747 "Target|"
748 "Text|"
749 "Text Box|"
750 "Track|"
751 "Via|"
752 "Zone" );
753 }
754 else if( viaTypeRegex.Matches( last ) )
755 {
756 tokens = wxT( "Through|"
757 "Blind/buried|"
758 "Micro" );
759 }
760 else if( padTypeRegex.Matches( last ) )
761 {
762 tokens = wxT( "Through-hole|"
763 "SMD|"
764 "Edge connector|"
765 "NPTH, mechanical" );
766 }
767 else if( pinTypeRegex.Matches( last ) )
768 {
769 tokens = wxT( "Input|"
770 "Output|"
771 "Bidirectional|"
772 "Tri-state|"
773 "Passive|"
774 "Free|"
775 "Unspecified|"
776 "Power input|"
777 "Power output|"
778 "Open collector|"
779 "Open emitter|"
780 "Unconnected" );
781 }
782 else if( fabPropRegex.Matches( last ) )
783 {
784 tokens = wxT( "None|"
785 "BGA pad|"
786 "Fiducial, global to board|"
787 "Fiducial, local to footprint|"
788 "Test point pad|"
789 "Heatsink pad|"
790 "Castellated pad" );
791 }
792 else if( shapeRegex.Matches( last ) )
793 {
794 tokens = wxT( "Segment|"
795 "Rectangle|"
796 "Arc|"
797 "Circle|"
798 "Polygon|"
799 "Bezier" );
800 }
801 else if( padShapeRegex.Matches( last ) )
802 {
803 tokens = wxT( "Circle|"
804 "Rectangle|"
805 "Oval|"
806 "Trapezoid|"
807 "Rounded rectangle|"
808 "Chamfered rectangle|"
809 "Custom" );
810 }
811 else if( padConnectionsRegex.Matches( last ) )
812 {
813 tokens = wxT( "Inherited|"
814 "None|"
815 "Solid|"
816 "Thermal reliefs|"
817 "Thermal reliefs for PTH" );
818 }
819 else if( zoneConnStyleRegex.Matches( last ) )
820 {
821 tokens = wxT( "Inherited|"
822 "None|"
823 "Solid|"
824 "Thermal reliefs" );
825 }
826 else if( lineStyleRegex.Matches( last ) )
827 {
828 tokens = wxT( "Default|"
829 "Solid|"
830 "Dashed|"
831 "Dotted|"
832 "Dash-Dot|"
833 "Dash-Dot-Dot" );
834 }
835 else if( hJustRegex.Matches( last ) )
836 {
837 tokens = wxT( "Left|"
838 "Center|"
839 "Right" );
840 }
841 else if( vJustRegex.Matches( last ) )
842 {
843 tokens = wxT( "Top|"
844 "Center|"
845 "Bottom" );
846 }
847 }
848 }
849
850 if( !tokens.IsEmpty() )
851 m_scintillaTricks->DoAutocomplete( partial, wxSplit( tokens, '|' ) );
852}
853
854
855void PANEL_DRC_RULE_EDITOR::onSyntaxHelp( wxHyperlinkEvent& aEvent )
856{
857 if( m_helpWindow )
858 {
859 m_helpWindow->ShowModeless();
860 return;
861 }
862
863 std::vector<wxString> msg;
864 msg.clear();
865
866 wxString t =
868 ;
869 msg.emplace_back( t );
870
871 t =
873 ;
874 msg.emplace_back( t );
875
876 t =
878 ;
879 msg.emplace_back( t );
880
881 t =
883 ;
884 msg.emplace_back( t );
885
886 t =
888 ;
889 msg.emplace_back( t );
890
891 t =
893 ;
894 msg.emplace_back( t );
895
896 t =
898 ;
899 msg.emplace_back( t );
900
901 t =
903 ;
904 msg.emplace_back( t );
905
906
907 wxString msg_txt = wxEmptyString;
908
909 for( wxString i : msg )
910 msg_txt << wxGetTranslation( i );
911
912#ifdef __WXMAC__
913 msg_txt.Replace( wxT( "Ctrl+" ), wxT( "Cmd+" ) );
914#endif
915 const wxString& msGg_txt = msg_txt;
916
917 m_helpWindow = new HTML_MESSAGE_BOX( this, _( "Syntax Help" ) );
918 m_helpWindow->SetDialogSizeInDU( 420, 320 );
919
920 wxString html_txt = wxEmptyString;
921 ConvertMarkdown2Html( msGg_txt, html_txt );
922
923 html_txt.Replace( wxS( "<td" ), wxS( "<td valign=top" ) );
924 m_helpWindow->AddHTML_Text( html_txt );
925
926 m_helpWindow->ShowModeless();
927}
928
929
930void PANEL_DRC_RULE_EDITOR::onCheckSyntax( wxCommandEvent& event )
931{
932 m_syntaxErrorReport->Clear();
933
934 // Get the complete condition from the condition group panel
935 wxString condition = m_conditionGroupPanel->BuildCondition();
936
937 if( condition.IsEmpty() )
938 {
939 wxString msg = _( "ERROR: No condition text provided for validation." );
941 m_syntaxErrorReport->Flush();
942 return;
943 }
944
945 try
946 {
947 std::vector<std::shared_ptr<DRC_RULE>> dummyRules;
948 wxString ruleTemplate = L"(version 1)\n(rule default\n (condition \"%s\")\n)";
949 wxString formattedRule = wxString::Format( ruleTemplate, condition );
950 DRC_RULES_PARSER ruleParser( formattedRule, _( "DRC rule" ) );
951 ruleParser.Parse( dummyRules, m_syntaxErrorReport );
952 }
953 catch( PARSE_ERROR& pe )
954 {
955 wxString msg = wxString::Format( wxT( "%s <a href='%d:%d'>%s</a>" ),
956 _( "ERROR:" ),
957 pe.lineNumber, pe.byteIndex, pe.ParseProblem() );
958
960 }
961
962 m_syntaxErrorReport->Flush();
963}
964
965
966void PANEL_DRC_RULE_EDITOR::onErrorLinkClicked( wxHtmlLinkEvent& event )
967{
968 wxString link = event.GetLinkInfo().GetHref();
969 wxArrayString parts;
970 long line = 0, offset = 0;
971
972 wxStringSplit( link, parts, ':' );
973
974 if( parts.size() > 1 )
975 {
976 parts[0].ToLong( &line );
977 parts[1].ToLong( &offset );
978 }
979
980 int pos = m_textConditionCtrl->PositionFromLine( line - 1 ) + ( offset - 1 );
981
982 m_textConditionCtrl->GotoPos( pos );
983
984 m_textConditionCtrl->SetFocus();
985}
986
987
988void PANEL_DRC_RULE_EDITOR::onContextMenu( wxMouseEvent& event )
989{
990 wxMenu menu;
991
992 menu.Append( wxID_UNDO, _( "Undo" ) );
993 menu.Append( wxID_REDO, _( "Redo" ) );
994
995 menu.AppendSeparator();
996
997 menu.Append( 1, _( "Cut" ) ); // Don't use wxID_CUT, wxID_COPY, etc. On Mac (at least),
998 menu.Append( 2, _( "Copy" ) ); // wxWidgets never delivers them to us.
999 menu.Append( 3, _( "Paste" ) );
1000 menu.Append( 4, _( "Delete" ) );
1001
1002 menu.AppendSeparator();
1003
1004 menu.Append( 5, _( "Select All" ) );
1005
1006 menu.AppendSeparator();
1007
1008 menu.Append( wxID_ZOOM_IN, _( "Zoom In" ) );
1009 menu.Append( wxID_ZOOM_OUT, _( "Zoom Out" ) );
1010
1011
1012 switch( GetPopupMenuSelectionFromUser( menu ) )
1013 {
1014 case wxID_UNDO: m_textConditionCtrl->Undo(); break;
1015 case wxID_REDO: m_textConditionCtrl->Redo(); break;
1016
1017 case 1: m_textConditionCtrl->Cut(); break;
1018 case 2: m_textConditionCtrl->Copy(); break;
1019 case 3: m_textConditionCtrl->Paste(); break;
1020 case 4:
1021 {
1022 long from, to;
1023 m_textConditionCtrl->GetSelection( &from, &to );
1024
1025 if( to > from )
1026 m_textConditionCtrl->DeleteRange( from, to );
1027
1028 break;
1029 }
1030
1031 case 5: m_textConditionCtrl->SelectAll(); break;
1032
1033 case wxID_ZOOM_IN: m_textConditionCtrl->ZoomIn(); break;
1034 case wxID_ZOOM_OUT: m_textConditionCtrl->ZoomOut(); break;
1035 }
1036}
1037
1038
1040{
1041 if( !m_constraintData )
1042 {
1043 wxLogTrace( KI_TRACE_DRC_RULE_EDITOR, wxS( "Show Matches clicked: no constraint data" ) );
1044 return;
1045 }
1046
1047 wxLogTrace( KI_TRACE_DRC_RULE_EDITOR,
1048 wxS( "Show Matches clicked: nodeId=%d, rule='%s', code='%s'" ),
1049 m_constraintData->GetId(), m_constraintData->GetRuleName(),
1050 m_constraintData->GetConstraintCode() );
1051
1053 {
1054 int matchCount = m_callBackShowMatches( m_constraintData->GetId() );
1055
1056 if( matchCount < 0 )
1057 {
1058 m_btnShowMatches->SetLabel( _( "Show Matches (error)" ) );
1059 }
1060 else
1061 {
1062 m_btnShowMatches->SetLabel( wxString::Format( _( "Show Matches (%d)" ), matchCount ) );
1063 }
1064 }
1065}
1066
1067
1069{
1070 m_btnShowMatches->SetLabel( _( "Show Matches" ) );
1071}
1072
1073
1075{
1076 m_layerListChoiceCtrl->Clear();
1077 m_layerIDs.clear();
1078
1079 m_layerListChoiceCtrl->Append( _( "Any" ) );
1080
1081 switch( aCategory )
1082 {
1084 m_layerListChoiceCtrl->Append( DRC_RULES_LEXER::TokenName( DRCRULE_T::T_outer ) );
1085 m_layerIDs.push_back( LAYER_SEL_OUTER );
1086 m_layerListChoiceCtrl->Append( DRC_RULES_LEXER::TokenName( DRCRULE_T::T_inner ) );
1087 m_layerIDs.push_back( LAYER_SEL_INNER );
1088
1089 for( PCB_LAYER_ID id : m_board->GetEnabledLayers().CuStack() )
1090 {
1091 m_layerListChoiceCtrl->Append( m_board->GetLayerName( id ) );
1092 m_layerIDs.push_back( static_cast<int>( id ) );
1093 }
1094
1095 break;
1096
1098 m_layerListChoiceCtrl->Append( m_board->GetLayerName( F_SilkS ) );
1099 m_layerIDs.push_back( static_cast<int>( F_SilkS ) );
1100 m_layerListChoiceCtrl->Append( m_board->GetLayerName( B_SilkS ) );
1101 m_layerIDs.push_back( static_cast<int>( B_SilkS ) );
1102 break;
1103
1105 m_layerListChoiceCtrl->Append( m_board->GetLayerName( F_Mask ) );
1106 m_layerIDs.push_back( static_cast<int>( F_Mask ) );
1107 m_layerListChoiceCtrl->Append( m_board->GetLayerName( B_Mask ) );
1108 m_layerIDs.push_back( static_cast<int>( B_Mask ) );
1109 break;
1110
1112 m_layerListChoiceCtrl->Append( m_board->GetLayerName( F_Paste ) );
1113 m_layerIDs.push_back( static_cast<int>( F_Paste ) );
1114 m_layerListChoiceCtrl->Append( m_board->GetLayerName( B_Paste ) );
1115 m_layerIDs.push_back( static_cast<int>( B_Paste ) );
1116 break;
1117
1119 m_layerListChoiceCtrl->Append( _( "Top" ) );
1120 m_layerIDs.push_back( LAYER_SEL_TOP );
1121 m_layerListChoiceCtrl->Append( _( "Bottom" ) );
1122 m_layerIDs.push_back( LAYER_SEL_BOTTOM );
1123 break;
1124
1126 m_layerListChoiceCtrl->Append( _( "outer" ) );
1127 m_layerIDs.push_back( LAYER_SEL_OUTER );
1128 m_layerListChoiceCtrl->Append( _( "inner" ) );
1129 m_layerIDs.push_back( LAYER_SEL_INNER );
1130
1131 for( PCB_LAYER_ID id : m_board->GetEnabledLayers().UIOrder() )
1132 {
1133 m_layerListChoiceCtrl->Append( m_board->GetLayerName( id ) );
1134 m_layerIDs.push_back( static_cast<int>( id ) );
1135 }
1136
1137 break;
1138
1140 // No layers to add - selector will be hidden
1141 break;
1142 }
1143
1144 m_layerListChoiceCtrl->SetSelection( 0 );
1145}
1146
1147
1149{
1151 return wxEmptyString;
1152
1153 int selection = m_layerListChoiceCtrl->GetSelection();
1154
1155 if( selection <= 0 )
1156 return wxEmptyString;
1157
1158 size_t index = static_cast<size_t>( selection - 1 );
1159
1160 if( index >= m_layerIDs.size() )
1161 return wxEmptyString;
1162
1163 int layerValue = m_layerIDs[index];
1164
1165 // Handle synthetic layers
1166 if( layerValue < 0 )
1167 {
1168 switch( layerValue )
1169 {
1170 case LAYER_SEL_OUTER:
1171 return wxString::Format( wxS( "(layer %s)" ), DRC_RULES_LEXER::TokenName( DRCRULE_T::T_outer ) );
1172
1173 case LAYER_SEL_INNER:
1174 return wxString::Format( wxS( "(layer %s)" ), DRC_RULES_LEXER::TokenName( DRCRULE_T::T_inner ) );
1175
1176 case LAYER_SEL_TOP:
1178
1179 case LAYER_SEL_BOTTOM:
1181
1182 default:
1183 return wxEmptyString;
1184 }
1185 }
1186
1187 // Real layer ID
1188 PCB_LAYER_ID layerId = static_cast<PCB_LAYER_ID>( layerValue );
1189 wxString clause = wxString::Format( wxS( "(layer \"%s\")" ), m_board->GetLayerName( layerId ) );
1190 wxLogTrace( KI_TRACE_DRC_RULE_EDITOR, wxS( "Layer clause: %s" ), clause );
1191 return clause;
1192}
1193
1195{
1196 int sel = m_layerListChoiceCtrl->GetSelection();
1197
1198 if( sel <= 0 )
1199 {
1201 return { F_SilkS, B_SilkS };
1202
1203 return {};
1204 }
1205
1206 int layerValue = m_layerIDs[sel - 1];
1207
1208 // Translate pseudo-IDs to real layer IDs
1209 if( layerValue < 0 )
1210 {
1211 switch( layerValue )
1212 {
1213 case LAYER_SEL_OUTER: return { F_Cu, B_Cu };
1214 case LAYER_SEL_INNER: return { In1_Cu };
1215 case LAYER_SEL_TOP: return { F_Cu };
1216 case LAYER_SEL_BOTTOM: return { B_Cu };
1217 default: return {};
1218 }
1219 }
1220
1221 return { static_cast<PCB_LAYER_ID>( layerValue ) };
1222}
1223
1224
1225void PANEL_DRC_RULE_EDITOR::setSelectedLayers( const std::vector<PCB_LAYER_ID>& aLayers,
1226 const wxString& aLayerSource )
1227{
1228 // Check for synthetic layer keywords first (for round-trip preservation)
1229 if( aLayerSource == DRC_RULES_LEXER::TokenName( DRCRULE_T::T_outer ) )
1230 {
1231 for( size_t i = 0; i < m_layerIDs.size(); ++i )
1232 {
1233 if( m_layerIDs[i] == LAYER_SEL_OUTER )
1234 {
1235 m_layerListChoiceCtrl->SetSelection( static_cast<int>( i ) + 1 );
1236 return;
1237 }
1238 }
1239 }
1240
1241 if( aLayerSource == DRC_RULES_LEXER::TokenName( DRCRULE_T::T_inner ) )
1242 {
1243 for( size_t i = 0; i < m_layerIDs.size(); ++i )
1244 {
1245 if( m_layerIDs[i] == LAYER_SEL_INNER )
1246 {
1247 m_layerListChoiceCtrl->SetSelection( static_cast<int>( i ) + 1 );
1248 return;
1249 }
1250 }
1251 }
1252
1253 // Map real layer IDs to Top/Bottom pseudo-entries (for TOP_BOTTOM_ANY dropdowns)
1254 if( !aLayers.empty() )
1255 {
1256 PCB_LAYER_ID target = aLayers.front();
1257 bool isFront = IsFrontLayer( target );
1258 bool isBack = IsBackLayer( target );
1259
1260 if( isFront || isBack )
1261 {
1262 int pseudoId = isFront ? LAYER_SEL_TOP : LAYER_SEL_BOTTOM;
1263
1264 for( size_t i = 0; i < m_layerIDs.size(); ++i )
1265 {
1266 if( m_layerIDs[i] == pseudoId )
1267 {
1268 m_layerListChoiceCtrl->SetSelection( static_cast<int>( i ) + 1 );
1269 return;
1270 }
1271 }
1272 }
1273 }
1274
1275 // Handle empty layers (default to "Any")
1276 if( aLayers.empty() )
1277 {
1278 m_layerListChoiceCtrl->SetSelection( 0 );
1279 return;
1280 }
1281
1282 // Find and select the matching layer
1283 int target = static_cast<int>( aLayers.front() );
1284
1285 for( size_t i = 0; i < m_layerIDs.size(); ++i )
1286 {
1287 if( m_layerIDs[i] == target )
1288 {
1289 m_layerListChoiceCtrl->SetSelection( static_cast<int>( i ) + 1 );
1290 return;
1291 }
1292 }
1293
1294 m_layerListChoiceCtrl->SetSelection( 0 );
1295}
int index
const char * name
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:110
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:322
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:39
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
@ 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:69
EDA_UNITS
Definition eda_units.h:48
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition layer_ids.h:780
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition layer_ids.h:803
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ F_Paste
Definition layer_ids.h:104
@ B_Mask
Definition layer_ids.h:98
@ B_Cu
Definition layer_ids.h:65
@ F_Mask
Definition layer_ids.h:97
@ B_Paste
Definition layer_ids.h:105
@ F_SilkS
Definition layer_ids.h:100
@ In1_Cu
Definition layer_ids.h:66
@ B_SilkS
Definition layer_ids.h:101
@ F_Cu
Definition layer_ids.h:64
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