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>
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 if( std::optional<DRC_RULE_EDITOR_UTILS::SELECTOR_CONDITION> cond =
253 {
254 switch( cond->kind )
255 {
258 m_netSelector->SetSelectedNet( cond->operand );
259 return true;
260
263 m_netclassSelector->SetSelectedNetclass( cond->operand );
264 return true;
265
268 m_areaSelector->SetSelectedArea( cond->operand );
269 return true;
270
271 // The area row writes intersectsArea, so it cannot hold this test
273 break;
274 }
275 }
276
277 // Fallback to custom query
278 wxLogTrace( TRACE_COND, wxS( "[ParseExpression] no match -> CUSTOM" ) );
280 m_customQueryCtrl->SetValue( aExpr );
281
282 return true;
283}
284
285
287{
288 wxString prefix = m_showObjectSelector
289 ? ( GetObjectTarget() == OBJECT_TARGET::OBJECT_A ? "A" : "B" )
290 : "A";
291
292 switch( GetConditionType() )
293 {
295 {
296 wxString net = m_netSelector->GetSelectedNetname();
297
298 if( net.IsEmpty() )
299 return wxEmptyString;
300
301 return prefix + wxString::Format( ".NetName == '%s'", net );
302 }
303
305 {
306 wxString netclass = m_netclassSelector->GetSelectedNetclass();
307
308 if( netclass.IsEmpty() )
309 return wxEmptyString;
310
311 return prefix + wxString::Format( ".hasNetclass('%s')", netclass );
312 }
313
315 {
316 wxString area = m_areaSelector->GetSelectedArea();
317
318 if( area.IsEmpty() )
319 return wxEmptyString;
320
321 return prefix + wxString::Format( ".intersectsArea('%s')", area );
322 }
323
325 return m_customQueryCtrl->GetText();
326
327 default:
328 return wxEmptyString;
329 }
330}
331
332
334{
335 m_deleteBtn->Show( aShow );
336 Layout();
337}
338
339
344
345
346void DRC_RE_CONDITION_ROW_PANEL::onObjectChoice( wxCommandEvent& aEvent )
347{
348 if( m_changeCallback )
350}
351
352
354{
356
357 if( m_changeCallback )
359}
360
361
363{
364 if( m_deleteCallback )
366}
367
368
369void DRC_RE_CONDITION_ROW_PANEL::onValueChanged( wxCommandEvent& aEvent )
370{
371 if( m_changeCallback )
373}
374
375
377{
378 if( m_changeCallback )
380}
381
382
384{
385 m_netSelector->Hide();
386 m_netclassSelector->Hide();
387 m_areaSelector->Hide();
388 m_customQueryCtrl->Hide();
389
390 switch( GetConditionType() )
391 {
393 m_netSelector->Show();
394 break;
395
397 m_netclassSelector->Show();
398 break;
399
401 m_areaSelector->Show();
402 break;
403
405 m_customQueryCtrl->Show();
406 break;
407
408 default:
409 break;
410 }
411
412 Layout();
413
414 // Notify parent to update layout since our size may have changed
415 if( wxWindow* parent = GetParent() )
416 parent->Layout();
417}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:411
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 std::optional< SELECTOR_CONDITION > ParseSelectorCondition(const wxString &aExpr)
Match a condition that a net, netclass or area selector can show.
static constexpr const wxChar * TRACE_COND
#define _(s)