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, 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 <board.h>
30#include <bitmaps.h>
31
32#include <wx/sizer.h>
33#include <wx/choice.h>
34#include <wx/bmpbuttn.h>
35#include <wx/stc/stc.h>
36#include <scintilla_tricks.h>
37#include <wx/regex.h>
38#include <wx/log.h>
39
40static constexpr const wxChar* TRACE_COND = wxT( "KI_TRACE_DRC_RULE_EDITOR" );
41
42
44 bool aShowObjectSelector ) :
45 wxPanel( aParent ),
46 m_showObjectSelector( aShowObjectSelector )
47{
48 wxLogTrace( TRACE_COND, wxS( "[DRC_RE_CONDITION_ROW_PANEL] ctor START" ) );
49
50 // Outer horizontal sizer: content on left, delete button on right
51 m_mainSizer = new wxBoxSizer( wxHORIZONTAL );
52
53 // Content sizer holds row of dropdowns and custom query text below
54 m_contentSizer = new wxBoxSizer( wxVERTICAL );
55 m_rowSizer = new wxBoxSizer( wxHORIZONTAL );
56
57 // Object selector (A/B) - only shown for two-object constraints
58 wxArrayString objectChoices;
59 objectChoices.Add( _( "Object A" ) );
60 objectChoices.Add( _( "Object B" ) );
61
62 m_objectChoice = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, objectChoices );
63 m_objectChoice->SetSelection( 0 );
64 m_rowSizer->Add( m_objectChoice, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2 );
65
66 if( !aShowObjectSelector )
67 m_objectChoice->Hide();
68
69 // Condition type dropdown
70 wxArrayString conditionChoices;
71 conditionChoices.Add( _( "Any" ) );
72 conditionChoices.Add( _( "Net" ) );
73 conditionChoices.Add( _( "Netclass" ) );
74 conditionChoices.Add( _( "Within Area" ) );
75 conditionChoices.Add( _( "Custom Query" ) );
76
77 m_conditionChoice = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, conditionChoices );
78 m_conditionChoice->SetSelection( 0 );
79 m_rowSizer->Add( m_conditionChoice, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2 );
80
81 // Net selector
82 m_netSelector = new NET_SELECTOR( this, wxID_ANY );
83 m_netSelector->SetNetInfo( &aBoard->GetNetInfo() );
84 m_rowSizer->Add( m_netSelector, 1, wxALL | wxEXPAND, 2 );
85 m_netSelector->Hide();
86
87 // Netclass selector
88 m_netclassSelector = new NETCLASS_SELECTOR( this, wxID_ANY );
89 m_netclassSelector->SetBoard( aBoard );
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_rowSizer->Add( m_areaSelector, 1, wxALL | wxEXPAND, 2 );
97 m_areaSelector->Hide();
98
99 m_contentSizer->Add( m_rowSizer, 0, wxEXPAND, 0 );
100
101 // Custom query text control - shown only when Custom Query is selected
102 m_customQueryCtrl = new wxStyledTextCtrl( this, wxID_ANY, wxDefaultPosition, wxSize( -1, 60 ) );
103 m_customQueryCtrl->SetUseTabs( true );
104 m_customQueryCtrl->SetTabWidth( 4 );
105 m_customQueryCtrl->SetIndent( 4 );
106 m_customQueryCtrl->SetTabIndents( true );
107 m_customQueryCtrl->SetBackSpaceUnIndents( true );
108 m_customQueryCtrl->SetViewEOL( false );
109 m_customQueryCtrl->SetViewWhiteSpace( false );
110 m_customQueryCtrl->SetIndentationGuides( true );
111 m_customQueryCtrl->SetReadOnly( false );
112 m_customQueryCtrl->SetUseHorizontalScrollBar( false );
113 m_customQueryCtrl->SetMaxSize( wxSize( -1, 60 ) );
114
115 m_scintillaTricks = std::make_unique<SCINTILLA_TRICKS>(
116 m_customQueryCtrl, wxT( "()" ), false,
117 []( wxKeyEvent& aEvent )
118 {
119 aEvent.Skip();
120 },
121 []( wxStyledTextEvent& aEvent )
122 {
123 } );
124
125 m_contentSizer->Add( m_customQueryCtrl, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5 );
126 m_customQueryCtrl->Hide();
127
128 m_mainSizer->Add( m_contentSizer, 1, wxEXPAND, 0 );
129
130 // Delete button on right side, aligned to top
131 m_deleteBtn = new wxBitmapButton( this, wxID_ANY, KiBitmapBundle( BITMAPS::trash ) );
132 m_deleteBtn->SetToolTip( _( "Remove this condition" ) );
133 m_mainSizer->Add( m_deleteBtn, 0, wxALL | wxALIGN_TOP, 2 );
134
135 wxLogTrace( TRACE_COND, wxS( "[DRC_RE_CONDITION_ROW_PANEL] setting sizer" ) );
136 SetSizer( m_mainSizer );
137
138 // Bind events
141 m_deleteBtn->Bind( wxEVT_BUTTON, &DRC_RE_CONDITION_ROW_PANEL::onDeleteClicked, this );
142 m_netSelector->Bind( FILTERED_ITEM_SELECTED, &DRC_RE_CONDITION_ROW_PANEL::onValueChanged, this );
143 m_netclassSelector->Bind( FILTERED_ITEM_SELECTED, &DRC_RE_CONDITION_ROW_PANEL::onValueChanged, this );
144 m_areaSelector->Bind( FILTERED_ITEM_SELECTED, &DRC_RE_CONDITION_ROW_PANEL::onValueChanged, this );
146
147 wxLogTrace( TRACE_COND, wxS( "[DRC_RE_CONDITION_ROW_PANEL] ctor END" ) );
148}
149
150
152{
153 m_objectChoice->SetSelection( static_cast<int>( aTarget ) );
154}
155
156
161
162
164{
165 m_conditionChoice->SetSelection( static_cast<int>( aType ) );
167}
168
169
174
175
176void DRC_RE_CONDITION_ROW_PANEL::SetValue( const wxString& aValue )
177{
178 switch( GetConditionType() )
179 {
181 m_netSelector->SetSelectedNet( aValue );
182 break;
183
185 m_netclassSelector->SetSelectedNetclass( aValue );
186 break;
187
189 m_areaSelector->SetSelectedArea( aValue );
190 break;
191
193 m_customQueryCtrl->SetValue( aValue );
194 break;
195
196 default:
197 break;
198 }
199}
200
201
203{
204 switch( GetConditionType() )
205 {
207 return m_netSelector->GetSelectedNetname();
208
210 return m_netclassSelector->GetSelectedNetclass();
211
213 return m_areaSelector->GetSelectedArea();
214
216 return m_customQueryCtrl->GetText();
217
218 default:
219 return wxString();
220 }
221}
222
223
225{
226 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] expr='%s'" ), aExpr );
227
228 if( aExpr.IsEmpty() )
229 {
230 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] empty -> ANY" ) );
232 return true;
233 }
234
235 wxString expr = aExpr;
236
237 // Check for object prefix (A. or B.)
238 if( expr.StartsWith( "A." ) )
239 {
241 expr = expr.Mid( 2 );
242 }
243 else if( expr.StartsWith( "B." ) )
244 {
246 expr = expr.Mid( 2 );
247 }
248
249 // Try to match known patterns
250 wxRegEx netRe( wxT( "^NetName\\s*==\\s*'([^']*)'" ) );
251 wxRegEx netclassRe1( wxT( "^hasNetclass\\('([^']*)'\\)" ) );
252 wxRegEx netclassRe2( wxT( "^NetClass\\s*==\\s*'([^']*)'" ) );
253 wxRegEx areaRe( wxT( "^(?:enclosedByArea|intersectsArea)\\('([^']*)'\\)" ) );
254
255 if( netRe.Matches( expr ) )
256 {
257 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] matched NET pattern" ) );
259 m_netSelector->SetSelectedNet( netRe.GetMatch( expr, 1 ) );
260 return true;
261 }
262 else if( netclassRe1.Matches( expr ) )
263 {
264 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] matched NETCLASS (hasNetclass) pattern" ) );
266 m_netclassSelector->SetSelectedNetclass( netclassRe1.GetMatch( expr, 1 ) );
267 return true;
268 }
269 else if( netclassRe2.Matches( expr ) )
270 {
271 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] matched NETCLASS (NetClass==) pattern" ) );
273 m_netclassSelector->SetSelectedNetclass( netclassRe2.GetMatch( expr, 1 ) );
274 return true;
275 }
276 else if( areaRe.Matches( expr ) )
277 {
278 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] matched WITHIN_AREA pattern" ) );
280 m_areaSelector->SetSelectedArea( areaRe.GetMatch( expr, 1 ) );
281 return true;
282 }
283
284 // Fallback to custom query
285 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] no match -> CUSTOM" ) );
287 m_customQueryCtrl->SetValue( aExpr );
288
289 return true;
290}
291
292
294{
295 wxString prefix = m_showObjectSelector
296 ? ( GetObjectTarget() == OBJECT_TARGET::OBJECT_A ? "A" : "B" )
297 : "A";
298
299 switch( GetConditionType() )
300 {
302 {
303 wxString net = m_netSelector->GetSelectedNetname();
304
305 if( net.IsEmpty() )
306 return wxEmptyString;
307
308 return prefix + wxString::Format( ".NetName == '%s'", net );
309 }
310
312 {
313 wxString netclass = m_netclassSelector->GetSelectedNetclass();
314
315 if( netclass.IsEmpty() )
316 return wxEmptyString;
317
318 return prefix + wxString::Format( ".hasNetclass('%s')", netclass );
319 }
320
322 {
323 wxString area = m_areaSelector->GetSelectedArea();
324
325 if( area.IsEmpty() )
326 return wxEmptyString;
327
328 return prefix + wxString::Format( ".intersectsArea('%s')", area );
329 }
330
332 return m_customQueryCtrl->GetText();
333
334 default:
335 return wxEmptyString;
336 }
337}
338
339
341{
342 m_deleteBtn->Show( aShow );
343 Layout();
344}
345
346
351
352
353void DRC_RE_CONDITION_ROW_PANEL::onObjectChoice( wxCommandEvent& aEvent )
354{
355 if( m_changeCallback )
357}
358
359
361{
363
364 if( m_changeCallback )
366}
367
368
370{
371 if( m_deleteCallback )
373}
374
375
376void DRC_RE_CONDITION_ROW_PANEL::onValueChanged( wxCommandEvent& aEvent )
377{
378 if( m_changeCallback )
380}
381
382
384{
385 if( m_changeCallback )
387}
388
389
391{
392 m_netSelector->Hide();
393 m_netclassSelector->Hide();
394 m_areaSelector->Hide();
395 m_customQueryCtrl->Hide();
396
397 switch( GetConditionType() )
398 {
400 m_netSelector->Show();
401 break;
402
404 m_netclassSelector->Show();
405 break;
406
408 m_areaSelector->Show();
409 break;
410
412 m_customQueryCtrl->Show();
413 break;
414
415 default:
416 break;
417 }
418
419 Layout();
420
421 // Notify parent to update layout since our size may have changed
422 if( wxWindow* parent = GetParent() )
423 parent->Layout();
424}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:110
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
const NETINFO_LIST & GetNetInfo() const
Definition board.h:996
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)