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