KiCad PCB EDA Suite
Loading...
Searching...
No Matches
eeschema/cross-probing.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) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2011 Wayne Stambaugh <[email protected]>
6 * Copyright (C) 2004-2023, 2024 KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU 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, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include <kiface_base.h>
27#include <kiway_express.h>
28#include <eda_dde.h>
29#include <connection_graph.h>
30#include <sch_sheet.h>
31#include <sch_symbol.h>
32#include <sch_reference_list.h>
33#include <string_utils.h>
37#include <richio.h>
38#include <tools/ee_actions.h>
40#include <advanced_config.h>
42#include <wx/log.h>
43
44SCH_ITEM* SCH_EDITOR_CONTROL::FindSymbolAndItem( const wxString* aPath, const wxString* aReference,
45 bool aSearchHierarchy, SCH_SEARCH_T aSearchType,
46 const wxString& aSearchText )
47{
48 SCH_SHEET_PATH* sheetWithSymbolFound = nullptr;
49 SCH_SYMBOL* symbol = nullptr;
50 SCH_PIN* pin = nullptr;
51 SCH_SHEET_LIST sheetList;
52 SCH_ITEM* foundItem = nullptr;
53
54 if( !aSearchHierarchy )
55 sheetList.push_back( m_frame->GetCurrentSheet() );
56 else
57 sheetList = m_frame->Schematic().Hierarchy();
58
59 for( SCH_SHEET_PATH& sheet : sheetList )
60 {
61 SCH_SCREEN* screen = sheet.LastScreen();
62
63 for( EDA_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
64 {
65 SCH_SYMBOL* candidate = static_cast<SCH_SYMBOL*>( item );
66
67 // Search by path if specified, otherwise search by reference
68 bool found = false;
69
70 if( aPath )
71 {
72 wxString path = sheet.PathAsString() + candidate->m_Uuid.AsString();
73 found = ( *aPath == path );
74 }
75 else
76 {
77 found = ( aReference && aReference->CmpNoCase( candidate->GetRef( &sheet ) ) == 0 );
78 }
79
80 if( found )
81 {
82 symbol = candidate;
83 sheetWithSymbolFound = &sheet;
84
85 if( aSearchType == HIGHLIGHT_PIN )
86 {
87 pin = symbol->GetPin( aSearchText );
88
89 // Ensure we have found the right unit in case of multi-units symbol
90 if( pin )
91 {
92 int unit = pin->GetLibPin()->GetUnit();
93
94 if( unit != 0 && unit != symbol->GetUnit() )
95 {
96 pin = nullptr;
97 continue;
98 }
99
100 // Get pin position in true schematic coordinate
101 foundItem = pin;
102 break;
103 }
104 }
105 else
106 {
107 foundItem = symbol;
108 break;
109 }
110 }
111 }
112
113 if( foundItem )
114 break;
115 }
116
117 CROSS_PROBING_SETTINGS& crossProbingSettings = m_frame->eeconfig()->m_CrossProbing;
118
119 if( symbol )
120 {
121 if( *sheetWithSymbolFound != m_frame->GetCurrentSheet() )
122 {
123 m_frame->Schematic().SetCurrentSheet( *sheetWithSymbolFound );
125 }
126
127 if( crossProbingSettings.center_on_items )
128 {
129 if( crossProbingSettings.zoom_to_fit )
130 {
131 BOX2I bbox = symbol->GetBoundingBox();
132
133 m_toolMgr->GetTool<EE_SELECTION_TOOL>()->ZoomFitCrossProbeBBox( bbox );
134 }
135
136 if( pin )
138 else
139 m_frame->FocusOnItem( symbol );
140 }
141 }
142
143 /* Print diag */
144 wxString msg;
145 wxString displayRef;
146
147 if( aReference )
148 displayRef = *aReference;
149 else if( aPath )
150 displayRef = *aPath;
151
152 if( symbol )
153 {
154 if( aSearchType == HIGHLIGHT_PIN )
155 {
156 if( foundItem )
157 msg.Printf( _( "%s pin %s found" ), displayRef, aSearchText );
158 else
159 msg.Printf( _( "%s found but pin %s not found" ), displayRef, aSearchText );
160 }
161 else
162 {
163 msg.Printf( _( "%s found" ), displayRef );
164 }
165 }
166 else
167 {
168 msg.Printf( _( "%s not found" ), displayRef );
169 }
170
171 m_frame->SetStatusText( msg );
173
174 return foundItem;
175}
176
177
178/* Execute a remote command sent via a socket on port KICAD_PCB_PORT_SERVICE_NUMBER
179 *
180 * Commands are:
181 *
182 * $PART: "reference" Put cursor on symbol.
183 * $PART: "reference" $REF: "ref" Put cursor on symbol reference.
184 * $PART: "reference" $VAL: "value" Put cursor on symbol value.
185 * $PART: "reference" $PAD: "pin name" Put cursor on the symbol pin.
186 * $NET: "netname" Highlight a specified net
187 * $CLEAR: "HIGHLIGHTED" Clear symbols highlight
188 *
189 * $CONFIG Show the Manage Symbol Libraries dialog
190 * $ERC Show the ERC dialog
191 */
192void SCH_EDIT_FRAME::ExecuteRemoteCommand( const char* cmdline )
193{
195 char line[1024];
196
197 strncpy( line, cmdline, sizeof( line ) - 1 );
198 line[ sizeof( line ) - 1 ] = '\0';
199
200 char* idcmd = strtok( line, " \n\r" );
201 char* text = strtok( nullptr, "\"\n\r" );
202
203 if( idcmd == nullptr )
204 return;
205
206 CROSS_PROBING_SETTINGS& crossProbingSettings = eeconfig()->m_CrossProbing;
207
208 if( strcmp( idcmd, "$CONFIG" ) == 0 )
209 {
211 return;
212 }
213 else if( strcmp( idcmd, "$ERC" ) == 0 )
214 {
216 return;
217 }
218 else if( strcmp( idcmd, "$NET:" ) == 0 )
219 {
220 if( !crossProbingSettings.auto_highlight )
221 return;
222
223 wxString netName = From_UTF8( text );
224
225 if( auto sg = Schematic().ConnectionGraph()->FindFirstSubgraphByName( netName ) )
226 m_highlightedConn = sg->GetDriverConnection()->Name();
227 else
228 m_highlightedConn = wxEmptyString;
229
232
233 SetStatusText( _( "Selected net:" ) + wxS( " " ) + UnescapeString( netName ) );
234 return;
235 }
236 else if( strcmp( idcmd, "$CLEAR:" ) == 0 )
237 {
238 // Cross-probing is now done through selection so we no longer need a clear command
239 return;
240 }
241
242 if( !crossProbingSettings.on_selection )
243 return;
244
245 if( text == nullptr )
246 return;
247
248 if( strcmp( idcmd, "$PART:" ) != 0 )
249 return;
250
251 wxString part_ref = From_UTF8( text );
252
253 /* look for a complement */
254 idcmd = strtok( nullptr, " \n\r" );
255
256 if( idcmd == nullptr ) // Highlight symbol only (from CvPcb or Pcbnew)
257 {
258 // Highlight symbol part_ref, or clear Highlight, if part_ref is not existing
259 editor->FindSymbolAndItem( nullptr, &part_ref, true, HIGHLIGHT_SYMBOL, wxEmptyString );
260 return;
261 }
262
263 text = strtok( nullptr, "\"\n\r" );
264
265 if( text == nullptr )
266 return;
267
268 wxString msg = From_UTF8( text );
269
270 if( strcmp( idcmd, "$REF:" ) == 0 )
271 {
272 // Highlighting the reference itself isn't actually that useful, and it's harder to
273 // see. Highlight the parent and display the message.
274 editor->FindSymbolAndItem( nullptr, &part_ref, true, HIGHLIGHT_SYMBOL, msg );
275 }
276 else if( strcmp( idcmd, "$VAL:" ) == 0 )
277 {
278 // Highlighting the value itself isn't actually that useful, and it's harder to see.
279 // Highlight the parent and display the message.
280 editor->FindSymbolAndItem( nullptr, &part_ref, true, HIGHLIGHT_SYMBOL, msg );
281 }
282 else if( strcmp( idcmd, "$PAD:" ) == 0 )
283 {
284 editor->FindSymbolAndItem( nullptr, &part_ref, true, HIGHLIGHT_PIN, msg );
285 }
286 else
287 {
288 editor->FindSymbolAndItem( nullptr, &part_ref, true, HIGHLIGHT_SYMBOL, wxEmptyString );
289 }
290}
291
292
293void SCH_EDIT_FRAME::SendSelectItemsToPcb( const std::vector<EDA_ITEM*>& aItems, bool aForce )
294{
295 std::vector<wxString> parts;
296
297 for( EDA_ITEM* item : aItems )
298 {
299 switch( item->Type() )
300 {
301 case SCH_SYMBOL_T:
302 {
303 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
304 wxString ref = symbol->GetField( REFERENCE_FIELD )->GetText();
305
306 parts.push_back( wxT( "F" ) + EscapeString( ref, CTX_IPC ) );
307 break;
308 }
309
310 case SCH_SHEET_T:
311 {
312 // For cross probing, we need the full path of the sheet, because
313 // we search by the footprint path prefix in the PCB editor
314 wxString full_path = GetCurrentSheet().PathAsString() + item->m_Uuid.AsString();
315
316 parts.push_back( wxT( "S" ) + full_path );
317 break;
318 }
319
320 case SCH_PIN_T:
321 {
322 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
323 SYMBOL* symbol = pin->GetParentSymbol();
324 wxString ref = symbol->GetRef( &GetCurrentSheet(), false );
325
326 parts.push_back( wxT( "P" ) + EscapeString( ref, CTX_IPC ) + wxT( "/" )
327 + EscapeString( pin->GetShownNumber(), CTX_IPC ) );
328 break;
329 }
330
331 default:
332 break;
333 }
334 }
335
336 if( parts.empty() )
337 return;
338
339 std::string command = "$SELECT: 0,";
340
341 for( wxString part : parts )
342 {
343 command += part;
344 command += ",";
345 }
346
347 command.pop_back();
348
349 if( Kiface().IsSingle() )
350 {
351 SendCommand( MSG_TO_PCB, command );
352 }
353 else
354 {
355 // Typically ExpressMail is going to be s-expression packets, but since
356 // we have existing interpreter of the selection packet on the other
357 // side in place, we use that here.
359 command, this );
360 }
361}
362
363
364void SCH_EDIT_FRAME::SendCrossProbeNetName( const wxString& aNetName )
365{
366 // The command is a keyword followed by a quoted string.
367
368 std::string packet = StrPrintf( "$NET: \"%s\"", TO_UTF8( aNetName ) );
369
370 if( !packet.empty() )
371 {
372 if( Kiface().IsSingle() )
373 {
374 SendCommand( MSG_TO_PCB, packet );
375 }
376 else
377 {
378 // Typically ExpressMail is going to be s-expression packets, but since
379 // we have existing interpreter of the cross probe packet on the other
380 // side in place, we use that here.
382 }
383 }
384}
385
386
388{
389 if( !aConnection )
390 {
392 return;
393 }
394
395 if( aConnection->IsNet() )
396 {
397 SendCrossProbeNetName( aConnection->Name() );
398 return;
399 }
400
401 if( aConnection->Members().empty() )
402 return;
403
404 auto all_members = aConnection->AllMembers();
405
406 wxString nets = all_members[0]->Name();
407
408 if( all_members.size() == 1 )
409 {
410 SendCrossProbeNetName( nets );
411 return;
412 }
413
414 // TODO: This could be replaced by just sending the bus name once we have bus contents
415 // included as part of the netlist sent from Eeschema to Pcbnew (and thus Pcbnew can
416 // natively keep track of bus membership)
417
418 for( size_t i = 1; i < all_members.size(); i++ )
419 nets << "," << all_members[i]->Name();
420
421 std::string packet = StrPrintf( "$NETS: \"%s\"", TO_UTF8( nets ) );
422
423 if( !packet.empty() )
424 {
425 if( Kiface().IsSingle() )
426 SendCommand( MSG_TO_PCB, packet );
427 else
428 {
429 // Typically ExpressMail is going to be s-expression packets, but since
430 // we have existing interpreter of the cross probe packet on the other
431 // side in place, we use that here.
433 }
434 }
435}
436
437
439{
440 std::string packet = "$CLEAR\n";
441
442 if( Kiface().IsSingle() )
443 {
444 SendCommand( MSG_TO_PCB, packet );
445 }
446 else
447 {
448 // Typically ExpressMail is going to be s-expression packets, but since
449 // we have existing interpreter of the cross probe packet on the other
450 // side in place, we use that here.
452 }
453}
454
455
457 const SCH_SHEET_LIST& aSchematicSheetList, const SCH_SHEET_PATH& aSheetPath,
458 std::unordered_map<wxString, std::vector<SCH_REFERENCE>>& aSyncSymMap,
459 std::unordered_map<wxString, std::unordered_map<wxString, SCH_PIN*>>& aSyncPinMap,
460 bool aRecursive = false )
461{
462 if( aRecursive )
463 {
464 // Iterate over children
465 for( const SCH_SHEET_PATH& candidate : aSchematicSheetList )
466 {
467 if( candidate == aSheetPath || !candidate.IsContainedWithin( aSheetPath ) )
468 continue;
469
470 findSymbolsAndPins( aSchematicSheetList, candidate, aSyncSymMap, aSyncPinMap,
471 aRecursive );
472 }
473 }
474
475 SCH_REFERENCE_LIST references;
476
477 aSheetPath.GetSymbols( references, false, true );
478
479 for( unsigned ii = 0; ii < references.GetCount(); ii++ )
480 {
481 SCH_REFERENCE& schRef = references[ii];
482
483 if( schRef.IsSplitNeeded() )
484 schRef.Split();
485
486 SCH_SYMBOL* symbol = schRef.GetSymbol();
487 wxString refNum = schRef.GetRefNumber();
488 wxString fullRef = schRef.GetRef() + refNum;
489
490 // Skip power symbols
491 if( fullRef.StartsWith( wxS( "#" ) ) )
492 continue;
493
494 // Unannotated symbols are not supported
495 if( refNum.compare( wxS( "?" ) ) == 0 )
496 continue;
497
498 // Look for whole footprint
499 auto symMatchIt = aSyncSymMap.find( fullRef );
500
501 if( symMatchIt != aSyncSymMap.end() )
502 {
503 symMatchIt->second.emplace_back( schRef );
504
505 // Whole footprint was selected, no need to select pins
506 continue;
507 }
508
509 // Look for pins
510 auto symPinMatchIt = aSyncPinMap.find( fullRef );
511
512 if( symPinMatchIt != aSyncPinMap.end() )
513 {
514 std::unordered_map<wxString, SCH_PIN*>& pinMap = symPinMatchIt->second;
515 std::vector<SCH_PIN*> pinsOnSheet = symbol->GetPins( &aSheetPath );
516
517 for( SCH_PIN* pin : pinsOnSheet )
518 {
519 int pinUnit = pin->GetLibPin()->GetUnit();
520
521 if( pinUnit > 0 && pinUnit != schRef.GetUnit() )
522 continue;
523
524 auto pinIt = pinMap.find( pin->GetNumber() );
525
526 if( pinIt != pinMap.end() )
527 pinIt->second = pin;
528 }
529 }
530 }
531
532 return false;
533}
534
535
537 const SCH_SHEET_LIST& aSchematicSheetList, const SCH_SHEET_PATH& aSheetPath,
538 std::unordered_map<wxString, std::vector<SCH_REFERENCE>>& aSyncSymMap,
539 std::unordered_map<wxString, std::unordered_map<wxString, SCH_PIN*>>& aSyncPinMap,
540 std::unordered_map<SCH_SHEET_PATH, bool>& aCache )
541{
542 auto cacheIt = aCache.find( aSheetPath );
543
544 if( cacheIt != aCache.end() )
545 return cacheIt->second;
546
547 // Iterate over children
548 for( const SCH_SHEET_PATH& candidate : aSchematicSheetList )
549 {
550 if( candidate == aSheetPath || !candidate.IsContainedWithin( aSheetPath ) )
551 continue;
552
553 bool childRet = sheetContainsOnlyWantedItems( aSchematicSheetList, candidate, aSyncSymMap,
554 aSyncPinMap, aCache );
555
556 if( !childRet )
557 {
558 aCache.emplace( aSheetPath, false );
559 return false;
560 }
561 }
562
563 SCH_REFERENCE_LIST references;
564 aSheetPath.GetSymbols( references, false, true );
565
566 if( references.GetCount() == 0 ) // Empty sheet, obviously do not contain wanted items
567 {
568 aCache.emplace( aSheetPath, false );
569 return false;
570 }
571
572 for( unsigned ii = 0; ii < references.GetCount(); ii++ )
573 {
574 SCH_REFERENCE& schRef = references[ii];
575
576 if( schRef.IsSplitNeeded() )
577 schRef.Split();
578
579 wxString refNum = schRef.GetRefNumber();
580 wxString fullRef = schRef.GetRef() + refNum;
581
582 // Skip power symbols
583 if( fullRef.StartsWith( wxS( "#" ) ) )
584 continue;
585
586 // Unannotated symbols are not supported
587 if( refNum.compare( wxS( "?" ) ) == 0 )
588 continue;
589
590 if( aSyncSymMap.find( fullRef ) == aSyncSymMap.end() )
591 {
592 aCache.emplace( aSheetPath, false );
593 return false; // Some symbol is not wanted.
594 }
595
596 if( aSyncPinMap.find( fullRef ) != aSyncPinMap.end() )
597 {
598 aCache.emplace( aSheetPath, false );
599 return false; // Looking for specific pins, so can't be mapped
600 }
601 }
602
603 aCache.emplace( aSheetPath, true );
604 return true;
605}
606
607
608std::optional<std::tuple<SCH_SHEET_PATH, SCH_ITEM*, std::vector<SCH_ITEM*>>>
609findItemsFromSyncSelection( const SCHEMATIC& aSchematic, const std::string aSyncStr,
610 bool aFocusOnFirst )
611{
612 wxArrayString syncArray = wxStringTokenize( aSyncStr, wxS( "," ) );
613
614 std::unordered_map<wxString, std::vector<SCH_REFERENCE>> syncSymMap;
615 std::unordered_map<wxString, std::unordered_map<wxString, SCH_PIN*>> syncPinMap;
616 std::unordered_map<SCH_SHEET_PATH, double> symScores;
617 std::unordered_map<SCH_SHEET_PATH, bool> fullyWantedCache;
618
619 std::optional<wxString> focusSymbol;
620 std::optional<std::pair<wxString, wxString>> focusPin;
621 std::unordered_map<SCH_SHEET_PATH, std::vector<SCH_ITEM*>> focusItemResults;
622
623 const SCH_SHEET_LIST allSheetsList = aSchematic.Hierarchy();
624
625 // In orderedSheets, the current sheet comes first.
626 std::vector<SCH_SHEET_PATH> orderedSheets;
627 orderedSheets.reserve( allSheetsList.size() );
628 orderedSheets.push_back( aSchematic.CurrentSheet() );
629
630 for( const SCH_SHEET_PATH& sheetPath : allSheetsList )
631 {
632 if( sheetPath != aSchematic.CurrentSheet() )
633 orderedSheets.push_back( sheetPath );
634 }
635
636 // Init sync maps from the sync string
637 for( size_t i = 0; i < syncArray.size(); i++ )
638 {
639 wxString syncEntry = syncArray[i];
640
641 if( syncEntry.empty() )
642 continue;
643
644 wxString syncData = syncEntry.substr( 1 );
645
646 switch( syncEntry.GetChar( 0 ).GetValue() )
647 {
648 case 'F': // Select by footprint: F<Reference>
649 {
650 wxString symRef = UnescapeString( syncData );
651
652 if( aFocusOnFirst && ( i == 0 ) )
653 focusSymbol = symRef;
654
655 syncSymMap[symRef] = std::vector<SCH_REFERENCE>();
656 break;
657 }
658
659 case 'P': // Select by pad: P<Footprint reference>/<Pad number>
660 {
661 wxString symRef = UnescapeString( syncData.BeforeFirst( '/' ) );
662 wxString padNum = UnescapeString( syncData.AfterFirst( '/' ) );
663
664 if( aFocusOnFirst && ( i == 0 ) )
665 focusPin = std::make_pair( symRef, padNum );
666
667 syncPinMap[symRef][padNum] = nullptr;
668 break;
669 }
670
671 default:
672 break;
673 }
674 }
675
676 // Lambda definitions
677 auto flattenSyncMaps =
678 [&syncSymMap, &syncPinMap]() -> std::vector<SCH_ITEM*>
679 {
680 std::vector<SCH_ITEM*> allVec;
681
682 for( const auto& [symRef, symbols] : syncSymMap )
683 {
684 for( const SCH_REFERENCE& ref : symbols )
685 allVec.push_back( ref.GetSymbol() );
686 }
687
688 for( const auto& [symRef, pinMap] : syncPinMap )
689 {
690 for( const auto& [padNum, pin] : pinMap )
691 {
692 if( pin )
693 allVec.push_back( pin );
694 }
695 }
696
697 return allVec;
698 };
699
700 auto clearSyncMaps =
701 [&syncSymMap, &syncPinMap]()
702 {
703 for( auto& [symRef, symbols] : syncSymMap )
704 symbols.clear();
705
706 for( auto& [reference, pins] : syncPinMap )
707 {
708 for( auto& [number, pin] : pins )
709 pin = nullptr;
710 }
711 };
712
713 auto syncMapsValuesEmpty =
714 [&syncSymMap, &syncPinMap]() -> bool
715 {
716 for( const auto& [symRef, symbols] : syncSymMap )
717 {
718 if( symbols.size() > 0 )
719 return false;
720 }
721
722 for( const auto& [symRef, pins] : syncPinMap )
723 {
724 for( const auto& [padNum, pin] : pins )
725 {
726 if( pin )
727 return false;
728 }
729 }
730
731 return true;
732 };
733
734 auto checkFocusItems =
735 [&]( const SCH_SHEET_PATH& aSheet )
736 {
737 if( focusSymbol )
738 {
739 auto findIt = syncSymMap.find( *focusSymbol );
740
741 if( findIt != syncSymMap.end() )
742 {
743 if( findIt->second.size() > 0 )
744 focusItemResults[aSheet].push_back( findIt->second.front().GetSymbol() );
745 }
746 }
747 else if( focusPin )
748 {
749 auto findIt = syncPinMap.find( focusPin->first );
750
751 if( findIt != syncPinMap.end() )
752 {
753 if( findIt->second[focusPin->second] )
754 focusItemResults[aSheet].push_back( findIt->second[focusPin->second] );
755 }
756 }
757 };
758
759 auto makeRetForSheet =
760 [&]( const SCH_SHEET_PATH& aSheet, SCH_ITEM* aFocusItem )
761 {
762 clearSyncMaps();
763
764 // Fill sync maps
765 findSymbolsAndPins( allSheetsList, aSheet, syncSymMap, syncPinMap );
766 std::vector<SCH_ITEM*> itemsVector = flattenSyncMaps();
767
768 // Add fully wanted sheets to vector
769 for( SCH_ITEM* item : aSheet.LastScreen()->Items().OfType( SCH_SHEET_T ) )
770 {
771 KIID_PATH kiidPath = aSheet.Path();
772 kiidPath.push_back( item->m_Uuid );
773
774 std::optional<SCH_SHEET_PATH> subsheetPath =
775 allSheetsList.GetSheetPathByKIIDPath( kiidPath );
776
777 if( !subsheetPath )
778 continue;
779
780 if( sheetContainsOnlyWantedItems( allSheetsList, *subsheetPath, syncSymMap,
781 syncPinMap, fullyWantedCache ) )
782 {
783 itemsVector.push_back( item );
784 }
785 }
786
787 return std::make_tuple( aSheet, aFocusItem, itemsVector );
788 };
789
790 if( aFocusOnFirst )
791 {
792 for( const SCH_SHEET_PATH& sheetPath : orderedSheets )
793 {
794 clearSyncMaps();
795
796 findSymbolsAndPins( allSheetsList, sheetPath, syncSymMap, syncPinMap );
797
798 checkFocusItems( sheetPath );
799 }
800
801 if( focusItemResults.size() > 0 )
802 {
803 for( const SCH_SHEET_PATH& sheetPath : orderedSheets )
804 {
805 const std::vector<SCH_ITEM*>& items = focusItemResults[sheetPath];
806
807 if( !items.empty() )
808 return makeRetForSheet( sheetPath, items.front() );
809 }
810 }
811 }
812 else
813 {
814 for( const SCH_SHEET_PATH& sheetPath : orderedSheets )
815 {
816 clearSyncMaps();
817
818 findSymbolsAndPins( allSheetsList, sheetPath, syncSymMap, syncPinMap );
819
820 if( !syncMapsValuesEmpty() )
821 {
822 // Something found on sheet
823 return makeRetForSheet( sheetPath, nullptr );
824 }
825 }
826 }
827
828 return std::nullopt;
829}
830
831
833{
834 std::string& payload = mail.GetPayload();
835
836 switch( mail.Command() )
837 {
838 case MAIL_CROSS_PROBE:
839 ExecuteRemoteCommand( payload.c_str() );
840 break;
841
842 case MAIL_SELECTION:
843 if( !eeconfig()->m_CrossProbing.on_selection )
844 break;
845
847
849 {
850 // $SELECT: 0,<spec1>,<spec2>,<spec3>
851 // Try to select specified items.
852
853 // $SELECT: 1,<spec1>,<spec2>,<spec3>
854 // Select and focus on <spec1> item, select other specified items that are on the same sheet.
855
856 std::string prefix = "$SELECT: ";
857
858 std::string paramStr = payload.substr( prefix.size() );
859
860 if( paramStr.size() < 2 ) // Empty/broken command: we need at least 2 chars for sync string.
861 break;
862
863 std::string syncStr = paramStr.substr( 2 );
864
865 bool focusOnFirst = ( paramStr[0] == '1' );
866
867 std::optional<std::tuple<SCH_SHEET_PATH, SCH_ITEM*, std::vector<SCH_ITEM*>>> findRet =
868 findItemsFromSyncSelection( Schematic(), syncStr, focusOnFirst );
869
870 if( findRet )
871 {
872 auto& [sheetPath, focusItem, items] = *findRet;
873
874 m_syncingPcbToSchSelection = true; // recursion guard
875
876 GetToolManager()->GetTool<EE_SELECTION_TOOL>()->SyncSelection( sheetPath, focusItem,
877 items );
878
880 }
881
882 break;
883 }
884
886 {
887 if( !payload.empty() )
888 {
889 wxString annotationMessage( payload );
890
891 // Ensure schematic is OK for netlist creation (especially that it is fully annotated):
892 if( !ReadyToNetlist( annotationMessage ) )
893 return;
894 }
895
896 if( ADVANCED_CFG::GetCfg().m_IncrementalConnectivity )
898
899 NETLIST_EXPORTER_KICAD exporter( &Schematic() );
900 STRING_FORMATTER formatter;
901
902 exporter.Format( &formatter, GNL_ALL | GNL_OPT_KICAD );
903
904 payload = formatter.GetString();
905 break;
906 }
907
909 {
910 KIID uuid( payload );
912
913 if( SCH_ITEM* item = m_schematic->GetItem( uuid, &path ) )
914 {
915 if( item->Type() == SCH_SHEET_T )
916 payload = static_cast<SCH_SHEET*>( item )->GetShownName( false );
917 else if( item->Type() == SCH_SYMBOL_T )
918 payload = static_cast<SCH_SYMBOL*>( item )->GetRef( &path, true );
919 else
920 payload = item->GetFriendlyName();
921 }
922
923 break;
924 }
925
927 try
928 {
930 controlTool->AssignFootprints( payload );
931 }
932 catch( const IO_ERROR& )
933 {
934 }
935 break;
936
937 case MAIL_SCH_REFRESH:
938 {
940
942 GetCanvas()->Refresh();
943 break;
944 }
945
946 case MAIL_IMPORT_FILE:
947 {
948 // Extract file format type and path (plugin type, path and properties keys, values separated with \n)
949 std::stringstream ss( payload );
950 char delim = '\n';
951
952 std::string formatStr;
953 wxCHECK( std::getline( ss, formatStr, delim ), /* void */ );
954
955 std::string fnameStr;
956 wxCHECK( std::getline( ss, fnameStr, delim ), /* void */ );
957 wxASSERT( !fnameStr.empty() );
958
959 int importFormat;
960
961 try
962 {
963 importFormat = std::stoi( formatStr );
964 }
965 catch( std::invalid_argument& )
966 {
967 wxFAIL;
968 importFormat = -1;
969 }
970
971 std::map<std::string, UTF8> props;
972
973 do
974 {
975 std::string key, value;
976
977 if( !std::getline( ss, key, delim ) )
978 break;
979
980 std::getline( ss, value, delim ); // We may want an empty string as value
981
982 props.emplace( key, value );
983
984 } while( true );
985
986 if( importFormat >= 0 )
987 importFile( fnameStr, importFormat, props.empty() ? nullptr : &props );
988
989 break;
990 }
991
992 case MAIL_SCH_SAVE:
993 if( SaveProject() )
994 payload = "success";
995
996 break;
997
998 case MAIL_SCH_UPDATE:
1000 break;
1001
1002 case MAIL_RELOAD_LIB:
1004 SyncView();
1005 break;
1006
1007 default:;
1008
1009 }
1010}
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
static TOOL_ACTION showSymbolLibTable
Definition: actions.h:229
static TOOL_ACTION updateSchematicFromPcb
Definition: actions.h:215
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
CROSS_PROBING_SETTINGS m_CrossProbing
Definition: app_settings.h:170
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
const KIID m_Uuid
Definition: eda_item.h:489
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:94
static TOOL_ACTION runERC
Inspection and Editing.
Definition: ee_actions.h:156
static TOOL_ACTION updateNetHighlighting
Definition: ee_actions.h:310
EE_TYPE OfType(KICAD_T aType) const
Definition: sch_rtree.h:238
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:77
bool IsSingle() const
Is this KIFACE running under single_top?
Definition: kiface_base.h:107
void UpdateAllItems(int aUpdateFlags)
Update all items in the view according to the given flags.
Definition: view.cpp:1535
Definition: kiid.h:49
wxString AsString() const
Definition: kiid.cpp:238
Carry a payload from one KIWAY_PLAYER to another within a PROJECT.
Definition: kiway_express.h:40
std::string & GetPayload()
Return the payload, which can be any text but it typically self identifying s-expression.
Definition: kiway_express.h:58
MAIL_T Command()
Returns the MAIL_T associated with this mail.
Definition: kiway_express.h:50
virtual void ExpressMail(FRAME_T aDestination, MAIL_T aCommand, std::string &aPayload, wxWindow *aSource=nullptr)
Send aPayload to aDestination from aSource.
Definition: kiway.cpp:527
Generate the KiCad netlist format supported by Pcbnew.
void Format(OUTPUTFORMATTER *aOutputFormatter, int aCtl)
Output this s-expression netlist into aOutputFormatter.
Holds all the data relating to one schematic.
Definition: schematic.h:77
SCH_SHEET_PATH & CurrentSheet() const override
Definition: schematic.h:152
void SetCurrentSheet(const SCH_SHEET_PATH &aPath) override
Definition: schematic.h:157
SCH_SHEET_LIST Hierarchy() const override
Return the full schematic flattened hierarchical sheet list.
Definition: schematic.cpp:214
SCH_ITEM * GetItem(const KIID &aID, SCH_SHEET_PATH *aPathOut=nullptr) const
Definition: schematic.h:116
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
void SyncView()
Mark all items for refresh.
EESCHEMA_SETTINGS * eeconfig() const
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
bool IsNet() const
const std::vector< std::shared_ptr< SCH_CONNECTION > > AllMembers() const
wxString Name(bool aIgnoreSheet=false) const
const std::vector< std::shared_ptr< SCH_CONNECTION > > & Members() const
KIGFX::SCH_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
Handle actions specific to the schematic editor.
void AssignFootprints(const std::string &aChangedSetOfReferences)
SCH_ITEM * FindSymbolAndItem(const wxString *aPath, const wxString *aReference, bool aSearchHierarchy, SCH_SEARCH_T aSearchType, const wxString &aSearchText)
Find a symbol in the schematic and an item in this symbol and select it.
bool ReadyToNetlist(const wxString &aAnnotateMessage)
Check if we are ready to write a netlist file for the current schematic.
bool m_syncingPcbToSchSelection
void SendSelectItemsToPcb(const std::vector< EDA_ITEM * > &aItems, bool aForce)
Send items to board editor for selection.
void SendCrossProbeClearHighlight()
Tell Pcbnew to clear the existing highlighted net, if one exists.
SCHEMATIC * m_schematic
The currently loaded schematic.
SCH_SHEET_PATH & GetCurrentSheet() const
SCHEMATIC & Schematic() const
void ExecuteRemoteCommand(const char *cmdline) override
Execute a remote command sent via a socket on port KICAD_SCH_PORT_SERVICE_NUMBER (which defaults to 4...
void RefreshNetNavigator(const NET_NAVIGATOR_ITEM_DATA *aSelection=nullptr)
bool importFile(const wxString &aFileName, int aFileType, const std::map< std::string, UTF8 > *aProperties=nullptr)
Load the given filename but sets the path to the current project path.
void SendCrossProbeNetName(const wxString &aNetName)
Send a net name to Pcbnew for highlighting.
void RecalculateConnections(SCH_COMMIT *aCommit, SCH_CLEANUP_FLAGS aCleanupFlags)
Generate the connection data for the entire schematic hierarchy.
void DisplayCurrentSheet()
Draw the current sheet on the display.
DESIGN_BLOCK_PANE * m_designBlocksPane
wxString m_highlightedConn
The highlighted net or bus or empty string.
void SetCrossProbeConnection(const SCH_CONNECTION *aConnection)
Send a connection (net or bus) to Pcbnew for highlighting.
void TestDanglingEnds()
Test all of the connectable objects in the schematic for unused connection points.
bool SaveProject(bool aSaveAs=false)
Save the currently-open schematic (including its hierarchy) and associated project.
void FocusOnItem(SCH_ITEM *aItem)
void KiwayMailIn(KIWAY_EXPRESS &aEvent) override
Receive KIWAY_EXPRESS messages from other players.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:166
int GetUnit() const
Definition: sch_item.h:229
Container to create a flattened list of symbols because in a complex hierarchy, a symbol can be used ...
size_t GetCount() const
A helper to define a symbol's reference designator in a schematic.
void Split()
Attempt to split the reference designator into a name (U) and number (1).
bool IsSplitNeeded()
Determine if this reference needs to be split or if it likely already has been.
SCH_SYMBOL * GetSymbol() const
wxString GetRef() const
int GetUnit() const
wxString GetRefNumber() const
EE_RTREE & Items()
Gets the full RTree, usually for iterating.
Definition: sch_screen.h:108
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
std::optional< SCH_SHEET_PATH > GetSheetPathByKIIDPath(const KIID_PATH &aPath, bool aIncludeLastSheet=true) const
Finds a SCH_SHEET_PATH that matches the provided KIID_PATH.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
void GetSymbols(SCH_REFERENCE_LIST &aReferences, bool aIncludePowerSymbols=true, bool aForceIncludeOrphanSymbols=false) const
Adds SCH_REFERENCE object to aReferences for each symbol in the sheet.
KIID_PATH Path() const
Get the sheet path as an KIID_PATH.
SCH_SCREEN * LastScreen()
bool IsContainedWithin(const SCH_SHEET_PATH &aSheetPathToTest) const
Check if this path is contained inside aSheetPathToTest.
wxString PathAsString() const
Return the path of time stamps which do not changes even when editing sheet parameters.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:57
Schematic symbol object.
Definition: sch_symbol.h:104
SCH_FIELD * GetField(MANDATORY_FIELD_T aFieldType)
Return a mandatory field in this symbol.
Definition: sch_symbol.cpp:939
std::vector< SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieve a list of the SCH_PINs for the given sheet path.
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
SCH_PIN * GetPin(const wxString &number) const
Find a symbol pin by number.
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
Definition: sch_symbol.cpp:737
Implement an OUTPUTFORMATTER to a memory buffer.
Definition: richio.h:436
const std::string & GetString()
Definition: richio.h:459
A base class for LIB_SYMBOL and SCH_SYMBOL.
Definition: symbol.h:34
virtual const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const =0
TOOL_MANAGER * m_toolManager
Definition: tools_holder.h:167
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:55
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:218
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Definition: tool_manager.h:150
#define _(s)
bool SendCommand(int aService, const std::string &aMessage)
Used by a client to sent (by a socket connection) a data to a server.
Definition: eda_dde.cpp:310
DDE server & client.
#define MSG_TO_PCB
Definition: eda_dde.h:49
std::optional< std::tuple< SCH_SHEET_PATH, SCH_ITEM *, std::vector< SCH_ITEM * > > > findItemsFromSyncSelection(const SCHEMATIC &aSchematic, const std::string aSyncStr, bool aFocusOnFirst)
bool findSymbolsAndPins(const SCH_SHEET_LIST &aSchematicSheetList, const SCH_SHEET_PATH &aSheetPath, std::unordered_map< wxString, std::vector< SCH_REFERENCE > > &aSyncSymMap, std::unordered_map< wxString, std::unordered_map< wxString, SCH_PIN * > > &aSyncPinMap, bool aRecursive=false)
bool sheetContainsOnlyWantedItems(const SCH_SHEET_LIST &aSchematicSheetList, const SCH_SHEET_PATH &aSheetPath, std::unordered_map< wxString, std::vector< SCH_REFERENCE > > &aSyncSymMap, std::unordered_map< wxString, std::unordered_map< wxString, SCH_PIN * > > &aSyncPinMap, std::unordered_map< SCH_SHEET_PATH, bool > &aCache)
@ FRAME_PCB_EDITOR
Definition: frame_type.h:42
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
@ MAIL_IMPORT_FILE
Definition: mail_type.h:48
@ MAIL_SCH_REFRESH
Definition: mail_type.h:53
@ MAIL_CROSS_PROBE
Definition: mail_type.h:39
@ MAIL_SELECTION_FORCE
Definition: mail_type.h:41
@ MAIL_SCH_SAVE
Definition: mail_type.h:43
@ MAIL_ASSIGN_FOOTPRINTS
Definition: mail_type.h:42
@ MAIL_SCH_GET_ITEM
Definition: mail_type.h:50
@ MAIL_SCH_UPDATE
Definition: mail_type.h:47
@ MAIL_SCH_GET_NETLIST
Definition: mail_type.h:49
@ MAIL_SELECTION
Definition: mail_type.h:40
@ MAIL_RELOAD_LIB
Definition: mail_type.h:56
@ ALL
All except INITIAL_ADD.
Definition: view_item.h:58
#define GNL_ALL
@ GNL_OPT_KICAD
int StrPrintf(std::string *result, const char *format,...)
This is like sprintf() but the output is appended to a std::string instead of to a character array.
Definition: richio.cpp:68
@ GLOBAL_CLEANUP
SCH_SEARCH_T
Schematic search type used by the socket link with Pcbnew.
@ HIGHLIGHT_SYMBOL
@ HIGHLIGHT_PIN
KIWAY Kiway(KFCTL_STANDALONE)
wxString UnescapeString(const wxString &aSource)
wxString From_UTF8(const char *cstring)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: string_utils.h:398
@ CTX_IPC
Definition: string_utils.h:56
Cross-probing behavior.
Definition: app_settings.h:32
bool on_selection
Synchronize the selection for multiple items too.
Definition: app_settings.h:33
bool zoom_to_fit
Zoom to fit items (ignored if center_on_items is off)
Definition: app_settings.h:35
bool center_on_items
Automatically pan to cross-probed items.
Definition: app_settings.h:34
bool auto_highlight
Automatically turn on highlight mode in the target frame.
Definition: app_settings.h:36
@ REFERENCE_FIELD
Field Reference of part, i.e. "IC21".
@ SCH_SYMBOL_T
Definition: typeinfo.h:172
@ SCH_SHEET_T
Definition: typeinfo.h:174
@ SCH_PIN_T
Definition: typeinfo.h:153