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#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}
316
317
319{
320 if( event.GetRow() >= 0 && m_lastAlias >= 0
321 && m_lastAlias < (int) m_aliases.size() )
322 {
324 }
325
326 event.Skip();
327}
328
329
331{
332 m_membersGrid->ClearRows();
333
334 if( m_lastAlias >= 0 && m_lastAlias < m_aliasesGrid->GetNumberRows() )
335 {
336 const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[ m_lastAlias ];
337 wxString source;
338 wxString membersLabel;
339
340 membersLabel.Printf( m_membersLabelTemplate, m_lastAliasName );
341
342 m_source->SetLabel( source );
343 m_membersLabel->SetLabel( membersLabel );
344
345 m_membersGrid->AppendRows( alias->Members().size() );
346
347 int ii = 0;
348
349 for( const wxString& member : alias->Members() )
350 m_membersGrid->SetCellValue( ii++, 0, member );
351 }
352
353 m_membersGridDirty = false;
354}
355
356
358{
361
362 Unbind( wxEVT_IDLE, &PANEL_SETUP_BUSES::reloadMembersGridOnIdle, this );
363}
364
365
366
367void PANEL_SETUP_BUSES::OnSizeGrid( wxSizeEvent& event )
368{
369 auto setColSize =
370 []( WX_GRID* grid )
371 {
372 int colSize = std::max( grid->GetClientSize().x, grid->GetVisibleWidth( 0 ) );
373
374 if( grid->GetColSize( 0 ) != colSize )
375 grid->SetColSize( 0, colSize );
376 };
377
378 setColSize( m_aliasesGrid );
379 setColSize( m_membersGrid );
380
381 // Always propagate for a grid repaint (needed if the height changes, as well as width)
382 event.Skip();
383}
384
385
386void PANEL_SETUP_BUSES::OnUpdateUI( wxUpdateUIEvent& event )
387{
388 // Handle a grid error. This is delayed to OnUpdateUI so that we can change focus
389 // even when the original validation was triggered from a killFocus event.
390 if( !m_errorMsg.IsEmpty() )
391 {
392 // We will re-enter this routine when the error dialog is displayed, so make
393 // sure we don't keep putting up more dialogs.
394 wxString errorMsg = m_errorMsg;
395 m_errorMsg = wxEmptyString;
396
397 wxWindow* topLevelParent = wxGetTopLevelParent( this );
398
399 DisplayErrorMessage( topLevelParent, errorMsg );
400
401 m_errorGrid->SetFocus();
402 m_errorGrid->MakeCellVisible( m_errorRow, 0 );
403 m_errorGrid->SetGridCursor( m_errorRow, 0 );
404
405 m_errorGrid->EnableCellEditControl( true );
406 m_errorGrid->ShowCellEditControl();
407
408 return;
409 }
410
411 if( !m_membersGrid->IsCellEditControlShown() )
412 {
413 int row = -1;
414 wxString aliasName;
415
416 if( m_aliasesGrid->IsCellEditControlShown() )
417 {
418 row = m_aliasesGrid->GetGridCursorRow();
419 wxGridCellEditor* cellEditor = m_aliasesGrid->GetCellEditor( row, 0 );
420
421 if( wxTextEntry* txt = dynamic_cast<wxTextEntry*>( cellEditor->GetControl() ) )
422 aliasName = txt->GetValue();
423
424 cellEditor->DecRef();
425 }
426 else if( m_aliasesGrid->GetGridCursorRow() >= 0 )
427 {
428 row = m_aliasesGrid->GetGridCursorRow();
429 aliasName = m_aliasesGrid->GetCellValue( row, 0 );
430 }
431 else if( m_lastAlias >= 0 && m_lastAlias < m_aliasesGrid->GetNumberRows() )
432 {
433 row = m_lastAlias;
434 aliasName = m_lastAliasName;
435 }
436
437 if( row < 0 )
438 {
439 m_membersBook->SetSelection( 1 );
440 }
441 else if( row != m_lastAlias || aliasName != m_lastAliasName )
442 {
443 m_lastAlias = row;
444 m_lastAliasName = aliasName;
445
446 m_membersBook->SetSelection( 0 );
447 m_membersBook->GetPage( 0 )->Layout();
448
449 const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[ row ];
450 alias->SetName( aliasName );
451
452 m_membersGridDirty = true;
453 Bind( wxEVT_IDLE, &PANEL_SETUP_BUSES::reloadMembersGridOnIdle, this );
454 }
455 }
456}
457
458
459void PANEL_SETUP_BUSES::ImportSettingsFrom( const std::map<wxString, std::vector<wxString>>& aAliases )
460{
461 m_aliases.clear();
462
463 std::vector<std::pair<wxString, std::vector<wxString>>> aliasList( aAliases.begin(), aAliases.end() );
464
465 std::sort( aliasList.begin(), aliasList.end(),
466 []( const std::pair<wxString, std::vector<wxString>>& a,
467 const std::pair<wxString, std::vector<wxString>>& b )
468 {
469 return a.first.CmpNoCase( b.first ) < 0;
470 } );
471
472 for( const auto& alias : aliasList )
473 {
474 std::shared_ptr<BUS_ALIAS> entry = std::make_shared<BUS_ALIAS>();
475
476 entry->SetName( alias.first );
477 entry->SetMembers( alias.second );
478
479 m_aliases.push_back( entry );
480 }
481
482 int ii = 0;
483
484 m_aliasesGrid->ClearRows();
485 m_aliasesGrid->AppendRows( m_aliases.size() );
486
487 for( const std::shared_ptr<BUS_ALIAS>& alias : m_aliases )
488 m_aliasesGrid->SetCellValue( ii++, 0, alias->GetName() );
489
490 // Clear members grid
491 m_lastAlias = -1;
492 m_lastAliasName = wxEmptyString;
494 m_membersBook->SetSelection( 1 );
495}
496
497
499{
500 if( !m_aliases.empty() && m_membersGrid->GetNumberRows() > 0 && aAliasIndex >= 0
501 && aAliasIndex < (int)m_aliases.size() )
502 {
503 const std::shared_ptr<BUS_ALIAS>& alias = m_aliases[aAliasIndex];
504
505 alias->ClearMembers();
506
507 for( int ii = 0; ii < m_membersGrid->GetNumberRows(); ++ii )
508 {
509 wxString cellValue = m_membersGrid->GetCellValue( ii, 0 );
510 wxStringTokenizer tok( cellValue, " " );
511
512 if( tok.CountTokens() > 1 )
513 {
514 m_membersGridDirty = true;
515 Bind( wxEVT_IDLE, &PANEL_SETUP_BUSES::reloadMembersGridOnIdle, this );
516 }
517
518 while( tok.HasMoreTokens() )
519 alias->AddMember( tok.GetNextToken() );
520 }
521 }
522}
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)