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