KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drc_re_custom_rule_panel.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
25
26#include <stack>
27
28#include <drc/drc_rule.h>
29#include <drc/drc_rule_parser.h>
30#include <ki_exception.h>
31#include <reporter.h>
32#include <scintilla_tricks.h>
33#include <wx/button.h>
34#include <wx/sizer.h>
35#include <wx/stc/stc.h>
36#include <wx/tipwin.h>
37
39 wxWindow* aParent, std::shared_ptr<DRC_RE_CUSTOM_RULE_CONSTRAINT_DATA> aConstraintData ) :
40 wxPanel( aParent ),
41 m_constraintData( aConstraintData ),
42 m_textCtrl( nullptr ),
43 m_checkSyntaxBtn( nullptr )
44{
45 wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
46
47 m_textCtrl = new wxStyledTextCtrl( this, wxID_ANY );
48 sizer->Add( m_textCtrl, 1, wxEXPAND | wxALL, 5 );
49
50 m_checkSyntaxBtn = new wxButton( this, wxID_ANY, _( "Check Syntax" ) );
51 sizer->Add( m_checkSyntaxBtn, 0, wxALIGN_RIGHT | wxRIGHT | wxBOTTOM, 5 );
52
53 SetSizer( sizer );
54
55 m_scintillaTricks = std::make_unique<SCINTILLA_TRICKS>(
56 m_textCtrl, wxT( "()" ), false,
57 // onAcceptFn
58 []( wxKeyEvent& aEvent )
59 {
60 aEvent.Skip();
61 },
62 // onCharFn
63 [this]( wxStyledTextEvent& aEvent )
64 {
65 onScintillaCharAdded( aEvent );
66 } );
67
68 m_textCtrl->AutoCompSetSeparator( '|' );
69
71}
72
73
77
78
80{
82 m_textCtrl->SetValue( m_constraintData->GetRuleText() );
83
84 return true;
85}
86
87
89{
91 m_constraintData->SetRuleText( m_textCtrl->GetValue() );
92
93 return true;
94}
95
96
97bool DRC_RE_CUSTOM_RULE_PANEL::ValidateInputs( int* aErrorCount, wxString* aValidationMessage )
98{
99 (void) aErrorCount;
100 (void) aValidationMessage;
101 return true;
102}
103
104
106{
107 (void) aContext;
108
109 if( m_constraintData )
110 return m_constraintData->GetRuleText();
111
112 if( m_textCtrl )
113 return m_textCtrl->GetValue();
114
115 return wxEmptyString;
116}
117
118
119void DRC_RE_CUSTOM_RULE_PANEL::onScintillaCharAdded( wxStyledTextEvent& aEvent )
120{
121 if( aEvent.GetModifiers() == wxMOD_CONTROL && aEvent.GetKey() == ' ' )
122 {
123 // Short-cut for do-auto-complete
124 }
125
126 m_textCtrl->SearchAnchor();
127
128 wxString rules = m_textCtrl->GetText();
129 int currentPos = m_textCtrl->GetCurrentPos();
130 int startPos = 0;
131
132 enum class EXPR_CONTEXT_T : int
133 {
134 NONE,
135 STRING,
136 SEXPR_OPEN,
137 SEXPR_TOKEN,
138 };
139
140 std::stack<wxString> sexprs;
141 wxString partial;
142 EXPR_CONTEXT_T context = EXPR_CONTEXT_T::NONE;
143
144 for( int i = startPos; i < currentPos; ++i )
145 {
146 wxChar c = m_textCtrl->GetCharAt( i );
147
148 if( c == '\\' )
149 {
150 i++; // skip escaped char
151 }
152 else if( context == EXPR_CONTEXT_T::STRING )
153 {
154 if( c == '"' )
155 context = EXPR_CONTEXT_T::NONE;
156 }
157 else if( c == '"' )
158 {
159 partial = wxEmptyString;
160 context = EXPR_CONTEXT_T::STRING;
161 }
162 else if( c == '(' )
163 {
164 if( context == EXPR_CONTEXT_T::SEXPR_OPEN && !partial.IsEmpty() )
165 {
166 m_textCtrl->AutoCompCancel();
167 sexprs.push( partial );
168 }
169
170 partial = wxEmptyString;
171 context = EXPR_CONTEXT_T::SEXPR_OPEN;
172 }
173 else if( c == ')' )
174 {
175 if( !sexprs.empty() )
176 sexprs.pop();
177
178 context = EXPR_CONTEXT_T::NONE;
179 }
180 else if( c == ' ' )
181 {
182 if( context == EXPR_CONTEXT_T::SEXPR_OPEN && !partial.IsEmpty() )
183 {
184 m_textCtrl->AutoCompCancel();
185 sexprs.push( partial );
186 context = EXPR_CONTEXT_T::SEXPR_TOKEN;
187 partial = wxEmptyString;
188 continue;
189 }
190
191 context = EXPR_CONTEXT_T::NONE;
192 }
193 else
194 {
195 partial += c;
196 }
197 }
198
199 wxString tokens;
200
201 if( context == EXPR_CONTEXT_T::SEXPR_OPEN )
202 {
203 if( sexprs.empty() )
204 {
205 // Top level - suggest (rule or (version
206 tokens = wxT( "rule|version" );
207 }
208 else if( sexprs.top() == wxT( "rule" ) )
209 {
210 // Inside (rule ...) - suggest sub-expressions
211 tokens = wxT( "condition|constraint|layer|severity" );
212 }
213 else if( sexprs.top() == wxT( "constraint" ) )
214 {
215 // Inside (constraint ...) - suggest constraint parameters
216 tokens = wxT( "min|max|opt" );
217 }
218 }
219 else if( context == EXPR_CONTEXT_T::SEXPR_TOKEN )
220 {
221 if( !sexprs.empty() && sexprs.top() == wxT( "constraint" ) )
222 {
223 // After (constraint - suggest constraint types
224 tokens = wxT( "annular_width|"
225 "assertion|"
226 "clearance|"
227 "connection_width|"
228 "courtyard_clearance|"
229 "creepage|"
230 "diff_pair_gap|"
231 "diff_pair_uncoupled|"
232 "disallow|"
233 "edge_clearance|"
234 "hole_clearance|"
235 "hole_size|"
236 "hole_to_hole|"
237 "length|"
238 "max_error|"
239 "min_resolved_spokes|"
240 "physical_clearance|"
241 "physical_hole_clearance|"
242 "silk_clearance|"
243 "skew|"
244 "text_height|"
245 "text_thickness|"
246 "thermal_relief_gap|"
247 "thermal_spoke_width|"
248 "track_angle|"
249 "track_segment_length|"
250 "track_width|"
251 "via_count|"
252 "via_diameter|"
253 "zone_connection" );
254 }
255 else if( !sexprs.empty() && sexprs.top() == wxT( "layer" ) )
256 {
257 // After (layer - suggest layer keywords
258 tokens = wxT( "inner|outer" );
259 }
260 else if( !sexprs.empty() && sexprs.top() == wxT( "severity" ) )
261 {
262 // After (severity - suggest severity levels
263 tokens = wxT( "error|warning|ignore|exclusion" );
264 }
265 }
266
267 if( !tokens.IsEmpty() )
268 m_scintillaTricks->DoAutocomplete( partial, wxSplit( tokens, '|' ) );
269}
270
271
272void DRC_RE_CUSTOM_RULE_PANEL::onCheckSyntax( wxCommandEvent& aEvent )
273{
274 // Close any existing tip window
275 if( m_tipWindow )
276 {
277 m_tipWindow->Close();
278 m_tipWindow = nullptr;
279 }
280
281 wxString rulesText = m_textCtrl->GetText();
282
283 if( rulesText.Trim().IsEmpty() )
284 {
285#if wxCHECK_VERSION( 3, 3, 2 )
286 m_tipWindow = wxTipWindow::New( this, _( "No rule text to check." ) );
287#else
288 m_tipWindow = new wxTipWindow( this, _( "No rule text to check." ) );
289 m_tipWindow->SetTipWindowPtr( &m_tipWindow );
290#endif
291 return;
292 }
293
294 WX_STRING_REPORTER reporter;
295
296 try
297 {
298 std::vector<std::shared_ptr<DRC_RULE>> dummyRules;
299 DRC_RULES_PARSER parser( rulesText, _( "Custom rule" ) );
300
301 parser.Parse( dummyRules, &reporter );
302 }
303 catch( PARSE_ERROR& pe )
304 {
305 reporter.Report( wxString::Format( _( "ERROR at line %d, column %d: %s" ),
306 pe.lineNumber,
307 pe.byteIndex,
308 pe.ParseProblem() ),
310 }
311
312 wxString message;
313
315 {
316 message = reporter.GetMessages();
317 }
318 else if( reporter.HasMessage() )
319 {
320 message = _( "Syntax check passed with warnings:\n" ) + reporter.GetMessages();
321 }
322 else
323 {
324 message = _( "Syntax OK" );
325 }
326
327#if wxCHECK_VERSION( 3, 3, 2 )
328 m_tipWindow = wxTipWindow::New( this, message );
329#else
330 m_tipWindow = new wxTipWindow( this, message );
331 m_tipWindow->SetTipWindowPtr( &m_tipWindow );
332#endif
333}
void onScintillaCharAdded(wxStyledTextEvent &aEvent)
bool ValidateInputs(int *aErrorCount, wxString *aValidationMessage) override
wxString GenerateRule(const RULE_GENERATION_CONTEXT &aContext) override
std::shared_ptr< DRC_RE_CUSTOM_RULE_CONSTRAINT_DATA > m_constraintData
std::unique_ptr< SCINTILLA_TRICKS > m_scintillaTricks
DRC_RE_CUSTOM_RULE_PANEL(wxWindow *aParent, std::shared_ptr< DRC_RE_CUSTOM_RULE_CONSTRAINT_DATA > aConstraintData)
void onCheckSyntax(wxCommandEvent &aEvent)
void Parse(std::vector< std::shared_ptr< DRC_RULE > > &aRules, REPORTER *aReporter)
virtual bool HasMessageOfSeverity(int aSeverityMask) const
Returns true if the reporter has one or more messages matching the specified severity mask.
Definition reporter.h:143
virtual bool HasMessage() const
Returns true if any messages were reported.
Definition reporter.h:134
A wrapper for reporting to a wxString object.
Definition reporter.h:191
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
Definition reporter.cpp:69
const wxString & GetMessages() const
Definition reporter.cpp:78
#define _(s)
@ NONE
Definition eda_shape.h:69
@ RPT_SEVERITY_ERROR
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