KiCad PCB EDA Suite
panel_setup_buses.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) 2018 CERN
5 * Copyright (C) 2021-2022 KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Jon Evans <[email protected]>
7 *
8 * This program is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include <widgets/wx_grid.h>
24#include <confirm.h>
25#include <sch_edit_frame.h>
26#include <schematic.h>
28#include "grid_tricks.h"
29
31 PANEL_SETUP_BUSES_BASE( aWindow ),
32 m_frame( aFrame ),
33 m_lastAlias( 0 ),
34 m_membersGridDirty( false ),
35 m_errorGrid( nullptr ),
36 m_errorRow( -1 )
37{
39
44
45 m_source->SetFont( KIUI::GetInfoFont( aWindow ) );
46
47 m_aliasesGrid->PushEventHandler( new GRID_TRICKS( m_aliasesGrid,
48 [this]( wxCommandEvent& aEvent )
49 {
50 OnAddAlias( aEvent );
51 } ) );
52
53 m_membersGrid->PushEventHandler( new GRID_TRICKS( m_membersGrid,
54 [this]( wxCommandEvent& aEvent )
55 {
56 wxIdleEvent dummy;
58
59 OnAddMember( aEvent );
60 } ) );
61
62 // wxFormBuilder doesn't include this event...
63 m_aliasesGrid->Connect( wxEVT_GRID_CELL_CHANGING,
65 nullptr, this );
66 m_membersGrid->Connect( wxEVT_GRID_CELL_CHANGING,
68 nullptr, this );
69
70 Layout();
71}
72
73
75{
76 // Delete the GRID_TRICKS.
77 m_aliasesGrid->PopEventHandler( true );
78 m_membersGrid->PopEventHandler( true );
79
80 m_aliasesGrid->Disconnect( wxEVT_GRID_CELL_CHANGING,
82 nullptr, this );
83 m_membersGrid->Disconnect( wxEVT_GRID_CELL_CHANGING,
85 nullptr, this );
86}
87
89{
90 auto contains =
91 [&]( const std::shared_ptr<BUS_ALIAS>& alias ) -> bool
92 {
93 wxString aName = alias->GetName();
94 std::vector<wxString> aMembers = alias->Members();
95
96 std::sort( aMembers.begin(), aMembers.end() );
97
98 for( const std::shared_ptr<BUS_ALIAS>& candidate : m_aliases )
99 {
100 wxString bName = candidate->GetName();
101 std::vector<wxString> bMembers = candidate->Members();
102
103 std::sort( bMembers.begin(), bMembers.end() );
104
105 if( aName == bName && aMembers == bMembers )
106 return true;
107 }
108
109 return false;
110 };
111
112 SCH_SCREENS screens( m_frame->Schematic().Root() );
113
114 // collect aliases from each open sheet
115 for( SCH_SCREEN* screen = screens.GetFirst(); screen != nullptr; screen = screens.GetNext() )
116 {
117 for( const std::shared_ptr<BUS_ALIAS>& alias : screen->GetBusAliases() )
118 {
119 if( !contains( alias ) )
120 m_aliases.push_back( alias->Clone() );
121 }
122 }
123
124 int ii = 0;
125
127 m_aliasesGrid->AppendRows( m_aliases.size() );
128
129 for( const std::shared_ptr<BUS_ALIAS>& alias : m_aliases )
130 m_aliasesGrid->SetCellValue( ii++, 0, alias->GetName() );
131
132 m_membersBook->SetSelection( 1 );
133
134 return true;
135}
136
137
139{
141 return false;
142
143 // Copy names back just in case they didn't get caught on the GridCellChanging event
144 for( int ii = 0; ii < m_aliasesGrid->GetNumberRows(); ++ii )
145 m_aliases[ii]->SetName( m_aliasesGrid->GetCellValue( ii, 0 ) );
146
147 SCH_SCREENS screens( m_frame->Schematic().Root() );
148
149 for( SCH_SCREEN* screen = screens.GetFirst(); screen != nullptr; screen = screens.GetNext() )
150 screen->ClearBusAliases();
151
152 for( const std::shared_ptr<BUS_ALIAS>& alias : m_aliases )
153 alias->GetParent()->AddBusAlias( alias );
154
155 return true;
156}
157
158
159void PANEL_SETUP_BUSES::OnAddAlias( wxCommandEvent& aEvent )
160{
162 return;
163
164 // New aliases get stored on the currently visible sheet
165 m_aliases.push_back( std::make_shared<BUS_ALIAS>( m_frame->GetScreen() ) );
166
167 int row = m_aliasesGrid->GetNumberRows();
168 m_aliasesGrid->AppendRows();
169
170 m_aliasesGrid->MakeCellVisible( row, 0 );
171 m_aliasesGrid->SetGridCursor( row, 0 );
172
173 m_aliasesGrid->EnableCellEditControl( true );
174 m_aliasesGrid->ShowCellEditControl();
175}
176
177
178void PANEL_SETUP_BUSES::OnDeleteAlias( wxCommandEvent& aEvent )
179{
181 return;
182
183 int curRow = m_aliasesGrid->GetGridCursorRow();
184
185 if( curRow < 0 )
186 return;
187
188 // Clear the members grid first so we don't try to write it back to a deleted alias
190 m_lastAlias = -1;
191 m_lastAliasName = wxEmptyString;
192
193 m_aliases.erase( m_aliases.begin() + curRow );
194
195 m_aliasesGrid->DeleteRows( curRow, 1 );
196
197 if( m_aliasesGrid->GetNumberRows() > 0 )
198 {
199 m_aliasesGrid->MakeCellVisible( std::max( 0, curRow-1 ), 0 );
200 m_aliasesGrid->SetGridCursor( std::max( 0, curRow-1 ), 0 );
201 }
202}
203
204
205void PANEL_SETUP_BUSES::OnAddMember( wxCommandEvent& aEvent )
206{
208 return;
209
210 int row = m_membersGrid->GetNumberRows();
211 m_membersGrid->AppendRows();
212
213 m_membersGrid->MakeCellVisible( row, 0 );
214 m_membersGrid->SetGridCursor( row, 0 );
215
216 m_membersGrid->EnableCellEditControl( true );
217 m_membersGrid->ShowCellEditControl();
218}
219
220
221void PANEL_SETUP_BUSES::OnRemoveMember( wxCommandEvent& aEvent )
222{
224 return;
225
226 int curRow = m_membersGrid->GetGridCursorRow();
227
228 if( curRow < 0 )
229 return;
230
231 m_membersGrid->DeleteRows( curRow, 1 );
232
233 // Update the member list of the current bus alias from the members grid
234 const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[ m_lastAlias ];
235 alias->Members().clear();
236
237 for( int ii = 0; ii < m_membersGrid->GetNumberRows(); ++ii )
238 alias->Members().push_back( m_membersGrid->GetCellValue( ii, 0 ) );
239
240 m_membersGridDirty = true;
241
242 if( m_membersGrid->GetNumberRows() > 0 )
243 {
244 m_membersGrid->MakeCellVisible( std::max( 0, curRow-1 ), 0 );
245 m_membersGrid->SetGridCursor( std::max( 0, curRow-1 ), 0 );
246 }
247}
248
249
251{
252 int row = event.GetRow();
253
254 if( row >= 0 )
255 {
256 wxString name = event.GetString();
257
258 for( int ii = 0; ii < m_aliasesGrid->GetNumberRows(); ++ii )
259 {
260 if( ii == event.GetRow() )
261 continue;
262
263 if( name == m_aliasesGrid->GetCellValue( ii, 0 )
264 && m_aliases[ row ]->GetParent() == m_aliases[ ii ]->GetParent() )
265 {
266 m_errorMsg = wxString::Format( _( "Alias name '%s' already in use." ), name );
268 m_errorRow = row;
269
270 event.Veto();
271 return;
272 }
273 }
274
275 m_aliases[ row ]->SetName( name );
276 }
277}
278
279
281{
282 int row = event.GetRow();
283
284 if( row >= 0 )
285 {
286 wxString name = event.GetString();
287
288 if( name.IsEmpty() )
289 {
290 m_errorMsg = _( "Member net/alias name cannot be empty." );
292 m_errorRow = event.GetRow();
293
294 event.Veto();
295 return;
296 }
297
298 const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[ m_lastAlias ];
299
300 alias->Members().clear();
301
302 for( int ii = 0; ii < m_membersGrid->GetNumberRows(); ++ii )
303 {
304 if( ii == row )
305 {
306 // Parse a space-separated list and add each one
307 wxStringTokenizer tok( name, " " );
308
309 while( tok.HasMoreTokens() )
310 alias->Members().push_back( tok.GetNextToken() );
311 }
312 else
313 {
314 alias->Members().push_back( m_membersGrid->GetCellValue( ii, 0 ) );
315 }
316 }
317
318 m_membersGridDirty = true;
319 Bind( wxEVT_IDLE, &PANEL_SETUP_BUSES::reloadMembersGridOnIdle, this );
320 }
321}
322
323
325{
326 if( m_lastAlias >= 0 && m_lastAlias < m_aliasesGrid->GetNumberRows() )
327 {
328 const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[ m_lastAlias ];
329 wxString source;
330 wxString membersLabel;
331
332 if( alias->GetParent() )
333 {
334 wxFileName sheet_name( alias->GetParent()->GetFileName() );
335 source.Printf( wxS( "(" ) + sheet_name.GetFullName() + wxS( ")" ) );
336 }
337
338 membersLabel.Printf( m_membersLabelTemplate, m_lastAliasName );
339
340 m_source->SetLabel( source );
341 m_membersLabel->SetLabel( membersLabel );
342
344 m_membersGrid->AppendRows( alias->Members().size() );
345
346 int ii = 0;
347
348 for( const wxString& member : alias->Members() )
349 m_membersGrid->SetCellValue( ii++, 0, member );
350 }
351
352 m_membersGridDirty = false;
353}
354
355
357{
360
361 Unbind( wxEVT_IDLE, &PANEL_SETUP_BUSES::reloadMembersGridOnIdle, this );
362}
363
364
365
366void PANEL_SETUP_BUSES::OnSizeGrid( wxSizeEvent& event )
367{
368 auto setColSize =
369 []( WX_GRID* grid )
370 {
371 int colSize = std::max( grid->GetClientSize().x, grid->GetVisibleWidth( 0 ) );
372
373 if( grid->GetColSize( 0 ) != colSize )
374 grid->SetColSize( 0, colSize );
375 };
376
377 setColSize( m_aliasesGrid );
378 setColSize( m_membersGrid );
379
380 // Always propagate for a grid repaint (needed if the height changes, as well as width)
381 event.Skip();
382}
383
384
385void PANEL_SETUP_BUSES::OnUpdateUI( wxUpdateUIEvent& event )
386{
387 // Handle a grid error. This is delayed to OnUpdateUI so that we can change focus
388 // even when the original validation was triggered from a killFocus event.
389 if( !m_errorMsg.IsEmpty() )
390 {
391 // We will re-enter this routine when the error dialog is displayed, so make
392 // sure we don't keep putting up more dialogs.
393 wxString errorMsg = m_errorMsg;
394 m_errorMsg = wxEmptyString;
395
396 DisplayErrorMessage( this, errorMsg );
397
398 m_errorGrid->SetFocus();
399 m_errorGrid->MakeCellVisible( m_errorRow, 0 );
400 m_errorGrid->SetGridCursor( m_errorRow, 0 );
401
402 m_errorGrid->EnableCellEditControl( true );
403 m_errorGrid->ShowCellEditControl();
404
405 return;
406 }
407
408 if( !m_membersGrid->IsCellEditControlShown() )
409 {
410 int row = -1;
411 wxString aliasName;
412
413 if( m_aliasesGrid->IsCellEditControlShown() )
414 {
415 row = m_aliasesGrid->GetGridCursorRow();
416 wxGridCellEditor* cellEditor = m_aliasesGrid->GetCellEditor( row, 0 );
417
418 if( wxTextEntry* txt = dynamic_cast<wxTextEntry*>( cellEditor->GetControl() ) )
419 aliasName = txt->GetValue();
420
421 cellEditor->DecRef();
422 }
423 else if( m_aliasesGrid->GetGridCursorRow() >= 0 )
424 {
425 row = m_aliasesGrid->GetGridCursorRow();
426 aliasName = m_aliasesGrid->GetCellValue( row, 0 );
427 }
428 else if( m_lastAlias >= 0 && m_lastAlias < m_aliasesGrid->GetNumberRows() )
429 {
430 row = m_lastAlias;
431 aliasName = m_lastAliasName;
432 }
433
434 if( row < 0 )
435 {
436 m_membersBook->SetSelection( 1 );
437 }
438 else if( row != m_lastAlias || aliasName != m_lastAliasName )
439 {
440 m_lastAlias = row;
441 m_lastAliasName = aliasName;
442
443 m_membersBook->SetSelection( 0 );
444 m_membersBook->GetPage( 0 )->Layout();
445
446 const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[ row ];
447 alias->SetName( aliasName );
448
449 m_membersGridDirty = true;
450 Bind( wxEVT_IDLE, &PANEL_SETUP_BUSES::reloadMembersGridOnIdle, this );
451 }
452 }
453}
454
455
const char * name
Definition: DXF_plotter.cpp:56
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:105
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition: grid_tricks.h:61
Class PANEL_SETUP_BUSES_BASE.
STD_BITMAP_BUTTON * m_addAlias
STD_BITMAP_BUTTON * m_addMember
STD_BITMAP_BUTTON * m_deleteAlias
STD_BITMAP_BUTTON * m_removeMember
void OnAliasesGridCellChanging(wxGridEvent &event)
std::vector< std::shared_ptr< BUS_ALIAS > > m_aliases
void reloadMembersGridOnIdle(wxIdleEvent &aEvent)
void OnDeleteAlias(wxCommandEvent &aEvent) override
void OnUpdateUI(wxUpdateUIEvent &event) override
SCH_EDIT_FRAME * m_frame
void OnSizeGrid(wxSizeEvent &event) override
PANEL_SETUP_BUSES(wxWindow *aWindow, SCH_EDIT_FRAME *aFrame)
bool TransferDataToWindow() override
wxString m_membersLabelTemplate
void OnRemoveMember(wxCommandEvent &aEvent) override
void OnMemberGridCellChanging(wxGridEvent &event)
bool TransferDataFromWindow() override
void OnAddMember(wxCommandEvent &aEvent) override
void OnAddAlias(wxCommandEvent &aEvent) override
SCH_SHEET & Root() const
Definition: schematic.h:91
Schematic editor (Eeschema) main window.
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
SCHEMATIC & Schematic() const
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition: sch_screen.h:656
SCH_SCREEN * GetNext()
SCH_SCREEN * GetFirst()
void SetBitmap(const wxBitmap &aBmp)
void ClearRows()
wxWidgets recently added an ASSERT which fires if the position is greater than or equal to the number...
Definition: wx_grid.h:139
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:423
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:325
This file is part of the common library.
#define _(s)
wxFont GetInfoFont(wxWindow *aWindow)
Definition: ui_common.cpp:156
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition: kicad_algo.h:99
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
std::vector< FAB_LAYER_COLOR > dummy