KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drc_re_condition_row_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, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <board.h>
26#include <bitmaps.h>
27
28#include <wx/sizer.h>
29#include <wx/choice.h>
30#include <wx/bmpbuttn.h>
31#include <wx/stc/stc.h>
32#include <scintilla_tricks.h>
33#include <wx/regex.h>
34#include <wx/log.h>
35
36static constexpr const wxChar* TRACE_COND = wxT( "KI_TRACE_DRC_RULE_EDITOR" );
37
38
40 bool aShowObjectSelector ) :
41 wxPanel( aParent ),
42 m_showObjectSelector( aShowObjectSelector )
43{
44 wxLogTrace( TRACE_COND, wxS( "[DRC_RE_CONDITION_ROW_PANEL] ctor START" ) );
45
46 // Outer horizontal sizer: content on left, delete button on right
47 m_mainSizer = new wxBoxSizer( wxHORIZONTAL );
48
49 // Content sizer holds row of dropdowns and custom query text below
50 m_contentSizer = new wxBoxSizer( wxVERTICAL );
51 m_rowSizer = new wxBoxSizer( wxHORIZONTAL );
52
53 // Object selector (A/B) - only shown for two-object constraints
54 wxArrayString objectChoices;
55 objectChoices.Add( _( "Object A" ) );
56 objectChoices.Add( _( "Object B" ) );
57
58 m_objectChoice = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, objectChoices );
59 m_objectChoice->SetSelection( 0 );
60 m_objectChoice->SetToolTip( _( "Which of the two compared objects this condition applies to" ) );
61 m_rowSizer->Add( m_objectChoice, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2 );
62
63 if( !aShowObjectSelector )
64 m_objectChoice->Hide();
65
66 // Condition type dropdown
67 wxArrayString conditionChoices;
68 conditionChoices.Add( _( "Any" ) );
69 conditionChoices.Add( _( "Net" ) );
70 conditionChoices.Add( _( "Netclass" ) );
71 conditionChoices.Add( _( "Within Area" ) );
72 conditionChoices.Add( _( "Custom Query" ) );
73
74 m_conditionChoice = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, conditionChoices );
75 m_conditionChoice->SetSelection( 0 );
76 m_conditionChoice->SetToolTip( _( "How items are matched by this condition" ) );
77 m_rowSizer->Add( m_conditionChoice, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2 );
78
79 // Net selector
80 m_netSelector = new NET_SELECTOR( this, wxID_ANY );
81 m_netSelector->SetNetInfo( &aBoard->GetNetInfo() );
82 m_netSelector->SetToolTip( _( "Net an item must belong to" ) );
83 m_rowSizer->Add( m_netSelector, 1, wxALL | wxEXPAND, 2 );
84 m_netSelector->Hide();
85
86 // Netclass selector
87 m_netclassSelector = new NETCLASS_SELECTOR( this, wxID_ANY );
88 m_netclassSelector->SetBoard( aBoard );
89 m_netclassSelector->SetToolTip( _( "Net class an item must belong to" ) );
90 m_rowSizer->Add( m_netclassSelector, 1, wxALL | wxEXPAND, 2 );
91 m_netclassSelector->Hide();
92
93 // Area selector
94 m_areaSelector = new AREA_SELECTOR( this, wxID_ANY );
95 m_areaSelector->SetBoard( aBoard );
96 m_areaSelector->SetToolTip( _( "Rule area an item must be inside" ) );
97 m_rowSizer->Add( m_areaSelector, 1, wxALL | wxEXPAND, 2 );
98 m_areaSelector->Hide();
99
100 m_contentSizer->Add( m_rowSizer, 0, wxEXPAND, 0 );
101
102 // Custom query text control - shown only when Custom Query is selected
103 m_customQueryCtrl = new wxStyledTextCtrl( this, wxID_ANY, wxDefaultPosition, wxSize( -1, 60 ) );
104 m_customQueryCtrl->SetToolTip( _( "Custom match expression in the DRC rule language" ) );
105 m_customQueryCtrl->SetUseTabs( true );
106 m_customQueryCtrl->SetTabWidth( 4 );
107 m_customQueryCtrl->SetIndent( 4 );
108 m_customQueryCtrl->SetTabIndents( true );
109 m_customQueryCtrl->SetBackSpaceUnIndents( true );
110 m_customQueryCtrl->SetViewEOL( false );
111 m_customQueryCtrl->SetViewWhiteSpace( false );
112 m_customQueryCtrl->SetIndentationGuides( true );
113 m_customQueryCtrl->SetReadOnly( false );
114 m_customQueryCtrl->SetUseHorizontalScrollBar( false );
115 m_customQueryCtrl->SetMaxSize( wxSize( -1, 60 ) );
116
117 m_scintillaTricks = std::make_unique<SCINTILLA_TRICKS>(
118 m_customQueryCtrl, wxT( "()" ), false,
119 []( wxKeyEvent& aEvent )
120 {
121 aEvent.Skip();
122 },
123 []( wxStyledTextEvent& aEvent )
124 {
125 } );
126
127 m_contentSizer->Add( m_customQueryCtrl, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5 );
128 m_customQueryCtrl->Hide();
129
130 m_mainSizer->Add( m_contentSizer, 1, wxEXPAND, 0 );
131
132 // Delete button on right side, aligned to top
133 m_deleteBtn = new wxBitmapButton( this, wxID_ANY, KiBitmapBundle( BITMAPS::trash ) );
134 m_deleteBtn->SetToolTip( _( "Remove this condition" ) );
135 m_mainSizer->Add( m_deleteBtn, 0, wxALL | wxALIGN_TOP, 2 );
136
137 wxLogTrace( TRACE_COND, wxS( "[DRC_RE_CONDITION_ROW_PANEL] setting sizer" ) );
138 SetSizer( m_mainSizer );
139
140 // Bind events
143 m_deleteBtn->Bind( wxEVT_BUTTON, &DRC_RE_CONDITION_ROW_PANEL::onDeleteClicked, this );
144 m_netSelector->Bind( FILTERED_ITEM_SELECTED, &DRC_RE_CONDITION_ROW_PANEL::onValueChanged, this );
145 m_netclassSelector->Bind( FILTERED_ITEM_SELECTED, &DRC_RE_CONDITION_ROW_PANEL::onValueChanged, this );
146 m_areaSelector->Bind( FILTERED_ITEM_SELECTED, &DRC_RE_CONDITION_ROW_PANEL::onValueChanged, this );
148
149 wxLogTrace( TRACE_COND, wxS( "[DRC_RE_CONDITION_ROW_PANEL] ctor END" ) );
150}
151
152
154{
155 m_objectChoice->SetSelection( static_cast<int>( aTarget ) );
156}
157
158
163
164
166{
167 m_conditionChoice->SetSelection( static_cast<int>( aType ) );
169}
170
171
176
177
178void DRC_RE_CONDITION_ROW_PANEL::SetValue( const wxString& aValue )
179{
180 switch( GetConditionType() )
181 {
183 m_netSelector->SetSelectedNet( aValue );
184 break;
185
187 m_netclassSelector->SetSelectedNetclass( aValue );
188 break;
189
191 m_areaSelector->SetSelectedArea( aValue );
192 break;
193
195 m_customQueryCtrl->SetValue( aValue );
196 break;
197
198 default:
199 break;
200 }
201}
202
203
205{
206 switch( GetConditionType() )
207 {
209 return m_netSelector->GetSelectedNetname();
210
212 return m_netclassSelector->GetSelectedNetclass();
213
215 return m_areaSelector->GetSelectedArea();
216
218 return m_customQueryCtrl->GetText();
219
220 default:
221 return wxString();
222 }
223}
224
225
227{
228 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] expr='%s'" ), aExpr );
229
230 if( aExpr.IsEmpty() )
231 {
232 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] empty -> ANY" ) );
234 return true;
235 }
236
237 wxString expr = aExpr;
238
239 // Check for object prefix (A. or B.)
240 if( expr.StartsWith( "A." ) )
241 {
243 expr = expr.Mid( 2 );
244 }
245 else if( expr.StartsWith( "B." ) )
246 {
248 expr = expr.Mid( 2 );
249 }
250
251 // Try to match known patterns
252 static wxRegEx netRe( wxT( "^NetName\\s*==\\s*'([^']*)'" ) );
253 static wxRegEx netclassRe1( wxT( "^hasNetclass\\('([^']*)'\\)" ) );
254 static wxRegEx netclassRe2( wxT( "^NetClass\\s*==\\s*'([^']*)'" ) );
255 static wxRegEx areaRe( wxT( "^(?:enclosedByArea|intersectsArea)\\('([^']*)'\\)" ) );
256
257 if( netRe.Matches( expr ) )
258 {
259 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] matched NET pattern" ) );
261 m_netSelector->SetSelectedNet( netRe.GetMatch( expr, 1 ) );
262 return true;
263 }
264 else if( netclassRe1.Matches( expr ) )
265 {
266 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] matched NETCLASS (hasNetclass) pattern" ) );
268 m_netclassSelector->SetSelectedNetclass( netclassRe1.GetMatch( expr, 1 ) );
269 return true;
270 }
271 else if( netclassRe2.Matches( expr ) )
272 {
273 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] matched NETCLASS (NetClass==) pattern" ) );
275 m_netclassSelector->SetSelectedNetclass( netclassRe2.GetMatch( expr, 1 ) );
276 return true;
277 }
278 else if( areaRe.Matches( expr ) )
279 {
280 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] matched WITHIN_AREA pattern" ) );
282 m_areaSelector->SetSelectedArea( areaRe.GetMatch( expr, 1 ) );
283 return true;
284 }
285
286 // Fallback to custom query
287 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] no match -> CUSTOM" ) );
289 m_customQueryCtrl->SetValue( aExpr );
290
291 return true;
292}
293
294
296{
297 wxString prefix = m_showObjectSelector
298 ? ( GetObjectTarget() == OBJECT_TARGET::OBJECT_A ? "A" : "B" )
299 : "A";
300
301 switch( GetConditionType() )
302 {
304 {
305 wxString net = m_netSelector->GetSelectedNetname();
306
307 if( net.IsEmpty() )
308 return wxEmptyString;
309
310 return prefix + wxString::Format( ".NetName == '%s'", net );
311 }
312
314 {
315 wxString netclass = m_netclassSelector->GetSelectedNetclass();
316
317 if( netclass.IsEmpty() )
318 return wxEmptyString;
319
320 return prefix + wxString::Format( ".hasNetclass('%s')", netclass );
321 }
322
324 {
325 wxString area = m_areaSelector->GetSelectedArea();
326
327 if( area.IsEmpty() )
328 return wxEmptyString;
329
330 return prefix + wxString::Format( ".intersectsArea('%s')", area );
331 }
332
334 return m_customQueryCtrl->GetText();
335
336 default:
337 return wxEmptyString;
338 }
339}
340
341
343{
344 m_deleteBtn->Show( aShow );
345 Layout();
346}
347
348
353
354
355void DRC_RE_CONDITION_ROW_PANEL::onObjectChoice( wxCommandEvent& aEvent )
356{
357 if( m_changeCallback )
359}
360
361
363{
365
366 if( m_changeCallback )
368}
369
370
372{
373 if( m_deleteCallback )
375}
376
377
378void DRC_RE_CONDITION_ROW_PANEL::onValueChanged( wxCommandEvent& aEvent )
379{
380 if( m_changeCallback )
382}
383
384
386{
387 if( m_changeCallback )
389}
390
391
393{
394 m_netSelector->Hide();
395 m_netclassSelector->Hide();
396 m_areaSelector->Hide();
397 m_customQueryCtrl->Hide();
398
399 switch( GetConditionType() )
400 {
402 m_netSelector->Show();
403 break;
404
406 m_netclassSelector->Show();
407 break;
408
410 m_areaSelector->Show();
411 break;
412
414 m_customQueryCtrl->Show();
415 break;
416
417 default:
418 break;
419 }
420
421 Layout();
422
423 // Notify parent to update layout since our size may have changed
424 if( wxWindow* parent = GetParent() )
425 parent->Layout();
426}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
const NETINFO_LIST & GetNetInfo() const
Definition board.h:1215
void SetValue(const wxString &aValue)
DRC_RE_CONDITION_ROW_PANEL(wxWindow *aParent, BOARD *aBoard, bool aShowObjectSelector)
wxString BuildExpression() const
Build a condition expression from the row's current state.
void SetConditionType(CONDITION_TYPE aType)
std::unique_ptr< SCINTILLA_TRICKS > m_scintillaTricks
bool ParseExpression(const wxString &aExpr)
Parse a condition expression and set the row's state.
void onCustomQueryChanged(wxStyledTextEvent &aEvent)
void onConditionChoice(wxCommandEvent &aEvent)
void onObjectChoice(wxCommandEvent &aEvent)
void onValueChanged(wxCommandEvent &aEvent)
void onDeleteClicked(wxCommandEvent &aEvent)
void SetObjectTarget(OBJECT_TARGET aTarget)
static constexpr const wxChar * TRACE_COND
#define _(s)