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_rowSizer->Add( m_objectChoice, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2 );
61
62 if( !aShowObjectSelector )
63 m_objectChoice->Hide();
64
65 // Condition type dropdown
66 wxArrayString conditionChoices;
67 conditionChoices.Add( _( "Any" ) );
68 conditionChoices.Add( _( "Net" ) );
69 conditionChoices.Add( _( "Netclass" ) );
70 conditionChoices.Add( _( "Within Area" ) );
71 conditionChoices.Add( _( "Custom Query" ) );
72
73 m_conditionChoice = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, conditionChoices );
74 m_conditionChoice->SetSelection( 0 );
75 m_rowSizer->Add( m_conditionChoice, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2 );
76
77 // Net selector
78 m_netSelector = new NET_SELECTOR( this, wxID_ANY );
79 m_netSelector->SetNetInfo( &aBoard->GetNetInfo() );
80 m_rowSizer->Add( m_netSelector, 1, wxALL | wxEXPAND, 2 );
81 m_netSelector->Hide();
82
83 // Netclass selector
84 m_netclassSelector = new NETCLASS_SELECTOR( this, wxID_ANY );
85 m_netclassSelector->SetBoard( aBoard );
86 m_rowSizer->Add( m_netclassSelector, 1, wxALL | wxEXPAND, 2 );
87 m_netclassSelector->Hide();
88
89 // Area selector
90 m_areaSelector = new AREA_SELECTOR( this, wxID_ANY );
91 m_areaSelector->SetBoard( aBoard );
92 m_rowSizer->Add( m_areaSelector, 1, wxALL | wxEXPAND, 2 );
93 m_areaSelector->Hide();
94
95 m_contentSizer->Add( m_rowSizer, 0, wxEXPAND, 0 );
96
97 // Custom query text control - shown only when Custom Query is selected
98 m_customQueryCtrl = new wxStyledTextCtrl( this, wxID_ANY, wxDefaultPosition, wxSize( -1, 60 ) );
99 m_customQueryCtrl->SetUseTabs( true );
100 m_customQueryCtrl->SetTabWidth( 4 );
101 m_customQueryCtrl->SetIndent( 4 );
102 m_customQueryCtrl->SetTabIndents( true );
103 m_customQueryCtrl->SetBackSpaceUnIndents( true );
104 m_customQueryCtrl->SetViewEOL( false );
105 m_customQueryCtrl->SetViewWhiteSpace( false );
106 m_customQueryCtrl->SetIndentationGuides( true );
107 m_customQueryCtrl->SetReadOnly( false );
108 m_customQueryCtrl->SetUseHorizontalScrollBar( false );
109 m_customQueryCtrl->SetMaxSize( wxSize( -1, 60 ) );
110
111 m_scintillaTricks = std::make_unique<SCINTILLA_TRICKS>(
112 m_customQueryCtrl, wxT( "()" ), false,
113 []( wxKeyEvent& aEvent )
114 {
115 aEvent.Skip();
116 },
117 []( wxStyledTextEvent& aEvent )
118 {
119 } );
120
121 m_contentSizer->Add( m_customQueryCtrl, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5 );
122 m_customQueryCtrl->Hide();
123
124 m_mainSizer->Add( m_contentSizer, 1, wxEXPAND, 0 );
125
126 // Delete button on right side, aligned to top
127 m_deleteBtn = new wxBitmapButton( this, wxID_ANY, KiBitmapBundle( BITMAPS::trash ) );
128 m_deleteBtn->SetToolTip( _( "Remove this condition" ) );
129 m_mainSizer->Add( m_deleteBtn, 0, wxALL | wxALIGN_TOP, 2 );
130
131 wxLogTrace( TRACE_COND, wxS( "[DRC_RE_CONDITION_ROW_PANEL] setting sizer" ) );
132 SetSizer( m_mainSizer );
133
134 // Bind events
137 m_deleteBtn->Bind( wxEVT_BUTTON, &DRC_RE_CONDITION_ROW_PANEL::onDeleteClicked, this );
138 m_netSelector->Bind( FILTERED_ITEM_SELECTED, &DRC_RE_CONDITION_ROW_PANEL::onValueChanged, this );
139 m_netclassSelector->Bind( FILTERED_ITEM_SELECTED, &DRC_RE_CONDITION_ROW_PANEL::onValueChanged, this );
140 m_areaSelector->Bind( FILTERED_ITEM_SELECTED, &DRC_RE_CONDITION_ROW_PANEL::onValueChanged, this );
142
143 wxLogTrace( TRACE_COND, wxS( "[DRC_RE_CONDITION_ROW_PANEL] ctor END" ) );
144}
145
146
148{
149 m_objectChoice->SetSelection( static_cast<int>( aTarget ) );
150}
151
152
157
158
160{
161 m_conditionChoice->SetSelection( static_cast<int>( aType ) );
163}
164
165
170
171
172void DRC_RE_CONDITION_ROW_PANEL::SetValue( const wxString& aValue )
173{
174 switch( GetConditionType() )
175 {
177 m_netSelector->SetSelectedNet( aValue );
178 break;
179
181 m_netclassSelector->SetSelectedNetclass( aValue );
182 break;
183
185 m_areaSelector->SetSelectedArea( aValue );
186 break;
187
189 m_customQueryCtrl->SetValue( aValue );
190 break;
191
192 default:
193 break;
194 }
195}
196
197
199{
200 switch( GetConditionType() )
201 {
203 return m_netSelector->GetSelectedNetname();
204
206 return m_netclassSelector->GetSelectedNetclass();
207
209 return m_areaSelector->GetSelectedArea();
210
212 return m_customQueryCtrl->GetText();
213
214 default:
215 return wxString();
216 }
217}
218
219
221{
222 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] expr='%s'" ), aExpr );
223
224 if( aExpr.IsEmpty() )
225 {
226 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] empty -> ANY" ) );
228 return true;
229 }
230
231 wxString expr = aExpr;
232
233 // Check for object prefix (A. or B.)
234 if( expr.StartsWith( "A." ) )
235 {
237 expr = expr.Mid( 2 );
238 }
239 else if( expr.StartsWith( "B." ) )
240 {
242 expr = expr.Mid( 2 );
243 }
244
245 // Try to match known patterns
246 static wxRegEx netRe( wxT( "^NetName\\s*==\\s*'([^']*)'" ) );
247 static wxRegEx netclassRe1( wxT( "^hasNetclass\\('([^']*)'\\)" ) );
248 static wxRegEx netclassRe2( wxT( "^NetClass\\s*==\\s*'([^']*)'" ) );
249 static wxRegEx areaRe( wxT( "^(?:enclosedByArea|intersectsArea)\\('([^']*)'\\)" ) );
250
251 if( netRe.Matches( expr ) )
252 {
253 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] matched NET pattern" ) );
255 m_netSelector->SetSelectedNet( netRe.GetMatch( expr, 1 ) );
256 return true;
257 }
258 else if( netclassRe1.Matches( expr ) )
259 {
260 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] matched NETCLASS (hasNetclass) pattern" ) );
262 m_netclassSelector->SetSelectedNetclass( netclassRe1.GetMatch( expr, 1 ) );
263 return true;
264 }
265 else if( netclassRe2.Matches( expr ) )
266 {
267 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] matched NETCLASS (NetClass==) pattern" ) );
269 m_netclassSelector->SetSelectedNetclass( netclassRe2.GetMatch( expr, 1 ) );
270 return true;
271 }
272 else if( areaRe.Matches( expr ) )
273 {
274 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] matched WITHIN_AREA pattern" ) );
276 m_areaSelector->SetSelectedArea( areaRe.GetMatch( expr, 1 ) );
277 return true;
278 }
279
280 // Fallback to custom query
281 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] no match -> CUSTOM" ) );
283 m_customQueryCtrl->SetValue( aExpr );
284
285 return true;
286}
287
288
290{
291 wxString prefix = m_showObjectSelector
292 ? ( GetObjectTarget() == OBJECT_TARGET::OBJECT_A ? "A" : "B" )
293 : "A";
294
295 switch( GetConditionType() )
296 {
298 {
299 wxString net = m_netSelector->GetSelectedNetname();
300
301 if( net.IsEmpty() )
302 return wxEmptyString;
303
304 return prefix + wxString::Format( ".NetName == '%s'", net );
305 }
306
308 {
309 wxString netclass = m_netclassSelector->GetSelectedNetclass();
310
311 if( netclass.IsEmpty() )
312 return wxEmptyString;
313
314 return prefix + wxString::Format( ".hasNetclass('%s')", netclass );
315 }
316
318 {
319 wxString area = m_areaSelector->GetSelectedArea();
320
321 if( area.IsEmpty() )
322 return wxEmptyString;
323
324 return prefix + wxString::Format( ".intersectsArea('%s')", area );
325 }
326
328 return m_customQueryCtrl->GetText();
329
330 default:
331 return wxEmptyString;
332 }
333}
334
335
337{
338 m_deleteBtn->Show( aShow );
339 Layout();
340}
341
342
347
348
349void DRC_RE_CONDITION_ROW_PANEL::onObjectChoice( wxCommandEvent& aEvent )
350{
351 if( m_changeCallback )
353}
354
355
357{
359
360 if( m_changeCallback )
362}
363
364
366{
367 if( m_deleteCallback )
369}
370
371
372void DRC_RE_CONDITION_ROW_PANEL::onValueChanged( wxCommandEvent& aEvent )
373{
374 if( m_changeCallback )
376}
377
378
380{
381 if( m_changeCallback )
383}
384
385
387{
388 m_netSelector->Hide();
389 m_netclassSelector->Hide();
390 m_areaSelector->Hide();
391 m_customQueryCtrl->Hide();
392
393 switch( GetConditionType() )
394 {
396 m_netSelector->Show();
397 break;
398
400 m_netclassSelector->Show();
401 break;
402
404 m_areaSelector->Show();
405 break;
406
408 m_customQueryCtrl->Show();
409 break;
410
411 default:
412 break;
413 }
414
415 Layout();
416
417 // Notify parent to update layout since our size may have changed
418 if( wxWindow* parent = GetParent() )
419 parent->Layout();
420}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:372
const NETINFO_LIST & GetNetInfo() const
Definition board.h:1086
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)