KiCad PCB EDA Suite
Loading...
Searching...
No Matches
panel_setup_rules.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 The 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
24#include <bitmaps.h>
25#include <confirm.h>
28#include <pcb_edit_frame.h>
29#include <pcbexpr_evaluator.h>
30#include <board.h>
32#include <project.h>
33#include <string_utils.h>
34#include <tool/tool_manager.h>
35#include <panel_setup_rules.h>
38#include <scintilla_tricks.h>
39#include <drc/drc_rule_parser.h>
40#include <tools/drc_tool.h>
41#include <pgm_base.h>
43
44PANEL_SETUP_RULES::PANEL_SETUP_RULES( wxWindow* aParentWindow, PCB_EDIT_FRAME* aFrame ) :
45 PANEL_SETUP_RULES_BASE( aParentWindow ),
46 m_frame( aFrame ),
47 m_scintillaTricks( nullptr ),
48 m_helpWindow( nullptr )
49{
50 m_scintillaTricks = new SCINTILLA_TRICKS( m_textEditor, wxT( "()" ), false,
51 // onAcceptFn
52 [this]( wxKeyEvent& aEvent )
53 {
54 wxPostEvent( PAGED_DIALOG::GetDialog( this ),
55 wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
56 },
57 // onCharFn
58 [this]( wxStyledTextEvent& aEvent )
59 {
60 onScintillaCharAdded( aEvent );
61 } );
62
63 m_textEditor->AutoCompSetSeparator( '|' );
64
65 m_netClassRegex.Compile( "^NetClass\\s*[!=]=\\s*$", wxRE_ADVANCED );
66 m_netNameRegex.Compile( "^NetName\\s*[!=]=\\s*$", wxRE_ADVANCED );
67 m_typeRegex.Compile( "^Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
68 m_viaTypeRegex.Compile( "^Via_Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
69 m_padTypeRegex.Compile( "^Pad_Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
70 m_pinTypeRegex.Compile( "^Pin_Type\\s*[!=]=\\s*$", wxRE_ADVANCED );
71 m_fabPropRegex.Compile( "^Fabrication_Property\\s*[!=]=\\s*$", wxRE_ADVANCED );
72 m_shapeRegex.Compile( "^Shape\\s*[!=]=\\s*$", wxRE_ADVANCED );
73 m_padShapeRegex.Compile( "^Pad_Shape\\s*[!=]=\\s*$", wxRE_ADVANCED );
74 m_padConnectionsRegex.Compile( "^Pad_Connections\\s*[!=]=\\s*$", wxRE_ADVANCED );
75 m_zoneConnStyleRegex.Compile( "^Zone_Connection_Style\\s*[!=]=\\s*$", wxRE_ADVANCED );
76 m_lineStyleRegex.Compile( "^Line_Style\\s*[!=]=\\s*$", wxRE_ADVANCED );
77 m_hJustRegex.Compile( "^Horizontal_Justification\\s*[!=]=\\s*$", wxRE_ADVANCED );
78 m_vJustRegex.Compile( "^Vertical_Justification\\s*[!=]=\\s*$", wxRE_ADVANCED );
79
80 m_compileButton->SetBitmap( KiBitmapBundle( BITMAPS::drc ) );
81
83
84 m_textEditor->UsePopUp( 0 );
85 m_textEditor->Bind( wxEVT_CHAR_HOOK, &PANEL_SETUP_RULES::onCharHook, this );
86}
87
88
90{
91 m_textEditor->Unbind( wxEVT_CHAR_HOOK, &PANEL_SETUP_RULES::onCharHook, this );
93
94 delete m_scintillaTricks;
95
96 if( m_helpWindow )
97 m_helpWindow->Destroy();
98};
99
100
101void PANEL_SETUP_RULES::onCharHook( wxKeyEvent& aEvent )
102{
103 if( aEvent.GetKeyCode() == WXK_ESCAPE && !m_textEditor->AutoCompActive() )
104 {
105 if( m_originalText != m_textEditor->GetText() )
106 {
107 if( IsOK( wxGetTopLevelParent( this ), _( "Cancel Changes?" ) ) )
108 {
109 m_textEditor->SetText( m_originalText );
110 m_textEditor->SelectAll();
111 }
112
113 return;
114 }
115 }
116
117 aEvent.Skip();
118}
119
120
121void PANEL_SETUP_RULES::OnContextMenu(wxMouseEvent &event)
122{
123 wxMenu menu;
124
125 menu.Append( wxID_UNDO, _( "Undo" ) );
126 menu.Append( wxID_REDO, _( "Redo" ) );
127
128 menu.AppendSeparator();
129
130 menu.Append( 1, _( "Cut" ) ); // Don't use wxID_CUT, wxID_COPY, etc. On Mac (at least),
131 menu.Append( 2, _( "Copy" ) ); // wxWidgets never delivers them to us.
132 menu.Append( 3, _( "Paste" ) );
133 menu.Append( 4, _( "Delete" ) );
134
135 menu.AppendSeparator();
136
137 menu.Append( 5, _( "Select All" ) );
138
139 menu.AppendSeparator();
140
141 menu.Append( wxID_ZOOM_IN, _( "Zoom In" ) );
142 menu.Append( wxID_ZOOM_OUT, _( "Zoom Out" ) );
143
144
145 switch( GetPopupMenuSelectionFromUser( menu ) )
146 {
147 case wxID_UNDO:
148 m_textEditor->Undo();
149 break;
150 case wxID_REDO:
151 m_textEditor->Redo();
152 break;
153
154 case 1:
155 m_textEditor->Cut();
156 break;
157 case 2:
158 m_textEditor->Copy();
159 break;
160 case 3:
161 m_textEditor->Paste();
162 break;
163 case 4:
164 {
165 long from, to;
166 m_textEditor->GetSelection( &from, &to );
167
168 if( to > from )
169 m_textEditor->DeleteRange( from, to );
170
171 break;
172 }
173
174 case 5:
175 m_textEditor->SelectAll();
176 break;
177
178 case wxID_ZOOM_IN:
179 m_textEditor->ZoomIn();
180 break;
181 case wxID_ZOOM_OUT:
182 m_textEditor->ZoomOut();
183 break;
184 }
185}
186
187
188void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent )
189{
190 if( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKey() == ' ' )
191 {
192 // This is just a short-cut for do-auto-complete
193 }
194 else
195 {
197 }
198
199 m_textEditor->SearchAnchor();
200
201 int currentPos = m_textEditor->GetCurrentPos();
202 int startPos = 0;
203
204 for( int line = m_textEditor->LineFromPosition( currentPos ); line > 0; line-- )
205 {
206 int lineStart = m_textEditor->PositionFromLine( line );
207 wxString beginning = m_textEditor->GetTextRange( lineStart, lineStart + 10 );
208
209 if( beginning.StartsWith( wxT( "(rule " ) ) )
210 {
211 startPos = lineStart;
212 break;
213 }
214 }
215
216 enum
217 {
218 NO_CONTEXT,
219 STRING,
220 SEXPR_OPEN,
221 SEXPR_TOKEN,
222 SEXPR_STRING,
223 STRUCT_REF
224 };
225
226 auto isDisallowToken =
227 []( const wxString& token ) -> bool
228 {
229 return token == wxT( "buried_via" )
230 || token == wxT( "graphic" )
231 || token == wxT( "hole" )
232 || token == wxT( "micro_via" )
233 || token == wxT( "pad" )
234 || token == wxT( "text" )
235 || token == wxT( "track" )
236 || token == wxT( "via" )
237 || token == wxT( "zone" );
238 };
239
240 auto isConstraintTypeToken =
241 []( const wxString& token ) -> bool
242 {
243 return token == wxT( "annular_width" )
244 || token == wxT( "assertion" )
245 || token == wxT( "clearance" )
246 || token == wxT( "connection_width" )
247 || token == wxT( "courtyard_clearance" )
248 || token == wxT( "diff_pair_gap" )
249 || token == wxT( "diff_pair_uncoupled" )
250 || token == wxT( "disallow" )
251 || token == wxT( "edge_clearance" )
252 || token == wxT( "length" )
253 || token == wxT( "hole_clearance" )
254 || token == wxT( "hole_size" )
255 || token == wxT( "hole_to_hole" )
256 || token == wxT( "min_resolved_spokes" )
257 || token == wxT( "physical_clearance" )
258 || token == wxT( "physical_hole_clearance" )
259 || token == wxT( "silk_clearance" )
260 || token == wxT( "skew" )
261 || token == wxT( "solder_mask_expansion" )
262 || token == wxT( "solder_paste_abs_margin" )
263 || token == wxT( "solder_paste_rel_margin" )
264 || token == wxT( "text_height" )
265 || token == wxT( "text_thickness" )
266 || token == wxT( "thermal_relief_gap" )
267 || token == wxT( "thermal_spoke_width" )
268 || token == wxT( "track_width" )
269 || token == wxT( "track_angle" )
270 || token == wxT( "track_segment_length" )
271 || token == wxT( "via_count" )
272 || token == wxT( "via_diameter" )
273 || token == wxT( "zone_connection" );
274 };
275
276 std::stack<wxString> sexprs;
277 wxString partial;
278 wxString last;
279 wxString constraintType;
280 int context = NO_CONTEXT;
281 int expr_context = NO_CONTEXT;
282
283 for( int i = startPos; i < currentPos; ++i )
284 {
285 wxChar c = m_textEditor->GetCharAt( i );
286
287 if( c == '\\' )
288 {
289 i++; // skip escaped char
290 }
291 else if( context == STRING )
292 {
293 if( c == '"' )
294 {
295 context = NO_CONTEXT;
296 }
297 else
298 {
299 if( expr_context == STRING )
300 {
301 if( c == '\'' )
302 expr_context = NO_CONTEXT;
303 else
304 partial += c;
305 }
306 else if( c == '\'' )
307 {
308 last = partial;
309 partial = wxEmptyString;
310 expr_context = STRING;
311 }
312 else if( c == '.' )
313 {
314 partial = wxEmptyString;
315 expr_context = STRUCT_REF;
316 }
317 else
318 {
319 partial += c;
320 }
321 }
322 }
323 else if( c == '"' )
324 {
325 last = partial;
326 partial = wxEmptyString;
327 context = STRING;
328 }
329 else if( c == '(' )
330 {
331 if( context == SEXPR_OPEN && !partial.IsEmpty() )
332 {
333 m_textEditor->AutoCompCancel();
334 sexprs.push( partial );
335 }
336
337 partial = wxEmptyString;
338 context = SEXPR_OPEN;
339 }
340 else if( c == ')' )
341 {
342 while( !sexprs.empty() && ( sexprs.top() == wxT( "assertion" )
343 || sexprs.top() == wxT( "disallow" )
344 || isDisallowToken( sexprs.top() )
345 || sexprs.top() == wxT( "min_resolved_spokes" )
346 || sexprs.top() == wxT( "zone_connection" ) ) )
347 {
348 sexprs.pop();
349 }
350
351 if( !sexprs.empty() )
352 {
353 // Ignore argument-less tokens
354 if( partial == wxT( "within_diff_pairs" ) )
355 {
356 partial = wxEmptyString;
357 }
358 else
359 {
360 if( sexprs.top() == wxT( "constraint" ) )
361 {
362 constraintType = wxEmptyString;
363 }
364
365 sexprs.pop();
366 }
367 }
368
369 context = NO_CONTEXT;
370 }
371 else if( c == ' ' )
372 {
373 if( context == SEXPR_OPEN && !partial.IsEmpty() )
374 {
375 m_textEditor->AutoCompCancel();
376 sexprs.push( partial );
377
378 if( partial == wxT( "constraint" )
379 || partial == wxT( "layer" )
380 || partial == wxT( "severity" ) )
381 {
382 context = SEXPR_TOKEN;
383 }
384 else if( partial == wxT( "rule" )
385 || partial == wxT( "condition" ) )
386 {
387 context = SEXPR_STRING;
388 }
389 else
390 {
391 context = NO_CONTEXT;
392 }
393
394 partial = wxEmptyString;
395 continue;
396 }
397 else if( partial == wxT( "disallow" )
398 || isDisallowToken( partial )
399 || partial == wxT( "min_resolved_spokes" )
400 || partial == wxT( "zone_connection" ) )
401 {
402 m_textEditor->AutoCompCancel();
403 sexprs.push( partial );
404
405 partial = wxEmptyString;
406 context = SEXPR_TOKEN;
407 continue;
408 }
409 else if( partial == wxT( "assertion" ) )
410 {
411 m_textEditor->AutoCompCancel();
412 sexprs.push( partial );
413
414 partial = wxEmptyString;
415 context = SEXPR_STRING;
416 continue;
417 }
418 else if( isConstraintTypeToken( partial ) )
419 {
420 constraintType = partial;
421 }
422
423 context = NO_CONTEXT;
424 }
425 else
426 {
427 partial += c;
428 }
429 }
430
431 wxString tokens;
432
433 if( context == SEXPR_OPEN )
434 {
435 if( sexprs.empty() )
436 {
437 tokens = wxT( "rule|"
438 "version" );
439 }
440 else if( sexprs.top() == wxT( "rule" ) )
441 {
442 tokens = wxT( "condition|"
443 "constraint|"
444 "layer|"
445 "severity" );
446 }
447 else if( sexprs.top() == wxT( "constraint" ) )
448 {
449 if( constraintType == wxT( "skew" ) )
450 tokens = wxT( "max|min|opt|within_diff_pairs" );
451 else
452 tokens = wxT( "max|min|opt" );
453 }
454 }
455 else if( context == SEXPR_TOKEN )
456 {
457 if( sexprs.empty() )
458 {
459 /* badly formed grammar */
460 }
461 else if( sexprs.top() == wxT( "constraint" ) )
462 {
463 tokens = wxT( "annular_width|"
464 "assertion|"
465 "clearance|"
466 "connection_width|"
467 "courtyard_clearance|"
468 "creepage|"
469 "diff_pair_gap|"
470 "diff_pair_uncoupled|"
471 "disallow|"
472 "edge_clearance|"
473 "length|"
474 "hole_clearance|"
475 "hole_size|"
476 "hole_to_hole|"
477 "min_resolved_spokes|"
478 "physical_clearance|"
479 "physical_hole_clearance|"
480 "silk_clearance|"
481 "skew|"
482 "solder_mask_expansion|"
483 "solder_paste_abs_margin|"
484 "solder_paste_rel_margin|"
485 "text_height|"
486 "text_thickness|"
487 "thermal_relief_gap|"
488 "thermal_spoke_width|"
489 "track_width|"
490 "track_angle|"
491 "track_segment_length|"
492 "via_count|"
493 "via_diameter|"
494 "zone_connection" );
495 }
496 else if( sexprs.top() == wxT( "disallow" ) || isDisallowToken( sexprs.top() ) )
497 {
498 tokens = wxT( "buried_via|"
499 "footprint|"
500 "graphic|"
501 "hole|"
502 "micro_via|"
503 "pad|"
504 "text|"
505 "track|"
506 "via|"
507 "zone" );
508 }
509 else if( sexprs.top() == wxT( "zone_connection" ) )
510 {
511 tokens = wxT( "none|solid|thermal_reliefs" );
512 }
513 else if( sexprs.top() == wxT( "min_resolved_spokes" ) )
514 {
515 tokens = wxT( "0|1|2|3|4" );
516 }
517 else if( sexprs.top() == wxT( "layer" ) )
518 {
519 tokens = wxT( "inner|outer|\"x\"" );
520 }
521 else if( sexprs.top() == wxT( "severity" ) )
522 {
523 tokens = wxT( "warning|error|ignore|exclusion" );
524 }
525 }
526 else if( context == SEXPR_STRING && !sexprs.empty()
527 && ( sexprs.top() == wxT( "condition" ) || sexprs.top() == wxT( "assertion" ) ) )
528 {
529 m_textEditor->AddText( wxT( "\"" ) );
530 }
531 else if( context == STRING && !sexprs.empty()
532 && ( sexprs.top() == wxT( "condition" ) || sexprs.top() == wxT( "assertion" ) ) )
533 {
534 if( expr_context == STRUCT_REF )
535 {
537 std::set<wxString> propNames;
538
539 for( const PROPERTY_MANAGER::CLASS_INFO& cls : propMgr.GetAllClasses() )
540 {
541 const PROPERTY_LIST& props = propMgr.GetProperties( cls.type );
542
543 for( PROPERTY_BASE* prop : props )
544 {
545 // TODO: It would be nice to replace IsHiddenFromRulesEditor with a nickname
546 // system, so that two different properies don't need to be created. This is
547 // a bigger change than I want to make right now, though.
548 if( prop->IsHiddenFromRulesEditor() )
549 continue;
550
551 wxString ref( prop->Name() );
552 ref.Replace( wxT( " " ), wxT( "_" ) );
553 propNames.insert( ref );
554 }
555 }
556
557 for( const wxString& propName : propNames )
558 tokens += wxT( "|" ) + propName;
559
561
562 for( const wxString& funcSig : functions.GetSignatures() )
563 {
564 if( !funcSig.Contains( "DEPRECATED" ) )
565 tokens += wxT( "|" ) + funcSig;
566 }
567 }
568 else if( expr_context == STRING )
569 {
570 if( m_netClassRegex.Matches( last ) )
571 {
573 std::shared_ptr<NET_SETTINGS>& netSettings = bds.m_NetSettings;
574
575 for( const auto& [name, netclass] : netSettings->GetNetclasses() )
576 tokens += wxT( "|" ) + name;
577 }
578 else if( m_netNameRegex.Matches( last ) )
579 {
580 BOARD* board = m_frame->GetBoard();
581
582 for( const wxString& netnameCandidate : board->GetNetClassAssignmentCandidates() )
583 tokens += wxT( "|" ) + netnameCandidate;
584 }
585 else if( m_typeRegex.Matches( last ) )
586 {
587 tokens = wxT( "Bitmap|"
588 "Dimension|"
589 "Footprint|"
590 "Graphic|"
591 "Group|"
592 "Leader|"
593 "Pad|"
594 "Target|"
595 "Text|"
596 "Text Box|"
597 "Track|"
598 "Via|"
599 "Zone" );
600 }
601 else if( m_viaTypeRegex.Matches( last ) )
602 {
603 tokens = wxT( "Through|"
604 "Blind/buried|"
605 "Micro" );
606 }
607 else if( m_padTypeRegex.Matches( last ) )
608 {
609 tokens = wxT( "Through-hole|"
610 "SMD|"
611 "Edge connector|"
612 "NPTH, mechanical" );
613 }
614 else if( m_pinTypeRegex.Matches( last ) )
615 {
616 tokens = wxT( "Input|"
617 "Output|"
618 "Bidirectional|"
619 "Tri-state|"
620 "Passive|"
621 "Free|"
622 "Unspecified|"
623 "Power input|"
624 "Power output|"
625 "Open collector|"
626 "Open emitter|"
627 "Unconnected" );
628 }
629 else if( m_fabPropRegex.Matches( last ) )
630 {
631 tokens = wxT( "None|"
632 "BGA pad|"
633 "Fiducial, global to board|"
634 "Fiducial, local to footprint|"
635 "Test point pad|"
636 "Heatsink pad|"
637 "Castellated pad" );
638 }
639 else if( m_shapeRegex.Matches( last ) )
640 {
641 tokens = wxT( "Segment|"
642 "Rectangle|"
643 "Arc|"
644 "Circle|"
645 "Polygon|"
646 "Bezier" );
647 }
648 else if( m_padShapeRegex.Matches( last ) )
649 {
650 tokens = wxT( "Circle|"
651 "Rectangle|"
652 "Oval|"
653 "Trapezoid|"
654 "Rounded rectangle|"
655 "Chamfered rectangle|"
656 "Custom" );
657 }
658 else if( m_padConnectionsRegex.Matches( last ) )
659 {
660 tokens = wxT( "Inherited|"
661 "None|"
662 "Solid|"
663 "Thermal reliefs|"
664 "Thermal reliefs for PTH" );
665 }
666 else if( m_zoneConnStyleRegex.Matches( last ) )
667 {
668 tokens = wxT( "Inherited|"
669 "None|"
670 "Solid|"
671 "Thermal reliefs" );
672 }
673 else if( m_lineStyleRegex.Matches( last ) )
674 {
675 tokens = wxT( "Default|"
676 "Solid|"
677 "Dashed|"
678 "Dotted|"
679 "Dash-Dot|"
680 "Dash-Dot-Dot" );
681 }
682 else if( m_hJustRegex.Matches( last ) )
683 {
684 tokens = wxT( "Left|"
685 "Center|"
686 "Right" );
687 }
688 else if( m_vJustRegex.Matches( last ) )
689 {
690 tokens = wxT( "Top|"
691 "Center|"
692 "Bottom" );
693 }
694 }
695 }
696
697 if( !tokens.IsEmpty() )
698 m_scintillaTricks->DoAutocomplete( partial, wxSplit( tokens, '|' ) );
699}
700
701
702void PANEL_SETUP_RULES::OnCompile( wxCommandEvent& event )
703{
705
706 try
707 {
708 std::vector<std::shared_ptr<DRC_RULE>> dummyRules;
709
710 std::function<bool( wxString* )> resolver =
711 [&]( wxString* token ) -> bool
712 {
713 if( m_frame->Prj().TextVarResolver( token ) )
714 return true;
715
716 return false;
717 };
718
719 wxString rulesText = ExpandTextVars( m_textEditor->GetText(), &resolver );
720
721 DRC_RULES_PARSER parser( rulesText, _( "DRC rules" ) );
722
723 parser.Parse( dummyRules, m_errorsReport );
724 }
725 catch( PARSE_ERROR& pe )
726 {
727 wxString msg = wxString::Format( wxT( "%s <a href='%d:%d'>%s</a>%s" ),
728 _( "ERROR:" ),
729 pe.lineNumber,
730 pe.byteIndex,
731 pe.ParseProblem(),
732 wxEmptyString );
733
735 }
736
738}
739
740
741void PANEL_SETUP_RULES::OnErrorLinkClicked( wxHtmlLinkEvent& event )
742{
743 wxString link = event.GetLinkInfo().GetHref();
744 wxArrayString parts;
745 long line = 0, offset = 0;
746
747 wxStringSplit( link, parts, ':' );
748
749 if( parts.size() > 1 )
750 {
751 parts[0].ToLong( &line );
752 parts[1].ToLong( &offset );
753 }
754
755 int pos = m_textEditor->PositionFromLine( line - 1 ) + ( offset - 1 );
756
757 m_textEditor->GotoPos( pos );
758
759 m_textEditor->SetFocus();
760}
761
762
764{
765 wxFileName rulesFile( m_frame->GetDesignRulesPath() );
766
767 if( rulesFile.FileExists() )
768 {
769 wxTextFile file( rulesFile.GetFullPath() );
770
771 if( file.Open() )
772 {
773 for ( wxString str = file.GetFirstLine(); !file.Eof(); str = file.GetNextLine() )
774 {
776 m_textEditor->AddText( str << '\n' );
777 }
778
779 m_textEditor->EmptyUndoBuffer();
780
781 wxCommandEvent dummy;
782 OnCompile( dummy );
783 }
784 }
785 else
786 {
787 m_textEditor->AddText( wxT( "(version 1)\n" ) );
788 }
789
790 m_originalText = m_textEditor->GetText();
791
792 if( m_frame->Prj().IsNullProject() )
793 {
794 m_textEditor->ClearAll();
795 m_textEditor->AddText( _( "Design rules cannot be added without a project" ) );
796 m_textEditor->Disable();
797 }
798
799 return true;
800}
801
802
804{
805 if( m_originalText == m_textEditor->GetText() )
806 return true;
807
808 if( m_frame->Prj().IsNullProject() )
809 return true;
810
811 wxString rulesFilepath = m_frame->GetDesignRulesPath();
812
813 try
814 {
815 if( m_textEditor->SaveFile( rulesFilepath ) )
816 {
817 m_frame->GetBoard()->GetDesignSettings().m_DRCEngine->InitEngine( rulesFilepath );
818 return true;
819 }
820 }
821 catch( PARSE_ERROR& )
822 {
823 // Don't lock them in to the Setup dialog if they have bad rules. They've already
824 // saved them so we can allow an exit.
825 return true;
826 }
827
828 return false;
829}
830
831
832void PANEL_SETUP_RULES::OnSyntaxHelp( wxHyperlinkEvent& aEvent )
833{
834 if( m_helpWindow )
835 {
837 return;
838 }
839 std::vector<wxString> msg;
840 msg.clear();
841
842 wxString t =
843#include "dialogs/panel_setup_rules_help_1clauses.h"
844 ;
845 msg.emplace_back( t );
846 t =
847#include "dialogs/panel_setup_rules_help_2constraints.h"
848 ;
849 msg.emplace_back( t );
850 t =
851#include "dialogs/panel_setup_rules_help_3items.h"
852 ;
853 msg.emplace_back( t );
854 t =
855#include "dialogs/panel_setup_rules_help_4severity_names.h"
856 ;
857 msg.emplace_back( t );
858 t =
859#include "dialogs/panel_setup_rules_help_5examples.h"
860 ;
861 msg.emplace_back( t );
862 t =
863#include "dialogs/panel_setup_rules_help_6notes.h"
864 ;
865 msg.emplace_back( t );
866 t =
867#include "dialogs/panel_setup_rules_help_7properties.h"
868 ;
869 msg.emplace_back( t );
870 t =
871#include "dialogs/panel_setup_rules_help_8expression_functions.h"
872 ;
873 msg.emplace_back( t );
874 t =
875#include "dialogs/panel_setup_rules_help_9more_examples.h"
876 ;
877 msg.emplace_back( t );
878 t =
879#include "dialogs/panel_setup_rules_help_10documentation.h"
880 ;
881 msg.emplace_back( t );
882
883 wxString msg_txt = wxEmptyString;
884
885 for( wxString i : msg )
886 msg_txt << wxGetTranslation( i );
887
888#ifdef __WXMAC__
889 msg_txt.Replace( wxT( "Ctrl+" ), wxT( "Cmd+" ) );
890#endif
891 const wxString& msGg_txt = msg_txt;
892
893 m_helpWindow = new HTML_MESSAGE_BOX( nullptr, _( "Syntax Help" ) );
894 m_helpWindow->SetDialogSizeInDU( 420, 320 );
895
896 wxString html_txt = wxEmptyString;
897 ConvertMarkdown2Html( msGg_txt, html_txt );
898
899 html_txt.Replace( wxS( "<td" ), wxS( "<td valign=top" ) );
900 m_helpWindow->AddHTML_Text( html_txt );
901
903}
904
905
907{
908 if( !m_frame->Prj().IsNullProject() )
909 {
910 wxFileName relFile = aBoard->GetFileName();
911 relFile.SetExt( FILEEXT::DesignRulesFileExtension );
912
913 wxFileName absFile( aBoard->GetProject()->AbsolutePath( relFile.GetFullName() ) );
914
915 if( absFile.FileExists() )
916 {
917 wxTextFile file( absFile.GetFullPath() );
918
919 if( file.Open() )
920 {
921 m_textEditor->ClearAll();
922
923 for ( wxString str = file.GetFirstLine(); !file.Eof(); str = file.GetNextLine() )
924 {
926 m_textEditor->AddText( str << '\n' );
927 }
928
929 m_textEditor->EmptyUndoBuffer();
930
931 wxCommandEvent dummy;
932 OnCompile( dummy );
933 }
934 }
935 }
936}
const char * name
Definition: DXF_plotter.cpp:62
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
std::shared_ptr< DRC_ENGINE > m_DRCEngine
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:317
std::set< wxString > GetNetClassAssignmentCandidates() const
Return the set of netname candidates for netclass assignment.
Definition: board.cpp:2178
const wxString & GetFileName() const
Definition: board.h:354
PROJECT * GetProject() const
Definition: board.h:536
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:1011
APPEARANCE m_Appearance
void Parse(std::vector< std::shared_ptr< DRC_RULE > > &aRules, REPORTER *aReporter)
void SetDialogSizeInDU(int aWidth, int aHeight)
Set the dialog size, using a "logical" value.
void AddHTML_Text(const wxString &message)
Add HTML text (without any change) to message list.
void ShowModeless()
Show a modeless version of the dialog (without an OK button).
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
void SetModified()
Definition: paged_dialog.h:43
static PAGED_DIALOG * GetDialog(wxWindow *aWindow)
Class PANEL_SETUP_RULES_BASE.
STD_BITMAP_BUTTON * m_compileButton
wxStyledTextCtrl * m_textEditor
WX_HTML_REPORT_BOX * m_errorsReport
bool TransferDataToWindow() override
void OnErrorLinkClicked(wxHtmlLinkEvent &event) override
void onScintillaCharAdded(wxStyledTextEvent &aEvent)
void ImportSettingsFrom(BOARD *aBoard)
PCB_EDIT_FRAME * m_frame
void OnContextMenu(wxMouseEvent &event) override
~PANEL_SETUP_RULES() override
HTML_MESSAGE_BOX * m_helpWindow
void OnCompile(wxCommandEvent &event) override
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()
wxString GetDesignRulesPath()
Return the absolute path to the design rules file for the currently-loaded board.
BOARD * GetBoard() const
The main frame for Pcbnew.
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition: pgm_base.cpp:556
virtual bool TextVarResolver(wxString *aToken) const
Definition: project.cpp:75
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.
Definition: project.cpp:372
virtual bool IsNullProject() const
Check if this project is a null project (i.e.
Definition: project.cpp:166
Provide class metadata.Helper macro to map type hashes to names.
Definition: property_mgr.h:85
CLASSES_INFO GetAllClasses()
const PROPERTY_LIST & GetProperties(TYPE_ID aType) const
Return all properties for a specific type.
static PROPERTY_MANAGER & Instance()
Definition: property_mgr.h:87
Add cut/copy/paste, dark theme, autocomplete and brace highlighting to a wxStyleTextCtrl instance.
void DoAutocomplete(const wxString &aPartial, const wxArrayString &aTokens)
void SetBitmap(const wxBitmapBundle &aBmp)
void Clear() override
Delete the stored messages.
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
void Flush()
Build the HTML messages page.
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition: common.cpp:59
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:251
This file is part of the common library.
#define _(s)
static FILENAME_RESOLVER * resolver
Definition: export_idf.cpp:53
static const std::string DesignRulesFileExtension
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:893
see class PGM_BASE
std::vector< PROPERTY_BASE * > PROPERTY_LIST
Definition: property_mgr.h:49
@ RPT_SEVERITY_ERROR
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,...
Definition: ki_exception.h:120
int lineNumber
at which line number, 1 based index.
Definition: ki_exception.h:121
const wxString ParseProblem()
Definition: ki_exception.h:151
int byteIndex
at which byte offset within the line, 1 based index
Definition: ki_exception.h:122
Definition of file extensions used in Kicad.