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 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 <tools/ee_actions.h>
39#include <advanced_config.h>
40#include <wx/log.h>
41
42SCH_ITEM* SCH_EDITOR_CONTROL::FindSymbolAndItem( const wxString* aPath, const wxString* aReference,
43 bool aSearchHierarchy, SCH_SEARCH_T aSearchType,
44 const wxString& aSearchText )
45{
46 SCH_SHEET_PATH* sheetWithSymbolFound = nullptr;
47 SCH_SYMBOL* symbol = nullptr;
48 SCH_PIN* pin = nullptr;
49 SCH_SHEET_LIST sheetList;
50 SCH_ITEM* foundItem = nullptr;
51
52 if( !aSearchHierarchy )
53 sheetList.push_back( m_frame->GetCurrentSheet() );
54 else
55 sheetList = m_frame->Schematic().GetSheets();
56
57 for( SCH_SHEET_PATH& sheet : sheetList )
58 {
59 SCH_SCREEN* screen = sheet.LastScreen();
60
61 for( EDA_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
62 {
63 SCH_SYMBOL* candidate = static_cast<SCH_SYMBOL*>( item );
64
65 // Search by path if specified, otherwise search by reference
66 bool found = false;
67
68 if( aPath )
69 {
70 wxString path = sheet.PathAsString() + candidate->m_Uuid.AsString();
71 found = ( *aPath == path );
72 }
73 else
74 {
75 found = ( aReference && aReference->CmpNoCase( candidate->GetRef( &sheet ) ) == 0 );
76 }
77
78 if( found )
79 {
80 symbol = candidate;
81 sheetWithSymbolFound = &sheet;
82
83 if( aSearchType == HIGHLIGHT_PIN )
84 {
85 pin = symbol->GetPin( aSearchText );
86
87 // Ensure we have found the right unit in case of multi-units symbol
88 if( pin )
89 {
90 int unit = pin->GetLibPin()->GetUnit();
91
92 if( unit != 0 && unit != symbol->GetUnit() )
93 {
94 pin = nullptr;
95 continue;
96 }
97
98 // Get pin position in true schematic coordinate
99 foundItem = pin;
100 break;
101 }
102 }
103 else
104 {
105 foundItem = symbol;
106 break;
107 }
108 }
109 }
110
111 if( foundItem )
112 break;
113 }
114
115 CROSS_PROBING_SETTINGS& crossProbingSettings = m_frame->eeconfig()->m_CrossProbing;
116
117 if( symbol )
118 {
119 if( *sheetWithSymbolFound != m_frame->GetCurrentSheet() )
120 {
121 m_frame->Schematic().SetCurrentSheet( *sheetWithSymbolFound );
123 }
124
125 if( crossProbingSettings.center_on_items )
126 {
127 if( crossProbingSettings.zoom_to_fit )
128 {
129 BOX2I bbox = symbol->GetBoundingBox();
130
131 m_toolMgr->GetTool<EE_SELECTION_TOOL>()->ZoomFitCrossProbeBBox( bbox );
132 }
133
134 if( pin )
136 else
137 m_frame->FocusOnItem( symbol );
138 }
139 }
140
141 /* Print diag */
142 wxString msg;
143 wxString displayRef;
144
145 if( aReference )
146 displayRef = *aReference;
147 else if( aPath )
148 displayRef = *aPath;
149
150 if( symbol )
151 {
152 if( aSearchType == HIGHLIGHT_PIN )
153 {
154 if( foundItem )
155 msg.Printf( _( "%s pin %s found" ), displayRef, aSearchText );
156 else
157 msg.Printf( _( "%s found but pin %s not found" ), displayRef, aSearchText );
158 }
159 else
160 {
161 msg.Printf( _( "%s found" ), displayRef );
162 }
163 }
164 else
165 {
166 msg.Printf( _( "%s not found" ), displayRef );
167 }
168
169 m_frame->SetStatusText( msg );
171
172 return foundItem;
173}
174
175
176/* Execute a remote command sent via a socket on port KICAD_PCB_PORT_SERVICE_NUMBER
177 *
178 * Commands are:
179 *
180 * $PART: "reference" Put cursor on symbol.
181 * $PART: "reference" $REF: "ref" Put cursor on symbol reference.
182 * $PART: "reference" $VAL: "value" Put cursor on symbol value.
183 * $PART: "reference" $PAD: "pin name" Put cursor on the symbol pin.
184 * $NET: "netname" Highlight a specified net
185 * $CLEAR: "HIGHLIGHTED" Clear symbols highlight
186 *
187 * $CONFIG Show the Manage Symbol Libraries dialog
188 * $ERC Show the ERC dialog
189 */
190void SCH_EDIT_FRAME::ExecuteRemoteCommand( const char* cmdline )
191{
193 char line[1024];
194
195 strncpy( line, cmdline, sizeof( line ) - 1 );
196 line[ sizeof( line ) - 1 ] = '\0';
197
198 char* idcmd = strtok( line, " \n\r" );
199 char* text = strtok( nullptr, "\"\n\r" );
200
201 if( idcmd == nullptr )
202 return;
203
204 CROSS_PROBING_SETTINGS& crossProbingSettings = eeconfig()->m_CrossProbing;
205
206 if( strcmp( idcmd, "$CONFIG" ) == 0 )
207 {
209 return;
210 }
211 else if( strcmp( idcmd, "$ERC" ) == 0 )
212 {
214 return;
215 }
216 else if( strcmp( idcmd, "$NET:" ) == 0 )
217 {
218 if( !crossProbingSettings.auto_highlight )
219 return;
220
221 wxString netName = From_UTF8( text );
222
223 if( auto sg = Schematic().ConnectionGraph()->FindFirstSubgraphByName( netName ) )
224 m_highlightedConn = sg->GetDriverConnection()->Name();
225 else
226 m_highlightedConn = wxEmptyString;
227
229
230 SetStatusText( _( "Selected net:" ) + wxS( " " ) + UnescapeString( netName ) );
231 return;
232 }
233 else if( strcmp( idcmd, "$CLEAR:" ) == 0 )
234 {
235 // Cross-probing is now done through selection so we no longer need a clear command
236 return;
237 }
238
239 if( !crossProbingSettings.on_selection )
240 return;
241
242 if( text == nullptr )
243 return;
244
245 if( strcmp( idcmd, "$PART:" ) != 0 )
246 return;
247
248 wxString part_ref = From_UTF8( text );
249
250 /* look for a complement */
251 idcmd = strtok( nullptr, " \n\r" );
252
253 if( idcmd == nullptr ) // Highlight symbol only (from CvPcb or Pcbnew)
254 {
255 // Highlight symbol part_ref, or clear Highlight, if part_ref is not existing
256 editor->FindSymbolAndItem( nullptr, &part_ref, true, HIGHLIGHT_SYMBOL, wxEmptyString );
257 return;
258 }
259
260 text = strtok( nullptr, "\"\n\r" );
261
262 if( text == nullptr )
263 return;
264
265 wxString msg = From_UTF8( text );
266
267 if( strcmp( idcmd, "$REF:" ) == 0 )
268 {
269 // Highlighting the reference itself isn't actually that useful, and it's harder to
270 // see. Highlight the parent and display the message.
271 editor->FindSymbolAndItem( nullptr, &part_ref, true, HIGHLIGHT_SYMBOL, msg );
272 }
273 else if( strcmp( idcmd, "$VAL:" ) == 0 )
274 {
275 // Highlighting the value itself isn't actually that useful, and it's harder to see.
276 // Highlight the parent and display the message.
277 editor->FindSymbolAndItem( nullptr, &part_ref, true, HIGHLIGHT_SYMBOL, msg );
278 }
279 else if( strcmp( idcmd, "$PAD:" ) == 0 )
280 {
281 editor->FindSymbolAndItem( nullptr, &part_ref, true, HIGHLIGHT_PIN, msg );
282 }
283 else
284 {
285 editor->FindSymbolAndItem( nullptr, &part_ref, true, HIGHLIGHT_SYMBOL, wxEmptyString );
286 }
287}
288
289
290void SCH_EDIT_FRAME::SendSelectItemsToPcb( const std::vector<EDA_ITEM*>& aItems, bool aForce )
291{
292 std::vector<wxString> parts;
293
294 for( EDA_ITEM* item : aItems )
295 {
296 switch( item->Type() )
297 {
298 case SCH_SYMBOL_T:
299 {
300 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
301 wxString ref = symbol->GetField( REFERENCE_FIELD )->GetText();
302
303 parts.push_back( wxT( "F" ) + EscapeString( ref, CTX_IPC ) );
304 break;
305 }
306
307 case SCH_SHEET_T:
308 {
309 // For cross probing, we need the full path of the sheet, because
310 // we search by the footprint path prefix in the PCB editor
311 wxString full_path = GetCurrentSheet().PathAsString() + item->m_Uuid.AsString();
312
313 parts.push_back( wxT( "S" ) + full_path );
314 break;
315 }
316
317 case SCH_PIN_T:
318 {
319 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
320 SCH_SYMBOL* symbol = pin->GetParentSymbol();
321 wxString ref = symbol->GetField( REFERENCE_FIELD )->GetText();
322
323 parts.push_back( wxT( "P" ) + EscapeString( ref, CTX_IPC ) + wxT( "/" )
324 + EscapeString( pin->GetShownNumber(), CTX_IPC ) );
325 break;
326 }
327
328 default:
329 break;
330 }
331 }
332
333 if( parts.empty() )
334 return;
335
336 std::string command = "$SELECT: 0,";
337
338 for( wxString part : parts )
339 {
340 command += part;
341 command += ",";
342 }
343
344 command.pop_back();
345
346 if( Kiface().IsSingle() )
347 {
348 SendCommand( MSG_TO_PCB, command );
349 }
350 else
351 {
352 // Typically ExpressMail is going to be s-expression packets, but since
353 // we have existing interpreter of the selection packet on the other
354 // side in place, we use that here.
356 command, this );
357 }
358}
359
360
361void SCH_EDIT_FRAME::SendCrossProbeNetName( const wxString& aNetName )
362{
363 // The command is a keyword followed by a quoted string.
364
365 std::string packet = StrPrintf( "$NET: \"%s\"", TO_UTF8( aNetName ) );
366
367 if( !packet.empty() )
368 {
369 if( Kiface().IsSingle() )
370 {
371 SendCommand( MSG_TO_PCB, packet );
372 }
373 else
374 {
375 // Typically ExpressMail is going to be s-expression packets, but since
376 // we have existing interpreter of the cross probe packet on the other
377 // side in place, we use that here.
379 }
380 }
381}
382
383
385{
386 if( !aConnection )
387 {
389 return;
390 }
391
392 if( aConnection->IsNet() )
393 {
394 SendCrossProbeNetName( aConnection->Name() );
395 return;
396 }
397
398 if( aConnection->Members().empty() )
399 return;
400
401 auto all_members = aConnection->AllMembers();
402
403 wxString nets = all_members[0]->Name();
404
405 if( all_members.size() == 1 )
406 {
407 SendCrossProbeNetName( nets );
408 return;
409 }
410
411 // TODO: This could be replaced by just sending the bus name once we have bus contents
412 // included as part of the netlist sent from Eeschema to Pcbnew (and thus Pcbnew can
413 // natively keep track of bus membership)
414
415 for( size_t i = 1; i < all_members.size(); i++ )
416 nets << "," << all_members[i]->Name();
417
418 std::string packet = StrPrintf( "$NETS: \"%s\"", TO_UTF8( nets ) );
419
420 if( !packet.empty() )
421 {
422 if( Kiface().IsSingle() )
423 SendCommand( MSG_TO_PCB, packet );
424 else
425 {
426 // Typically ExpressMail is going to be s-expression packets, but since
427 // we have existing interpreter of the cross probe packet on the other
428 // side in place, we use that here.
430 }
431 }
432}
433
434
436{
437 std::string packet = "$CLEAR\n";
438
439 if( Kiface().IsSingle() )
440 {
441 SendCommand( MSG_TO_PCB, packet );
442 }
443 else
444 {
445 // Typically ExpressMail is going to be s-expression packets, but since
446 // we have existing interpreter of the cross probe packet on the other
447 // side in place, we use that here.
449 }
450}
451
452
454 const SCHEMATIC& aSchematic, const SCH_SHEET_PATH& aSheetPath,
455 std::unordered_map<wxString, std::vector<SCH_REFERENCE>>& aSyncSymMap,
456 std::unordered_map<wxString, std::unordered_map<wxString, SCH_PIN*>>& aSyncPinMap,
457 bool aRecursive = false )
458{
459 if( aRecursive )
460 {
461 // Iterate over children
462 for( const SCH_SHEET_PATH& candidate : aSchematic.GetSheets() )
463 {
464 if( candidate == aSheetPath || !candidate.IsContainedWithin( aSheetPath ) )
465 continue;
466
467 findSymbolsAndPins( aSchematic, candidate, aSyncSymMap, aSyncPinMap, aRecursive );
468 }
469 }
470
471 SCH_REFERENCE_LIST references;
472
473 aSheetPath.GetSymbols( references, false, true );
474
475 for( unsigned ii = 0; ii < references.GetCount(); ii++ )
476 {
477 SCH_REFERENCE& schRef = references[ii];
478
479 if( schRef.IsSplitNeeded() )
480 schRef.Split();
481
482 SCH_SYMBOL* symbol = schRef.GetSymbol();
483 wxString refNum = schRef.GetRefNumber();
484 wxString fullRef = schRef.GetRef() + refNum;
485
486 // Skip power symbols
487 if( fullRef.StartsWith( wxS( "#" ) ) )
488 continue;
489
490 // Unannotated symbols are not supported
491 if( refNum.compare( wxS( "?" ) ) == 0 )
492 continue;
493
494 // Look for whole footprint
495 auto symMatchIt = aSyncSymMap.find( fullRef );
496
497 if( symMatchIt != aSyncSymMap.end() )
498 {
499 symMatchIt->second.emplace_back( schRef );
500
501 // Whole footprint was selected, no need to select pins
502 continue;
503 }
504
505 // Look for pins
506 auto symPinMatchIt = aSyncPinMap.find( fullRef );
507
508 if( symPinMatchIt != aSyncPinMap.end() )
509 {
510 std::unordered_map<wxString, SCH_PIN*>& pinMap = symPinMatchIt->second;
511 std::vector<SCH_PIN*> pinsOnSheet = symbol->GetPins( &aSheetPath );
512
513 for( SCH_PIN* pin : pinsOnSheet )
514 {
515 int pinUnit = pin->GetLibPin()->GetUnit();
516
517 if( pinUnit > 0 && pinUnit != schRef.GetUnit() )
518 continue;
519
520 auto pinIt = pinMap.find( pin->GetNumber() );
521
522 if( pinIt != pinMap.end() )
523 pinIt->second = pin;
524 }
525 }
526 }
527
528 return false;
529}
530
531
533 const SCHEMATIC& aSchematic, const SCH_SHEET_PATH& aSheetPath,
534 std::unordered_map<wxString, std::vector<SCH_REFERENCE>>& aSyncSymMap,
535 std::unordered_map<wxString, std::unordered_map<wxString, SCH_PIN*>>& aSyncPinMap,
536 std::unordered_map<SCH_SHEET_PATH, bool>& aCache )
537{
538 auto cacheIt = aCache.find( aSheetPath );
539
540 if( cacheIt != aCache.end() )
541 return cacheIt->second;
542
543 // Iterate over children
544 for( const SCH_SHEET_PATH& candidate : aSchematic.GetSheets() )
545 {
546 if( candidate == aSheetPath || !candidate.IsContainedWithin( aSheetPath ) )
547 continue;
548
549 bool childRet = sheetContainsOnlyWantedItems( aSchematic, candidate, aSyncSymMap,
550 aSyncPinMap, aCache );
551
552 if( !childRet )
553 {
554 aCache.emplace( aSheetPath, false );
555 return false;
556 }
557 }
558
559 SCH_REFERENCE_LIST references;
560 aSheetPath.GetSymbols( references, false, true );
561
562 if( references.GetCount() == 0 ) // Empty sheet, obviously do not contain wanted items
563 {
564 aCache.emplace( aSheetPath, false );
565 return false;
566 }
567
568 for( unsigned ii = 0; ii < references.GetCount(); ii++ )
569 {
570 SCH_REFERENCE& schRef = references[ii];
571
572 if( schRef.IsSplitNeeded() )
573 schRef.Split();
574
575 wxString refNum = schRef.GetRefNumber();
576 wxString fullRef = schRef.GetRef() + refNum;
577
578 // Skip power symbols
579 if( fullRef.StartsWith( wxS( "#" ) ) )
580 continue;
581
582 // Unannotated symbols are not supported
583 if( refNum.compare( wxS( "?" ) ) == 0 )
584 continue;
585
586 if( aSyncSymMap.find( fullRef ) == aSyncSymMap.end() )
587 {
588 aCache.emplace( aSheetPath, false );
589 return false; // Some symbol is not wanted.
590 }
591
592 if( aSyncPinMap.find( fullRef ) != aSyncPinMap.end() )
593 {
594 aCache.emplace( aSheetPath, false );
595 return false; // Looking for specific pins, so can't be mapped
596 }
597 }
598
599 aCache.emplace( aSheetPath, true );
600 return true;
601}
602
603
604std::optional<std::tuple<SCH_SHEET_PATH, SCH_ITEM*, std::vector<SCH_ITEM*>>>
605findItemsFromSyncSelection( const SCHEMATIC& aSchematic, const std::string aSyncStr,
606 bool aFocusOnFirst )
607{
608 wxArrayString syncArray = wxStringTokenize( aSyncStr, wxS( "," ) );
609
610 std::unordered_map<wxString, std::vector<SCH_REFERENCE>> syncSymMap;
611 std::unordered_map<wxString, std::unordered_map<wxString, SCH_PIN*>> syncPinMap;
612 std::unordered_map<SCH_SHEET_PATH, double> symScores;
613 std::unordered_map<SCH_SHEET_PATH, bool> fullyWantedCache;
614
615 std::optional<wxString> focusSymbol;
616 std::optional<std::pair<wxString, wxString>> focusPin;
617 std::unordered_map<SCH_SHEET_PATH, std::vector<SCH_ITEM*>> focusItemResults;
618
619 const SCH_SHEET_LIST allSheetsList = aSchematic.GetSheets();
620
621 // In orderedSheets, the current sheet comes first.
622 SCH_SHEET_PATHS orderedSheets;
623 orderedSheets.reserve( allSheetsList.size() );
624 orderedSheets.push_back( aSchematic.CurrentSheet() );
625
626 for( const SCH_SHEET_PATH& sheetPath : allSheetsList )
627 {
628 if( sheetPath != aSchematic.CurrentSheet() )
629 orderedSheets.push_back( sheetPath );
630 }
631
632 // Init sync maps from the sync string
633 for( size_t i = 0; i < syncArray.size(); i++ )
634 {
635 wxString syncEntry = syncArray[i];
636
637 if( syncEntry.empty() )
638 continue;
639
640 wxString syncData = syncEntry.substr( 1 );
641
642 switch( syncEntry.GetChar( 0 ).GetValue() )
643 {
644 case 'F': // Select by footprint: F<Reference>
645 {
646 wxString symRef = UnescapeString( syncData );
647
648 if( aFocusOnFirst && ( i == 0 ) )
649 focusSymbol = symRef;
650
651 syncSymMap[symRef] = std::vector<SCH_REFERENCE>();
652 break;
653 }
654 case 'P': // Select by pad: P<Footprint reference>/<Pad number>
655 {
656 wxString symRef = UnescapeString( syncData.BeforeFirst( '/' ) );
657 wxString padNum = UnescapeString( syncData.AfterFirst( '/' ) );
658
659 if( aFocusOnFirst && ( i == 0 ) )
660 focusPin = std::make_pair( symRef, padNum );
661
662 syncPinMap[symRef][padNum] = nullptr;
663 break;
664 }
665 default: break;
666 }
667 }
668
669 // Lambda definitions
670 auto flattenSyncMaps = [&syncSymMap, &syncPinMap]() -> std::vector<SCH_ITEM*>
671 {
672 std::vector<SCH_ITEM*> allVec;
673
674 for( auto const& pairSym : syncSymMap )
675 {
676 for( const SCH_REFERENCE& ref : pairSym.second )
677 {
678 allVec.push_back( ref.GetSymbol() );
679 }
680 }
681
682 for( auto const& pairSym : syncPinMap )
683 {
684 for( auto const& pairPin : pairSym.second )
685 {
686 if( pairPin.second )
687 allVec.push_back( pairPin.second );
688 }
689 }
690
691 return allVec;
692 };
693
694 auto clearSyncMaps = [&syncSymMap, &syncPinMap]()
695 {
696 for( auto& pairSym : syncSymMap )
697 {
698 pairSym.second.clear();
699 }
700
701 for( auto& pairSym : syncPinMap )
702 {
703 for( auto& pairPin : pairSym.second )
704 {
705 pairPin.second = nullptr;
706 }
707 }
708 };
709
710 auto syncMapsValuesEmpty = [&syncSymMap, &syncPinMap]() -> bool
711 {
712 for( auto const& pairSym : syncSymMap )
713 {
714 if( pairSym.second.size() > 0 )
715 return false;
716 }
717
718 for( auto const& pairSym : syncPinMap )
719 {
720 for( auto const& pairPin : pairSym.second )
721 {
722 if( pairPin.second )
723 return false;
724 }
725 }
726
727 return true;
728 };
729
730 auto checkFocusItems = [&]( const SCH_SHEET_PATH& aSheetPath )
731 {
732 if( focusSymbol )
733 {
734 auto findIt = syncSymMap.find( *focusSymbol );
735 if( findIt != syncSymMap.end() )
736 {
737 if( findIt->second.size() > 0 )
738 {
739 focusItemResults[aSheetPath].push_back( findIt->second.front().GetSymbol() );
740 }
741 }
742 }
743 else if( focusPin )
744 {
745 auto findIt = syncPinMap.find( focusPin->first );
746 if( findIt != syncPinMap.end() )
747 {
748 if( findIt->second[focusPin->second] )
749 {
750 focusItemResults[aSheetPath].push_back( findIt->second[focusPin->second] );
751 }
752 }
753 }
754 };
755
756 auto makeRetForSheet = [&]( const SCH_SHEET_PATH& aSheet, SCH_ITEM* aFocusItem )
757 {
758 clearSyncMaps();
759
760 // Fill sync maps
761 findSymbolsAndPins( aSchematic, aSheet, syncSymMap, syncPinMap );
762 std::vector<SCH_ITEM*> itemsVector = flattenSyncMaps();
763
764 // Add fully wanted sheets to vector
765 for( SCH_ITEM* item : aSheet.LastScreen()->Items().OfType( SCH_SHEET_T ) )
766 {
767 KIID_PATH kiidPath = aSheet.Path();
768 kiidPath.push_back( item->m_Uuid );
769
770 std::optional<SCH_SHEET_PATH> subsheetPath =
771 allSheetsList.GetSheetPathByKIIDPath( kiidPath );
772
773 if( !subsheetPath )
774 continue;
775
776 if( sheetContainsOnlyWantedItems( aSchematic, *subsheetPath, syncSymMap, syncPinMap,
777 fullyWantedCache ) )
778 {
779 itemsVector.push_back( item );
780 }
781 }
782
783 return std::make_tuple( aSheet, aFocusItem, itemsVector );
784 };
785
786 if( aFocusOnFirst )
787 {
788 for( const SCH_SHEET_PATH& sheetPath : orderedSheets )
789 {
790 clearSyncMaps();
791
792 findSymbolsAndPins( aSchematic, sheetPath, syncSymMap, syncPinMap );
793
794 checkFocusItems( sheetPath );
795 }
796
797 if( focusItemResults.size() > 0 )
798 {
799 for( const SCH_SHEET_PATH& sheetPath : orderedSheets )
800 {
801 auto vec = focusItemResults[sheetPath];
802
803 if( !vec.empty() )
804 return makeRetForSheet( sheetPath, vec.front() );
805 }
806 }
807 }
808 else
809 {
810 for( const SCH_SHEET_PATH& sheetPath : orderedSheets )
811 {
812 clearSyncMaps();
813
814 findSymbolsAndPins( aSchematic, sheetPath, syncSymMap, syncPinMap );
815
816 if( !syncMapsValuesEmpty() )
817 {
818 // Something found on sheet
819 return makeRetForSheet( sheetPath, nullptr );
820 }
821 }
822 }
823
824 return std::nullopt;
825}
826
827
829{
830 std::string& payload = mail.GetPayload();
831
832 switch( mail.Command() )
833 {
834 case MAIL_CROSS_PROBE:
835 ExecuteRemoteCommand( payload.c_str() );
836 break;
837
838 case MAIL_SELECTION:
839 if( !eeconfig()->m_CrossProbing.on_selection )
840 break;
841
843
845 {
846 // $SELECT: 0,<spec1>,<spec2>,<spec3>
847 // Try to select specified items.
848
849 // $SELECT: 1,<spec1>,<spec2>,<spec3>
850 // Select and focus on <spec1> item, select other specified items that are on the same sheet.
851
852 std::string prefix = "$SELECT: ";
853
854 std::string paramStr = payload.substr( prefix.size() );
855
856 if( paramStr.size() < 2 ) // Empty/broken command: we need at least 2 chars for sync string.
857 break;
858
859 std::string syncStr = paramStr.substr( 2 );
860
861 bool focusOnFirst = ( paramStr[0] == '1' );
862
863 std::optional<std::tuple<SCH_SHEET_PATH, SCH_ITEM*, std::vector<SCH_ITEM*>>> findRet =
864 findItemsFromSyncSelection( Schematic(), syncStr, focusOnFirst );
865
866 if( findRet )
867 {
868 auto& [sheetPath, focusItem, items] = *findRet;
869
870 m_syncingPcbToSchSelection = true; // recursion guard
871
872 GetToolManager()->GetTool<EE_SELECTION_TOOL>()->SyncSelection( sheetPath, focusItem,
873 items );
874
876 }
877
878 break;
879 }
880
882 {
883 if( !payload.empty() )
884 {
885 wxString annotationMessage( payload );
886
887 // Ensure schematic is OK for netlist creation (especially that it is fully annotated):
888 if( !ReadyToNetlist( annotationMessage ) )
889 return;
890 }
891
892 if( ADVANCED_CFG::GetCfg().m_IncrementalConnectivity )
894
895 NETLIST_EXPORTER_KICAD exporter( &Schematic() );
896 STRING_FORMATTER formatter;
897
898 exporter.Format( &formatter, GNL_ALL | GNL_OPT_KICAD );
899
900 payload = formatter.GetString();
901 break;
902 }
903
905 {
906 KIID uuid( payload );
908
909 if( SCH_ITEM* item = m_schematic->GetSheets().GetItem( uuid, &path ) )
910 {
911 if( item->Type() == SCH_SHEET_T )
912 payload = static_cast<SCH_SHEET*>( item )->GetShownName( false );
913 else if( item->Type() == SCH_SYMBOL_T )
914 payload = static_cast<SCH_SYMBOL*>( item )->GetRef( &path, true );
915 else
916 payload = item->GetFriendlyName();
917 }
918
919 break;
920 }
921
923 try
924 {
926 controlTool->AssignFootprints( payload );
927 }
928 catch( const IO_ERROR& )
929 {
930 }
931 break;
932
933 case MAIL_SCH_REFRESH:
934 {
936
938 GetCanvas()->Refresh();
939 break;
940 }
941
942 case MAIL_IMPORT_FILE:
943 {
944 // Extract file format type and path (plugin type, path and properties keys, values separated with \n)
945 std::stringstream ss( payload );
946 char delim = '\n';
947
948 std::string formatStr;
949 wxCHECK( std::getline( ss, formatStr, delim ), /* void */ );
950
951 std::string fnameStr;
952 wxCHECK( std::getline( ss, fnameStr, delim ), /* void */ );
953 wxASSERT( !fnameStr.empty() );
954
955 int importFormat;
956
957 try
958 {
959 importFormat = std::stoi( formatStr );
960 }
961 catch( std::invalid_argument& )
962 {
963 wxFAIL;
964 importFormat = -1;
965 }
966
967 STRING_UTF8_MAP props;
968
969 do
970 {
971 std::string key, value;
972
973 if( !std::getline( ss, key, delim ) )
974 break;
975
976 std::getline( ss, value, delim ); // We may want an empty string as value
977
978 props.emplace( key, value );
979
980 } while( true );
981
982 if( importFormat >= 0 )
983 importFile( fnameStr, importFormat, props.empty() ? nullptr : &props );
984
985 break;
986 }
987
988 case MAIL_SCH_SAVE:
989 if( SaveProject() )
990 payload = "success";
991
992 break;
993
994 case MAIL_SCH_UPDATE:
996 break;
997
998 case MAIL_RELOAD_LIB:
999 SyncView();
1000 break;
1001
1002 default:;
1003
1004 }
1005}
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
static TOOL_ACTION showSymbolLibTable
Definition: actions.h:189
static TOOL_ACTION updateSchematicFromPcb
Definition: actions.h:176
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:155
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:85
const KIID m_Uuid
Definition: eda_item.h:482
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:95
static TOOL_ACTION runERC
Definition: ee_actions.h:151
static TOOL_ACTION updateNetHighlighting
Definition: ee_actions.h:286
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:1500
Definition: kiid.h:49
wxString AsString() const
Definition: kiid.cpp:257
Carry a payload from one KIWAY_PLAYER to another within a PROJECT.
Definition: kiway_express.h:39
std::string & GetPayload()
Return the payload, which can be any text but it typically self identifying s-expression.
Definition: kiway_express.h:57
MAIL_T Command()
Returns the MAIL_T associated with this mail.
Definition: kiway_express.h:49
virtual void ExpressMail(FRAME_T aDestination, MAIL_T aCommand, std::string &aPayload, wxWindow *aSource=nullptr)
Send aPayload to aDestination from aSource.
Definition: kiway.cpp:553
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:75
SCH_SHEET_PATH & CurrentSheet() const override
Definition: schematic.h:136
void SetCurrentSheet(const SCH_SHEET_PATH &aPath) override
Definition: schematic.h:141
SCH_SHEET_LIST GetSheets() const override
Builds and returns an updated schematic hierarchy TODO: can this be cached?
Definition: schematic.h:100
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
bool importFile(const wxString &aFileName, int aFileType, const STRING_UTF8_MAP *aProperties=nullptr)
Load the given filename but sets the path to the current project path.
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 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.
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:151
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:109
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.
SCH_ITEM * GetItem(const KIID &aID, SCH_SHEET_PATH *aPathOut=nullptr) const
Fetch a SCH_ITEM by ID.
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:81
int GetUnit() const
Definition: sch_symbol.h:226
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const
Return the reference for the given sheet path.
Definition: sch_symbol.cpp:731
SCH_FIELD * GetField(MANDATORY_FIELD_T aFieldType)
Return a mandatory field in this symbol.
Definition: sch_symbol.cpp:933
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.
Implement an OUTPUTFORMATTER to a memory buffer.
Definition: richio.h:427
const std::string & GetString()
Definition: richio.h:450
A name/value tuple with unique names and optional values.
TOOL_MANAGER * m_toolManager
Definition: tools_holder.h:165
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:55
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:216
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:145
#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
bool findSymbolsAndPins(const SCHEMATIC &aSchematic, 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)
std::optional< std::tuple< SCH_SHEET_PATH, SCH_ITEM *, std::vector< SCH_ITEM * > > > findItemsFromSyncSelection(const SCHEMATIC &aSchematic, const std::string aSyncStr, bool aFocusOnFirst)
bool sheetContainsOnlyWantedItems(const SCHEMATIC &aSchematic, 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
KIWAY Kiway
#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:67
@ GLOBAL_CLEANUP
SCH_SEARCH_T
Schematic search type used by the socket link with Pcbnew.
@ HIGHLIGHT_SYMBOL
@ HIGHLIGHT_PIN
std::vector< SCH_SHEET_PATH > SCH_SHEET_PATHS
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:391
@ 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:156
@ SCH_SHEET_T
Definition: typeinfo.h:158
@ SCH_PIN_T
Definition: typeinfo.h:159