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