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