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 The 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
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <algorithm>
23
24#include <bitmaps.h>
25#include <wx/tokenzr.h>
26#include <widgets/wx_grid.h>
28#include <confirm.h>
30#include <sch_edit_frame.h>
31#include <schematic.h>
33#include "grid_tricks.h"
34#include <wx/clipbrd.h>
35
37 PANEL_SETUP_BUSES_BASE( aWindow ),
38 m_frame( aFrame ),
39 m_lastAlias( 0 ),
40 m_membersGridDirty( false ),
41 m_errorGrid( nullptr ),
42 m_errorRow( -1 )
43{
45
50
51 m_source->SetFont( KIUI::GetSmallInfoFont( aWindow ) );
52
53 m_aliasesGrid->OverrideMinSize( 0.6, 0.3 );
54 m_membersGrid->OverrideMinSize( 0.6, 0.3 );
55 m_aliasesGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
56 m_membersGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
57
58 m_aliasesGrid->PushEventHandler( new GRID_TRICKS( m_aliasesGrid,
59 [this]( wxCommandEvent& aEvent )
60 {
61 OnAddAlias( aEvent );
62 } ) );
63
64 m_membersGrid->PushEventHandler( new GRID_TRICKS( m_membersGrid,
65 [this]( wxCommandEvent& aEvent )
66 {
67 OnAddMember( aEvent );
68 } ) );
69
70 m_aliasesGrid->SetUseNativeColLabels();
71 m_membersGrid->SetUseNativeColLabels();
72
73 // wxFormBuilder doesn't include this event...
74 m_aliasesGrid->Connect( wxEVT_GRID_CELL_CHANGING,
76 nullptr, this );
77 m_membersGrid->Connect( wxEVT_GRID_CELL_CHANGING,
79 nullptr, this );
80 m_membersGrid->Connect( wxEVT_GRID_CELL_CHANGED,
82 nullptr, this );
83
84 Layout();
85}
86
87
89{
90 // Delete the GRID_TRICKS.
91 m_aliasesGrid->PopEventHandler( true );
92 m_membersGrid->PopEventHandler( true );
93
94 m_aliasesGrid->Disconnect( wxEVT_GRID_CELL_CHANGING,
96 nullptr, this );
97 m_membersGrid->Disconnect( wxEVT_GRID_CELL_CHANGING,
99 nullptr, this );
100 m_membersGrid->Disconnect( wxEVT_GRID_CELL_CHANGED,
101 wxGridEventHandler( PANEL_SETUP_BUSES::OnMemberGridCellChanged ),
102 nullptr, this );
103}
104
105
107{
108 m_aliases.clear();
109
110 const auto& projectAliases = m_frame->Prj().GetProjectFile().m_BusAliases;
111
112 std::vector<std::pair<wxString, std::vector<wxString>>> aliasList( projectAliases.begin(),
113 projectAliases.end() );
114
115 std::sort( aliasList.begin(), aliasList.end(),
116 []( const std::pair<wxString, std::vector<wxString>>& a,
117 const std::pair<wxString, std::vector<wxString>>& b )
118 {
119 return a.first.CmpNoCase( b.first ) < 0;
120 } );
121
122 for( const auto& alias : aliasList )
123 {
124 std::shared_ptr<BUS_ALIAS> entry = std::make_shared<BUS_ALIAS>();
125
126 entry->SetName( alias.first );
127 entry->SetMembers( alias.second );
128
129 m_aliases.push_back( entry );
130 }
131
132 int ii = 0;
133
134 m_aliasesGrid->ClearRows();
135 m_aliasesGrid->AppendRows( m_aliases.size() );
136
137 for( const std::shared_ptr<BUS_ALIAS>& alias : m_aliases )
138 m_aliasesGrid->SetCellValue( ii++, 0, alias->GetName() );
139
140 m_membersBook->SetSelection( 1 );
141}
142
143
145{
146 loadAliases();
147 return true;
148}
149
150
152{
153 if( !m_aliasesGrid->CommitPendingChanges() || !m_membersGrid->CommitPendingChanges() )
154 return false;
155
156 // Copy names back just in case they didn't get caught on the GridCellChanging event
157 for( int ii = 0; ii < m_aliasesGrid->GetNumberRows(); ++ii )
158 m_aliases[ii]->SetName( m_aliasesGrid->GetCellValue( ii, 0 ) );
159
160 // Associate the respective members with the last alias that is active.
162
163 m_frame->Schematic().SetBusAliases( m_aliases );
164
165 return true;
166}
167
168
169void PANEL_SETUP_BUSES::OnAddAlias( wxCommandEvent& aEvent )
170{
171 if( !m_aliasesGrid->CommitPendingChanges() || !m_membersGrid->CommitPendingChanges() )
172 return;
173
174 m_aliasesGrid->OnAddRow(
175 [&]() -> std::pair<int, int>
176 {
177 // New aliases are stored at the project level
178 m_aliases.push_back( std::make_shared<BUS_ALIAS>() );
179
180 int row = m_aliasesGrid->GetNumberRows();
181
182 // Associate the respective members with the previous alias. This ensures that the association
183 // starts correctly when adding more than one row.
184 // But in order to avoid overwriting the members of the last (row - 1) alias with those of the
185 // selected alias (since the user may choose a different alias), the alias member update here
186 // should only happen if the current alias (m_lastAlias) is the last one (row - 1).
187 if( ( row > 0 ) && ( m_lastAlias == ( row - 1 ) ) )
188 updateAliasMembers( row - 1 );
189
190 m_aliasesGrid->AppendRows();
191 return { row, 0 };
192 } );
193}
194
195
196void PANEL_SETUP_BUSES::OnDeleteAlias( wxCommandEvent& aEvent )
197{
198 if( !m_aliasesGrid->CommitPendingChanges() || !m_membersGrid->CommitPendingChanges() )
199 return;
200
201 m_aliasesGrid->OnDeleteRows(
202 [&]( int row )
203 {
204 // Clear the members grid first so we don't try to write it back to a deleted alias
205 m_membersGrid->ClearRows();
206 m_lastAlias = -1;
207 m_lastAliasName = wxEmptyString;
208
209 m_aliases.erase( m_aliases.begin() + row );
210
211 m_aliasesGrid->DeleteRows( row, 1 );
212 } );
213}
214
215
216void PANEL_SETUP_BUSES::OnAddMember( wxCommandEvent& aEvent )
217{
218 m_membersGrid->OnAddRow(
219 [&]() -> std::pair<int, int>
220 {
221 int row = m_membersGrid->GetNumberRows();
222 m_membersGrid->AppendRows();
223
224 /*
225 * Check if the clipboard contains text data.
226 *
227 * - If `clipboardHasText` is true, select the specified row in the members grid to allow
228 * our custom context menu to paste the clipboard .
229 * - Otherwise, enable and display the cell edit control, allowing the user to manually edit
230 * the cell.
231 */
232 bool clipboardHasText = false;
233
234 if( wxTheClipboard->Open() )
235 {
236 if( wxTheClipboard->IsSupported( wxDF_TEXT )
237 || wxTheClipboard->IsSupported( wxDF_UNICODETEXT ) )
238 {
239 clipboardHasText = true;
240 }
241
242 wxTheClipboard->Close();
243 }
244
245 if( clipboardHasText )
246 return { row, -1 };
247 else
248 return { row, 0 };
249 } );
250}
251
252
253void PANEL_SETUP_BUSES::OnRemoveMember( wxCommandEvent& aEvent )
254{
255 m_membersGrid->OnDeleteRows(
256 [&]( int row )
257 {
258 m_membersGrid->DeleteRows( row, 1 );
259
260 // Update the member list of the current bus alias from the members grid
261 const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[ m_lastAlias ];
262 alias->ClearMembers();
263
264 for( int ii = 0; ii < m_membersGrid->GetNumberRows(); ++ii )
265 alias->AddMember( m_membersGrid->GetCellValue( ii, 0 ) );
266 } );
267}
268
269
271{
272 int row = event.GetRow();
273
274 if( row >= 0 )
275 {
276 wxString name = event.GetString();
277
278 for( int ii = 0; ii < m_aliasesGrid->GetNumberRows(); ++ii )
279 {
280 if( ii == event.GetRow() )
281 continue;
282
283 if( name == m_aliasesGrid->GetCellValue( ii, 0 ) )
284 {
285 m_errorMsg = wxString::Format( _( "Alias name '%s' already in use." ), name );
287 m_errorRow = row;
288
289 event.Veto();
290 return;
291 }
292 }
293
294 m_aliases[ row ]->SetName( name );
295 }
296}
297
298
300{
301 int row = event.GetRow();
302
303 if( row >= 0 )
304 {
305 wxString name = event.GetString();
306
307 if( name.IsEmpty() )
308 {
309 m_errorMsg = _( "Member net/alias name cannot be empty." );
311 m_errorRow = event.GetRow();
312
313 event.Veto();
314 return;
315 }
316 }
317}
318
319
321{
322 if( event.GetRow() >= 0 && m_lastAlias >= 0
323 && m_lastAlias < (int) m_aliases.size() )
324 {
326 }
327
328 event.Skip();
329}
330
331
333{
334 m_membersGrid->ClearRows();
335
336 if( m_lastAlias >= 0 && m_lastAlias < m_aliasesGrid->GetNumberRows() )
337 {
338 const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[ m_lastAlias ];
339 wxString source;
340 wxString membersLabel;
341
342 membersLabel.Printf( m_membersLabelTemplate, m_lastAliasName );
343
344 m_source->SetLabel( source );
345 m_membersLabel->SetLabel( membersLabel );
346
347 m_membersGrid->AppendRows( alias->Members().size() );
348
349 int ii = 0;
350
351 for( const wxString& member : alias->Members() )
352 m_membersGrid->SetCellValue( ii++, 0, member );
353 }
354
355 m_membersGridDirty = false;
356}
357
358
360{
363
364 Unbind( wxEVT_IDLE, &PANEL_SETUP_BUSES::reloadMembersGridOnIdle, this );
365}
366
367
368
369void PANEL_SETUP_BUSES::OnSizeGrid( wxSizeEvent& event )
370{
371 auto setColSize =
372 []( WX_GRID* grid )
373 {
374 int colSize = std::max( grid->GetClientSize().x, grid->GetVisibleWidth( 0 ) );
375
376 if( grid->GetColSize( 0 ) != colSize )
377 grid->SetColSize( 0, colSize );
378 };
379
380 setColSize( m_aliasesGrid );
381 setColSize( m_membersGrid );
382
383 // Always propagate for a grid repaint (needed if the height changes, as well as width)
384 event.Skip();
385}
386
387
388void PANEL_SETUP_BUSES::OnUpdateUI( wxUpdateUIEvent& event )
389{
390 // Handle a grid error. This is delayed to OnUpdateUI so that we can change focus
391 // even when the original validation was triggered from a killFocus event.
392 if( !m_errorMsg.IsEmpty() )
393 {
394 // We will re-enter this routine when the error dialog is displayed, so make
395 // sure we don't keep putting up more dialogs.
396 wxString errorMsg = m_errorMsg;
397 m_errorMsg = wxEmptyString;
398
399 wxWindow* topLevelParent = wxGetTopLevelParent( this );
400
401 DisplayErrorMessage( topLevelParent, errorMsg );
402
403 m_errorGrid->SetFocus();
404 m_errorGrid->MakeCellVisible( m_errorRow, 0 );
405 m_errorGrid->SetGridCursor( m_errorRow, 0 );
406
407 m_errorGrid->EnableCellEditControl( true );
408 m_errorGrid->ShowCellEditControl();
409
410 return;
411 }
412
413 if( !m_membersGrid->IsCellEditControlShown() )
414 {
415 int row = -1;
416 wxString aliasName;
417
418 if( m_aliasesGrid->IsCellEditControlShown() )
419 {
420 row = m_aliasesGrid->GetGridCursorRow();
421 wxGridCellEditor* cellEditor = m_aliasesGrid->GetCellEditor( row, 0 );
422
423 if( wxTextEntry* txt = dynamic_cast<wxTextEntry*>( cellEditor->GetControl() ) )
424 aliasName = txt->GetValue();
425
426 cellEditor->DecRef();
427 }
428 else if( m_aliasesGrid->GetGridCursorRow() >= 0 )
429 {
430 row = m_aliasesGrid->GetGridCursorRow();
431 aliasName = m_aliasesGrid->GetCellValue( row, 0 );
432 }
433 else if( m_lastAlias >= 0 && m_lastAlias < m_aliasesGrid->GetNumberRows() )
434 {
435 row = m_lastAlias;
436 aliasName = m_lastAliasName;
437 }
438
439 if( row < 0 )
440 {
441 m_membersBook->SetSelection( 1 );
442 }
443 else if( row != m_lastAlias || aliasName != m_lastAliasName )
444 {
445 m_lastAlias = row;
446 m_lastAliasName = aliasName;
447
448 m_membersBook->SetSelection( 0 );
449 m_membersBook->GetPage( 0 )->Layout();
450
451 const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[ row ];
452 alias->SetName( aliasName );
453
454 m_membersGridDirty = true;
455 Bind( wxEVT_IDLE, &PANEL_SETUP_BUSES::reloadMembersGridOnIdle, this );
456 }
457 }
458}
459
460
461void PANEL_SETUP_BUSES::ImportSettingsFrom( const std::map<wxString, std::vector<wxString>>& aAliases )
462{
463 m_aliases.clear();
464
465 std::vector<std::pair<wxString, std::vector<wxString>>> aliasList( aAliases.begin(), aAliases.end() );
466
467 std::sort( aliasList.begin(), aliasList.end(),
468 []( const std::pair<wxString, std::vector<wxString>>& a,
469 const std::pair<wxString, std::vector<wxString>>& b )
470 {
471 return a.first.CmpNoCase( b.first ) < 0;
472 } );
473
474 for( const auto& alias : aliasList )
475 {
476 std::shared_ptr<BUS_ALIAS> entry = std::make_shared<BUS_ALIAS>();
477
478 entry->SetName( alias.first );
479 entry->SetMembers( alias.second );
480
481 m_aliases.push_back( entry );
482 }
483
484 int ii = 0;
485
486 m_aliasesGrid->ClearRows();
487 m_aliasesGrid->AppendRows( m_aliases.size() );
488
489 for( const std::shared_ptr<BUS_ALIAS>& alias : m_aliases )
490 m_aliasesGrid->SetCellValue( ii++, 0, alias->GetName() );
491
492 // Clear members grid
493 m_lastAlias = -1;
494 m_lastAliasName = wxEmptyString;
496 m_membersBook->SetSelection( 1 );
497}
498
499
501{
502 if( !m_aliases.empty() && m_membersGrid->GetNumberRows() > 0 && aAliasIndex >= 0
503 && aAliasIndex < (int)m_aliases.size() )
504 {
505 const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[aAliasIndex];
506
507 alias->ClearMembers();
508
509 for( int ii = 0; ii < m_membersGrid->GetNumberRows(); ++ii )
510 {
511 wxString cellValue = m_membersGrid->GetCellValue( ii, 0 );
512 wxStringTokenizer tok( cellValue, " " );
513
514 if( tok.CountTokens() > 1 )
515 {
516 m_membersGridDirty = true;
517 Bind( wxEVT_IDLE, &PANEL_SETUP_BUSES::reloadMembersGridOnIdle, this );
518 }
519
520 while( tok.HasMoreTokens() )
521 alias->AddMember( tok.GetNextToken() );
522 }
523 }
524}
const char * name
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition grid_tricks.h:57
STD_BITMAP_BUTTON * m_addAlias
STD_BITMAP_BUTTON * m_addMember
PANEL_SETUP_BUSES_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
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
void OnMemberGridCellChanged(wxGridEvent &event)
void updateAliasMembers(int aAliasIndex)
Keep the BUS_ALIAS member list synchronized with the values displayed in the grid.
wxString m_membersLabelTemplate
void OnRemoveMember(wxCommandEvent &aEvent) override
void OnMemberGridCellChanging(wxGridEvent &event)
bool TransferDataFromWindow() override
void OnAddMember(wxCommandEvent &aEvent) override
void ImportSettingsFrom(const std::map< wxString, std::vector< wxString > > &aAliases)
void OnAddAlias(wxCommandEvent &aEvent) override
Schematic editor (Eeschema) main window.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
This file is part of the common library.
#define _(s)
std::string source
KICOMMON_API wxFont GetSmallInfoFont(wxWindow *aWindow)