KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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-2024 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#include <wx/clipbrd.h>
30
32 PANEL_SETUP_BUSES_BASE( aWindow ),
33 m_frame( aFrame ),
34 m_lastAlias( 0 ),
35 m_membersGridDirty( false ),
36 m_errorGrid( nullptr ),
37 m_errorRow( -1 )
38{
40
41 m_addAlias->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
42 m_deleteAlias->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
43 m_addMember->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
44 m_removeMember->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
45
46 m_source->SetFont( KIUI::GetInfoFont( aWindow ) );
47
48 m_aliasesGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
49 m_membersGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
50
51 m_aliasesGrid->PushEventHandler( new GRID_TRICKS( m_aliasesGrid,
52 [this]( wxCommandEvent& aEvent )
53 {
54 OnAddAlias( aEvent );
55 } ) );
56
57 m_membersGrid->PushEventHandler( new GRID_TRICKS( m_membersGrid,
58 [this]( wxCommandEvent& aEvent )
59 {
60 OnAddMember( aEvent );
61 } ) );
62
63 m_aliasesGrid->SetUseNativeColLabels();
64 m_membersGrid->SetUseNativeColLabels();
65
66 // wxFormBuilder doesn't include this event...
67 m_aliasesGrid->Connect( wxEVT_GRID_CELL_CHANGING,
69 nullptr, this );
70 m_membersGrid->Connect( wxEVT_GRID_CELL_CHANGING,
72 nullptr, this );
73
74 Layout();
75}
76
77
79{
80 // Delete the GRID_TRICKS.
81 m_aliasesGrid->PopEventHandler( true );
82 m_membersGrid->PopEventHandler( true );
83
84 m_aliasesGrid->Disconnect( wxEVT_GRID_CELL_CHANGING,
86 nullptr, this );
87 m_membersGrid->Disconnect( wxEVT_GRID_CELL_CHANGING,
89 nullptr, this );
90}
91
92
94{
95 auto contains =
96 [&]( const std::shared_ptr<BUS_ALIAS>& alias ) -> bool
97 {
98 wxString aName = alias->GetName();
99 std::vector<wxString> aMembers = alias->Members();
100
101 std::sort( aMembers.begin(), aMembers.end() );
102
103 for( const std::shared_ptr<BUS_ALIAS>& candidate : m_aliases )
104 {
105 wxString bName = candidate->GetName();
106 std::vector<wxString> bMembers = candidate->Members();
107
108 std::sort( bMembers.begin(), bMembers.end() );
109
110 if( aName == bName && aMembers == bMembers )
111 return true;
112 }
113
114 return false;
115 };
116
117 SCH_SCREENS screens( aSchematic.Root() );
118
119 // collect aliases from each open sheet
120 for( SCH_SCREEN* screen = screens.GetFirst(); screen != nullptr; screen = screens.GetNext() )
121 {
122 for( const std::shared_ptr<BUS_ALIAS>& alias : screen->GetBusAliases() )
123 {
124 if( !contains( alias ) )
125 m_aliases.push_back( alias->Clone() );
126 }
127 }
128
129 int ii = 0;
130
132 m_aliasesGrid->AppendRows( m_aliases.size() );
133
134 for( const std::shared_ptr<BUS_ALIAS>& alias : m_aliases )
135 m_aliasesGrid->SetCellValue( ii++, 0, alias->GetName() );
136
137 m_membersBook->SetSelection( 1 );
138}
139
140
142{
144 return true;
145}
146
147
149{
151 return false;
152
153 // Copy names back just in case they didn't get caught on the GridCellChanging event
154 for( int ii = 0; ii < m_aliasesGrid->GetNumberRows(); ++ii )
155 m_aliases[ii]->SetName( m_aliasesGrid->GetCellValue( ii, 0 ) );
156
157 // Associate the respective members with the last alias that is active.
159
160 SCH_SCREENS screens( m_frame->Schematic().Root() );
161
162 for( SCH_SCREEN* screen = screens.GetFirst(); screen != nullptr; screen = screens.GetNext() )
163 screen->ClearBusAliases();
164
165 for( const std::shared_ptr<BUS_ALIAS>& alias : m_aliases )
166 alias->GetParent()->AddBusAlias( alias );
167
168 return true;
169}
170
171
172void PANEL_SETUP_BUSES::OnAddAlias( wxCommandEvent& aEvent )
173{
175 return;
176
177 // New aliases get stored on the currently visible sheet
178 m_aliases.push_back( std::make_shared<BUS_ALIAS>( m_frame->GetScreen() ) );
179
180 int row = m_aliasesGrid->GetNumberRows();
181
182 // Associate the respective members with the previous alias. This ensures that the association starts
183 // correctly when adding more than one row
184 if( row > 0 )
185 updateAliasMembers( row - 1 );
186
187 m_aliasesGrid->AppendRows();
188
189 m_aliasesGrid->MakeCellVisible( row, 0 );
190 m_aliasesGrid->SetGridCursor( row, 0 );
191
192 m_aliasesGrid->EnableCellEditControl( true );
193 m_aliasesGrid->ShowCellEditControl();
194}
195
196
197void PANEL_SETUP_BUSES::OnDeleteAlias( wxCommandEvent& aEvent )
198{
200 return;
201
202 int curRow = m_aliasesGrid->GetGridCursorRow();
203
204 if( curRow < 0 )
205 return;
206
207 // Clear the members grid first so we don't try to write it back to a deleted alias
209 m_lastAlias = -1;
210 m_lastAliasName = wxEmptyString;
211
212 m_aliases.erase( m_aliases.begin() + curRow );
213
214 m_aliasesGrid->DeleteRows( curRow, 1 );
215
216 if( m_aliasesGrid->GetNumberRows() > 0 )
217 {
218 m_aliasesGrid->MakeCellVisible( std::max( 0, curRow-1 ), 0 );
219 m_aliasesGrid->SelectRow( std::max( 0, curRow-1 ) );
220 }
221}
222
223
224void PANEL_SETUP_BUSES::OnAddMember( wxCommandEvent& aEvent )
225{
227 return;
228
229 int row = m_membersGrid->GetNumberRows();
230 m_membersGrid->AppendRows();
231
232 m_membersGrid->MakeCellVisible( row, 0 );
233 m_membersGrid->SetGridCursor( row, 0 );
234
235 /*
236 * Check if the clipboard contains text data.
237 *
238 * - If `clipboardHasText` is true, select the specified row in the members grid to allow our custom
239 * conext menu to paste the clipbaord .
240 * - Otherwise, enable and display the cell edit control, allowing the user to manually edit the cell.
241 */
242 bool clipboardHasText = false;
243
244 if( wxTheClipboard->Open() )
245 {
246 if( wxTheClipboard->IsSupported( wxDF_TEXT )
247 || wxTheClipboard->IsSupported( wxDF_UNICODETEXT ) )
248 {
249 clipboardHasText = true;
250 }
251
252 wxTheClipboard->Close();
253 }
254
255 if( clipboardHasText )
256 {
257 m_membersGrid->SelectRow( row );
258 }
259 else
260 {
261 m_membersGrid->EnableCellEditControl( true );
262 m_membersGrid->ShowCellEditControl();
263 }
264}
265
266
267void PANEL_SETUP_BUSES::OnRemoveMember( wxCommandEvent& aEvent )
268{
270 return;
271
272 int curRow = m_membersGrid->GetGridCursorRow();
273
274 if( curRow < 0 )
275 return;
276
277 m_membersGrid->DeleteRows( curRow, 1 );
278
279 // Update the member list of the current bus alias from the members grid
280 const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[ m_lastAlias ];
281 alias->Members().clear();
282
283 for( int ii = 0; ii < m_membersGrid->GetNumberRows(); ++ii )
284 alias->Members().push_back( m_membersGrid->GetCellValue( ii, 0 ) );
285
286 if( m_membersGrid->GetNumberRows() > 0 )
287 {
288 m_membersGrid->MakeCellVisible( std::max( 0, curRow-1 ), 0 );
289 m_membersGrid->SelectRow( std::max( 0, curRow-1 ) );
290 }
291}
292
293
295{
296 int row = event.GetRow();
297
298 if( row >= 0 )
299 {
300 wxString name = event.GetString();
301
302 for( int ii = 0; ii < m_aliasesGrid->GetNumberRows(); ++ii )
303 {
304 if( ii == event.GetRow() )
305 continue;
306
307 if( name == m_aliasesGrid->GetCellValue( ii, 0 )
308 && m_aliases[ row ]->GetParent() == m_aliases[ ii ]->GetParent() )
309 {
310 m_errorMsg = wxString::Format( _( "Alias name '%s' already in use." ), name );
312 m_errorRow = row;
313
314 event.Veto();
315 return;
316 }
317 }
318
319 m_aliases[ row ]->SetName( name );
320 }
321}
322
323
325{
326 int row = event.GetRow();
327
328 if( row >= 0 )
329 {
330 wxString name = event.GetString();
331
332 if( name.IsEmpty() )
333 {
334 m_errorMsg = _( "Member net/alias name cannot be empty." );
336 m_errorRow = event.GetRow();
337
338 event.Veto();
339 return;
340 }
341
342 const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[ m_lastAlias ];
343
344 alias->Members().clear();
345
346 for( int ii = 0; ii < m_membersGrid->GetNumberRows(); ++ii )
347 {
348 if( ii == row )
349 {
350 // Parse a space-separated list and add each one
351 wxStringTokenizer tok( name, " " );
352
353 if( tok.CountTokens() > 1 )
354 {
355 m_membersGridDirty = true;
356 Bind( wxEVT_IDLE, &PANEL_SETUP_BUSES::reloadMembersGridOnIdle, this );
357 }
358
359 while( tok.HasMoreTokens() )
360 alias->Members().push_back( tok.GetNextToken() );
361 }
362 else
363 {
364 alias->Members().push_back( m_membersGrid->GetCellValue( ii, 0 ) );
365 }
366 }
367 }
368}
369
370
372{
373 if( m_lastAlias >= 0 && m_lastAlias < m_aliasesGrid->GetNumberRows() )
374 {
375 const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[ m_lastAlias ];
376 wxString source;
377 wxString membersLabel;
378
379 if( alias->GetParent() )
380 {
381 wxFileName sheet_name( alias->GetParent()->GetFileName() );
382 source.Printf( wxS( "(" ) + sheet_name.GetFullName() + wxS( ")" ) );
383 }
384
385 membersLabel.Printf( m_membersLabelTemplate, m_lastAliasName );
386
387 m_source->SetLabel( source );
388 m_membersLabel->SetLabel( membersLabel );
389
391 m_membersGrid->AppendRows( alias->Members().size() );
392
393 int ii = 0;
394
395 for( const wxString& member : alias->Members() )
396 m_membersGrid->SetCellValue( ii++, 0, member );
397 }
398
399 m_membersGridDirty = false;
400}
401
402
404{
407
408 Unbind( wxEVT_IDLE, &PANEL_SETUP_BUSES::reloadMembersGridOnIdle, this );
409}
410
411
412
413void PANEL_SETUP_BUSES::OnSizeGrid( wxSizeEvent& event )
414{
415 auto setColSize =
416 []( WX_GRID* grid )
417 {
418 int colSize = std::max( grid->GetClientSize().x, grid->GetVisibleWidth( 0 ) );
419
420 if( grid->GetColSize( 0 ) != colSize )
421 grid->SetColSize( 0, colSize );
422 };
423
424 setColSize( m_aliasesGrid );
425 setColSize( m_membersGrid );
426
427 // Always propagate for a grid repaint (needed if the height changes, as well as width)
428 event.Skip();
429}
430
431
432void PANEL_SETUP_BUSES::OnUpdateUI( wxUpdateUIEvent& event )
433{
434 // Handle a grid error. This is delayed to OnUpdateUI so that we can change focus
435 // even when the original validation was triggered from a killFocus event.
436 if( !m_errorMsg.IsEmpty() )
437 {
438 // We will re-enter this routine when the error dialog is displayed, so make
439 // sure we don't keep putting up more dialogs.
440 wxString errorMsg = m_errorMsg;
441 m_errorMsg = wxEmptyString;
442
443 wxWindow* topLevelParent = wxGetTopLevelParent( this );
444
445 DisplayErrorMessage( topLevelParent, errorMsg );
446
447 m_errorGrid->SetFocus();
448 m_errorGrid->MakeCellVisible( m_errorRow, 0 );
449 m_errorGrid->SetGridCursor( m_errorRow, 0 );
450
451 m_errorGrid->EnableCellEditControl( true );
452 m_errorGrid->ShowCellEditControl();
453
454 return;
455 }
456
457 if( !m_membersGrid->IsCellEditControlShown() )
458 {
459 int row = -1;
460 wxString aliasName;
461
462 if( m_aliasesGrid->IsCellEditControlShown() )
463 {
464 row = m_aliasesGrid->GetGridCursorRow();
465 wxGridCellEditor* cellEditor = m_aliasesGrid->GetCellEditor( row, 0 );
466
467 if( wxTextEntry* txt = dynamic_cast<wxTextEntry*>( cellEditor->GetControl() ) )
468 aliasName = txt->GetValue();
469
470 cellEditor->DecRef();
471 }
472 else if( m_aliasesGrid->GetGridCursorRow() >= 0 )
473 {
474 row = m_aliasesGrid->GetGridCursorRow();
475 aliasName = m_aliasesGrid->GetCellValue( row, 0 );
476 }
477 else if( m_lastAlias >= 0 && m_lastAlias < m_aliasesGrid->GetNumberRows() )
478 {
479 row = m_lastAlias;
480 aliasName = m_lastAliasName;
481 }
482
483 if( row < 0 )
484 {
485 m_membersBook->SetSelection( 1 );
486 }
487 else if( row != m_lastAlias || aliasName != m_lastAliasName )
488 {
489 m_lastAlias = row;
490 m_lastAliasName = aliasName;
491
492 m_membersBook->SetSelection( 0 );
493 m_membersBook->GetPage( 0 )->Layout();
494
495 const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[ row ];
496 alias->SetName( aliasName );
497
498 m_membersGridDirty = true;
499 Bind( wxEVT_IDLE, &PANEL_SETUP_BUSES::reloadMembersGridOnIdle, this );
500 }
501 }
502}
503
504
506{
507 loadAliases( aOtherSchematic );
508
509 // New aliases get stored on the currently visible sheet
510 for( const std::shared_ptr<BUS_ALIAS>& alias : m_aliases )
511 alias->SetParent( m_frame->GetScreen() );
512}
513
514
516{
517 if( !m_aliases.empty() && m_membersGrid->GetNumberRows() > 0 && aAliasIndex >= 0
518 && aAliasIndex < (int)m_aliases.size() )
519 {
520 const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[aAliasIndex];
521
522 alias->Members().clear();
523
524 for( int ii = 0; ii < m_membersGrid->GetNumberRows(); ++ii )
525 {
526 wxString memberValue = m_membersGrid->GetCellValue( ii, 0 );
527
528 if( !memberValue.empty() )
529 {
530 alias->Members().push_back( memberValue );
531 }
532 }
533 }
534}
const char * name
Definition: DXF_plotter.cpp:57
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap)
Definition: bitmap.cpp:110
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
void loadAliases(const SCHEMATIC &aSchematic)
SCH_EDIT_FRAME * m_frame
void OnSizeGrid(wxSizeEvent &event) override
PANEL_SETUP_BUSES(wxWindow *aWindow, SCH_EDIT_FRAME *aFrame)
bool TransferDataToWindow() override
void ImportSettingsFrom(const SCHEMATIC &aOtherSchematic)
void updateAliasMembers(int aAliasIndex)
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
Holds all the data relating to one schematic.
Definition: schematic.h:77
SCH_SHEET & Root() const
Definition: schematic.h:121
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:712
SCH_SCREEN * GetNext()
SCH_SCREEN * GetFirst()
void SetBitmap(const wxBitmapBundle &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:184
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:637
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:195
This file is part of the common library.
#define _(s)
KICOMMON_API wxFont GetInfoFont(wxWindow *aWindow)
Definition: ui_common.cpp:154