KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_commit.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <sch_edit_frame.h>
21#include <symbol_edit_frame.h>
23#include <sch_view.h>
24#include <macros.h>
25#include <tool/actions.h>
26#include <tool/tool_manager.h>
27#include <tools/sch_tool_base.h>
28
29#include <lib_symbol.h>
30#include <advanced_config.h>
31
32#include <sch_group.h>
33#include <sch_screen.h>
34#include <schematic.h>
35
36#include <view/view.h>
37#include <sch_commit.h>
38#include <connection_graph.h>
39
40#include <functional>
41#include <memory>
42#include <set>
43#include <wx/log.h>
44
45
47 COMMIT(),
48 m_toolMgr( aToolMgr ),
49 m_isLibEditor( false )
50{
51 SCH_BASE_FRAME* frame = static_cast<SCH_BASE_FRAME*>( m_toolMgr->GetToolHolder() );
52 m_isLibEditor = frame && frame->IsType( FRAME_SCH_SYMBOL_EDITOR );
53}
54
55
56SCH_COMMIT::SCH_COMMIT( TOOL_MANAGER* aToolMgr, bool aIsLibEditor ) :
57 COMMIT(),
58 m_toolMgr( aToolMgr ),
59 m_isLibEditor( aIsLibEditor )
60{
61}
62
63
69
70
76
77
81
82
83COMMIT& SCH_COMMIT::Stage( EDA_ITEM *aItem, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen, RECURSE_MODE aRecurse )
84{
85 wxCHECK( aItem, *this );
86
87 if( !m_isLibEditor )
88 {
89 SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( aItem );
90 SCH_SCREEN* screen = dynamic_cast<SCH_SCREEN*>( aScreen );
91
92 if( !screen && item )
93 screen = item->GetParentScreen();
94
95 if( screen && item && SCH_SCREEN::IsConnectivitySource( item ) )
96 screen->BumpConnectivityRevision( item->Type() );
97 }
98
99 if( aRecurse == RECURSE_MODE::RECURSE )
100 {
101 if( SCH_GROUP* group = dynamic_cast<SCH_GROUP*>( aItem ) )
102 {
103 for( EDA_ITEM* member : group->GetItems() )
104 Stage( member, aChangeType, aScreen, aRecurse );
105 }
106 }
107
108 // IS_SELECTED flag should not be set on undo items which were added for a drag operation.
109 if( aItem->IsSelected() && aItem->HasFlag( SELECTED_BY_DRAG ) )
110 {
111 aItem->ClearSelected();
112 COMMIT::Stage( aItem, aChangeType, aScreen );
113 aItem->SetSelected();
114 }
115 else
116 {
117 COMMIT::Stage( aItem, aChangeType, aScreen );
118 }
119
120 return *this;
121}
122
123
124COMMIT& SCH_COMMIT::Stage( std::vector<EDA_ITEM*> &container, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen )
125{
126 for( EDA_ITEM* item : container )
127 Stage( item, aChangeType, aScreen );
128
129 return *this;
130}
131
132
134{
135 wxASSERT( !aScreen->CheckIfOnDrawList( aItem ) );
136
137 if( COMMIT_LINE* entry = findEntry( aItem, aScreen ); entry && ( entry->m_type & CHT_TYPE ) == CHT_MODIFY )
138 {
139 // Staging REMOVE discards MODIFY, so preserve the original geometry on the removed item
140 // before its image is discarded. Cleanup must still skip it until the commit is processed.
141 aItem->SwapItemData( static_cast<SCH_ITEM*>( entry->m_copy ) );
142 aItem->SetFlags( STRUCT_DELETED );
143 }
144
145 Removed( aItem, aScreen );
146}
147
148
149void SCH_COMMIT::pushLibEdit( const wxString& aMessage, int aCommitFlags )
150{
151 // Symbol editor just saves copies of the whole symbol, so grab the first and discard the rest
152 LIB_SYMBOL* symbol = dynamic_cast<LIB_SYMBOL*>( m_entries.front().m_item );
153 LIB_SYMBOL* copy = dynamic_cast<LIB_SYMBOL*>( m_entries.front().m_copy );
154
155 if( symbol )
156 {
157 if( KIGFX::VIEW* view = m_toolMgr->GetView() )
158 {
159 view->Update( symbol );
160
161 symbol->RunOnChildren(
162 [&]( SCH_ITEM* aChild )
163 {
164 view->Update( aChild );
165 },
167 }
168
169 if( SYMBOL_EDIT_FRAME* frame = static_cast<SYMBOL_EDIT_FRAME*>( m_toolMgr->GetToolHolder() ) )
170 {
171 if( !( aCommitFlags & SKIP_UNDO ) )
172 {
173 if( copy )
174 {
175 frame->PushSymbolToUndoList( aMessage, copy );
176 copy = nullptr; // we've transferred ownership to the undo stack
177 }
178 }
179 }
180
181 if( copy )
182 {
183 // if no undo entry was needed, the copy would create a memory leak
184 delete copy;
185 copy = nullptr;
186 }
187 }
188
189 m_toolMgr->PostEvent( { TC_MESSAGE, TA_MODEL_CHANGE, AS_GLOBAL } );
191}
192
193
194void SCH_COMMIT::pushSchEdit( const wxString& aMessage, int aCommitFlags )
195{
196 // Objects potentially interested in changes:
197 PICKED_ITEMS_LIST undoList;
198 KIGFX::VIEW* view = m_toolMgr->GetView();
199
200 SCH_EDIT_FRAME* frame = static_cast<SCH_EDIT_FRAME*>( m_toolMgr->GetToolHolder() );
201 SCH_SCREEN* currentScreen = frame ? frame->GetScreen() : nullptr;
202 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
203 SCH_GROUP* enteredGroup = selTool ? selTool->GetEnteredGroup() : nullptr;
204 bool itemsDeselected = false;
205 bool selectedModified = false;
206 bool dirtyConnectivity = false;
207 bool refreshConnectivity = false;
208 bool refreshHierarchy = false;
209 SCH_CLEANUP_FLAGS connectivityCleanUp = NO_CLEANUP;
210
211 if( Empty() )
212 {
213 return;
214 }
215
216 if( !frame )
217 aCommitFlags |= SKIP_UNDO;
218
219 undoList.SetDescription( aMessage );
220
221 SCHEMATIC* schematic = nullptr;
222 std::vector<SCH_ITEM*> bulkAddedItems;
223 std::vector<SCH_ITEM*> bulkRemovedItems;
224 std::vector<SCH_ITEM*> itemsChanged;
225 std::vector<std::unique_ptr<SCH_ITEM>> cleanupRemovedItems;
226 std::set<SCH_SCREEN*> connectivityScreens;
227
228 auto notifyModel =
229 [&]()
230 {
231 if( !schematic )
232 return;
233
234 if( !bulkAddedItems.empty() )
235 schematic->OnItemsAdded( bulkAddedItems );
236
237 if( !bulkRemovedItems.empty() )
238 schematic->OnItemsRemoved( bulkRemovedItems );
239
240 if( !itemsChanged.empty() )
241 schematic->OnItemsChanged( itemsChanged );
242
243 if( refreshHierarchy )
244 schematic->RefreshHierarchy();
245
246 bulkAddedItems.clear();
247 bulkRemovedItems.clear();
248 itemsChanged.clear();
249 };
250
251 auto updateConnectivityFlag =
252 [&]( SCH_ITEM* schItem, SCH_SCREEN* screen, bool fullSheetUpdate = true )
253 {
254 if( schItem->IsConnectable() || ( schItem->Type() == SCH_RULE_AREA_T ) )
255 {
256 dirtyConnectivity = true;
257 connectivityScreens.insert( screen );
258
259 // Do a local clean up if there are any connectable objects in the commit.
260 if( connectivityCleanUp == NO_CLEANUP )
261 connectivityCleanUp = LOCAL_CLEANUP;
262
263 if( schItem->Type() == SCH_SHEET_T && fullSheetUpdate )
264 connectivityCleanUp = GLOBAL_CLEANUP;
265 }
266 };
267
268 // We don't know that anything will be added to the entered group, but it does no harm to
269 // add it to the commit anyway.
270 if( enteredGroup && frame )
271 Modify( enteredGroup, frame->GetScreen() );
272
273
274 // Handle wires with Hop Over shapes (view update only; skipped headless):
275 if( frame )
276 {
277 for( COMMIT_LINE& entry : m_entries )
278 {
279 SCH_ITEM* schCopyItem = dynamic_cast<SCH_ITEM*>( entry.m_copy );
280 SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( entry.m_item );
281
282 if( schCopyItem && schCopyItem->Type() == SCH_LINE_T )
283 frame->UpdateHopOveredWires( schCopyItem );
284
285 if( schItem && schItem->Type() == SCH_LINE_T )
286 frame->UpdateHopOveredWires( schItem );
287 }
288 }
289
290 auto stageRemovedGroups =
291 [&]()
292 {
293 // Modify() appends to m_entries, so collect before staging group changes.
294 std::vector<std::pair<EDA_GROUP*, BASE_SCREEN*>> removedItemGroups;
295
296 for( COMMIT_LINE& entry : m_entries )
297 {
298 SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( entry.m_item );
299 int changeType = entry.m_type & CHT_TYPE;
300
301 wxCHECK2( schItem, continue );
302
303 if( changeType == CHT_REMOVE && schItem->GetParentGroup() )
304 removedItemGroups.emplace_back( schItem->GetParentGroup(), entry.m_screen );
305 }
306
307 for( const auto& [group, screen] : removedItemGroups )
308 Modify( group->AsEdaItem(), screen );
309 };
310
311 stageRemovedGroups();
312 bool cleanupPending = true;
313 std::set<SCH_SCREEN*> touchedScreens;
314
315 for( size_t index = 0; ; )
316 {
317 if( index == m_entries.size() )
318 {
319 if( !cleanupPending || !dirtyConnectivity || ( aCommitFlags & SKIP_CONNECTIVITY ) || !schematic )
320 break;
321
322 cleanupPending = false;
323 notifyModel();
324
325 // Cleanup can replace a MODIFY with REMOVE. Keep its staging separate from processed
326 // entries while retaining their undo records, so both phases form one user action.
327 clear();
328 // Legacy cleanup stays schematic-wide; only the engine scopes it to the staged screens
329 schematic->CleanUpConnections( this, connectivityCleanUp,
330 ADVANCED_CFG::GetCfg().m_ConnectivityEngine ? connectivityScreens
331 : std::set<SCH_SCREEN*>() );
332
333 stageRemovedGroups();
334 index = 0;
335 continue;
336 }
337
338 COMMIT_LINE& entry = m_entries[index++];
339 int changeType = entry.m_type & CHT_TYPE;
340 int changeFlags = entry.m_type & CHT_FLAGS;
341 SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( entry.m_item );
342 SCH_SCREEN* screen = dynamic_cast<SCH_SCREEN*>( entry.m_screen );
343
344 wxCHECK2( schItem, continue );
345 wxCHECK2( screen, continue );
346
347 touchedScreens.insert( screen );
348
349 if( !schematic )
350 schematic = schItem->Schematic();
351
352 // Staging invalidates captured sources even when the final connectivity is unchanged
353 if( ADVANCED_CFG::GetCfg().m_ConnectivityEngine && SCH_SCREEN::IsConnectivitySource( schItem ) )
354 refreshConnectivity = true;
355
356 if( schItem->IsSelected() )
357 {
358 selectedModified = true;
359 }
360 else
361 {
362 schItem->RunOnChildren(
363 [&selectedModified]( SCH_ITEM* aChild )
364 {
365 if( aChild->IsSelected() )
366 selectedModified = true;
367 },
369 }
370
371 switch( changeType )
372 {
373 case CHT_ADD:
374 {
375 if( enteredGroup && schItem->IsGroupableType() && !schItem->GetParentGroup() )
376 selTool->GetEnteredGroup()->AddItem( schItem );
377
378 updateConnectivityFlag( schItem, screen );
379
380 if( !( aCommitFlags & SKIP_UNDO ) )
381 undoList.PushItem( ITEM_PICKER( screen, schItem, UNDO_REDO::NEWITEM ) );
382
383 if( !( changeFlags & CHT_DONE ) )
384 {
385 if( !screen->CheckIfOnDrawList( schItem ) ) // don't want a loop!
386 screen->Append( schItem );
387
388 if( view && screen == currentScreen )
389 view->Add( schItem );
390 }
391
392 if( frame && screen == currentScreen )
393 frame->UpdateItem( schItem, true, true );
394 else if( screen )
395 screen->Update( schItem );
396
397 bulkAddedItems.push_back( schItem );
398
399 if( schItem->Type() == SCH_SHEET_T )
400 refreshHierarchy = true;
401
402 break;
403 }
404
405 case CHT_REMOVE:
406 {
407 updateConnectivityFlag( schItem, screen );
408
409 if( !( aCommitFlags & SKIP_UNDO ) )
410 {
411 ITEM_PICKER itemWrapper( screen, schItem, UNDO_REDO::DELETED );
412 itemWrapper.SetLink( entry.m_copy );
413 entry.m_copy = nullptr; // We've transferred ownership to the undo list
414 undoList.PushItem( itemWrapper );
415 }
416
417 if( schItem->IsSelected() )
418 {
419 if( selTool )
420 selTool->RemoveItemFromSel( schItem, true /* quiet mode */ );
421
422 itemsDeselected = true;
423 }
424
425 if( schItem->Type() == SCH_FIELD_T )
426 {
427 static_cast<SCH_FIELD*>( schItem )->SetVisible( false );
428 break;
429 }
430
431 if( ( aCommitFlags & SKIP_UNDO ) && ( !cleanupPending || ( aCommitFlags & DELETE_REMOVED_ITEMS ) ) )
432 cleanupRemovedItems.emplace_back( schItem );
433
434 if( EDA_GROUP* group = schItem->GetParentGroup() )
435 group->RemoveItem( schItem );
436
437 if( !( changeFlags & CHT_DONE ) )
438 {
439 screen->Remove( schItem );
440
441 if( view && screen == currentScreen )
442 view->Remove( schItem );
443 }
444
445 if( frame && screen == currentScreen )
446 frame->UpdateItem( schItem, true, true );
447 else if( screen )
448 screen->Update( schItem );
449
450 if( schItem->Type() == SCH_SHEET_T )
451 refreshHierarchy = true;
452
453 bulkRemovedItems.push_back( schItem );
454 break;
455 }
456
457 case CHT_MODIFY:
458 {
459 const SCH_ITEM* itemCopy = static_cast<const SCH_ITEM*>( entry.m_copy );
460 SCH_SHEET_PATH currentSheet;
461 bool fullSheetUpdate = true;
462
463 if( frame )
464 currentSheet = frame->GetCurrentSheet();
465
466 if( schItem->Type() == SCH_SHEET_T )
467 {
468 const SCH_SHEET* modifiedSheet = static_cast<const SCH_SHEET*>( schItem );
469 const SCH_SHEET* originalSheet = static_cast<const SCH_SHEET*>( itemCopy );
470 const bool hierarchyChanged = originalSheet->HasHierarchyChanges( *modifiedSheet );
471
472 refreshHierarchy |= hierarchyChanged;
473
474 // Stable ports are invalidated through their containing screen, including shared instances
475 fullSheetUpdate = !ADVANCED_CFG::GetCfg().m_ConnectivityEngine
476 || hierarchyChanged
477 || originalSheet->HasPinIdentityChanges( *modifiedSheet );
478 }
479
480 if( itemCopy->HasConnectivityChanges( schItem, &currentSheet )
481 || ( itemCopy->Type() == SCH_RULE_AREA_T ) )
482 {
483 updateConnectivityFlag( schItem, screen, fullSheetUpdate );
484 }
485 else if( schItem->IsConnectable() && schItem->IsConnectivityDirty() )
486 {
487 // Move previews can change dangling flags without changing the final geometry.
488 refreshConnectivity = true;
489 }
490
491 if( schItem->Type() == SCH_SYMBOL_T )
492 {
493 const SCH_SYMBOL* origSymbol = static_cast<const SCH_SYMBOL*>( itemCopy );
494 const SCH_SYMBOL* modSymbol = static_cast<const SCH_SYMBOL*>( schItem );
495
496 if( origSymbol->GetPins( &currentSheet ).size() != modSymbol->GetPins( &currentSheet ).size() )
497 connectivityCleanUp = GLOBAL_CLEANUP;
498
499 if( origSymbol->GetSchSymbolLibraryName() != modSymbol->GetSchSymbolLibraryName() )
500 screen->PruneUnusedLibSymbol( origSymbol->GetSchSymbolLibraryName() );
501 }
502
503 if( !( aCommitFlags & SKIP_UNDO ) )
504 {
505#if 0
506 // While this keeps us from marking documents modified when someone OK's a dialog with
507 // no changes, it depends on our various SCH_ITEM::operator=='s being bullet-proof. They
508 // currently aren't.
509 if( *itemCopy == *schItem )
510 {
511 // No actual changes made; short-circuit undo
512 delete entry.m_copy;
513 entry.m_copy = nullptr;
514 break;
515 }
516#endif
517
518 ITEM_PICKER itemWrapper( screen, schItem, UNDO_REDO::CHANGED );
519 itemWrapper.SetLink( entry.m_copy );
520 entry.m_copy = nullptr; // We've transferred ownership to the undo list
521 undoList.PushItem( itemWrapper );
522 }
523
524 if( frame && screen == currentScreen )
525 frame->UpdateItem( schItem, false, true );
526 else if( screen )
527 screen->Update( schItem );
528
529 itemsChanged.push_back( schItem );
530 break;
531 }
532
533 default:
534 wxASSERT( false );
535 break;
536 }
537
538 // Delete any copies we still have ownership of
539 delete entry.m_copy;
540 entry.m_copy = nullptr;
541
542 // Clear all flags but SELECTED and others used to move and rotate commands,
543 // after edition (selected items must keep their selection flag).
544 const int selected_mask = ( SELECTED | STARTPOINT | ENDPOINT );
545 schItem->ClearFlags( EDA_ITEM_ALL_FLAGS - selected_mask );
546
547 if( schItem->Type() == SCH_SHEET_T || schItem->Type() == SCH_SYMBOL_T )
548 {
549 schItem->RunOnChildren(
550 [&]( SCH_ITEM* child )
551 {
552 child->ClearFlags( EDA_ITEM_ALL_FLAGS - selected_mask );
553 },
555 }
556 }
557
558 notifyModel();
559
560 // Mark the touched screens dirty so that the state is tracked even in headless API server mode
561 if( !( aCommitFlags & SKIP_SET_DIRTY ) )
562 {
563 for( SCH_SCREEN* screen : touchedScreens )
564 screen->SetContentModified();
565 }
566
567 if( !( aCommitFlags & SKIP_UNDO ) && frame && undoList.GetCount() > 0 )
568 frame->SaveCopyInUndoList( undoList, UNDO_REDO::UNSPECIFIED, false );
569
570 cleanupRemovedItems.clear();
571
572 if( ( dirtyConnectivity || refreshConnectivity ) && !( aCommitFlags & SKIP_CONNECTIVITY ) )
573 {
574 wxLogTrace( wxS( "CONN_PROFILE" ), wxS( "SCH_COMMIT::pushSchEdit() connectivity refresh, cleanup=%d." ),
575 static_cast<int>( connectivityCleanUp ) );
576
577 if( frame )
578 {
579 frame->RecalculateConnections( this, connectivityCleanUp, nullptr, true );
580 }
581 else if( schematic )
582 {
583 schematic->RecalculateConnections( this, connectivityCleanUp, m_toolMgr, nullptr, nullptr, nullptr,
584 nullptr, true );
585 }
586 }
587
588 if( refreshHierarchy && frame )
590
591 m_toolMgr->PostEvent( { TC_MESSAGE, TA_MODEL_CHANGE, AS_GLOBAL } );
592
593 if( itemsDeselected )
595
596 if( selectedModified )
598}
599
600
601void SCH_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
602{
603 if( Empty() )
604 return;
605
606 if( m_isLibEditor )
607 pushLibEdit( aMessage, aCommitFlags );
608 else
609 pushSchEdit( aMessage, aCommitFlags );
610
611 if( SCH_BASE_FRAME* frame = static_cast<SCH_BASE_FRAME*>( m_toolMgr->GetToolHolder() ) )
612 {
613 if( !( aCommitFlags & SKIP_SET_DIRTY ) )
614 frame->OnModify();
615
616 if( frame && frame->GetCanvas() )
617 frame->GetCanvas()->Refresh();
618 }
619
620 clear();
621}
622
623
625{
626 EDA_ITEM* parent = aItem->GetParent();
627
628 if( m_isLibEditor )
629 {
630 // Headless commits operate directly on the staged item
631 if( SYMBOL_EDIT_FRAME* frame = static_cast<SYMBOL_EDIT_FRAME*>( m_toolMgr->GetToolHolder() ) )
632 return frame->GetCurSymbol();
633
634 return aItem;
635 }
636
637 if( parent && parent->IsType( { SCH_SYMBOL_T, SCH_TABLE_T, SCH_SHEET_T, SCH_LABEL_LOCATE_ANY_T } ) )
638 return parent;
639
640 return aItem;
641}
642
643
645{
646 if( m_isLibEditor )
647 {
648 SYMBOL_EDIT_FRAME* frame = static_cast<SYMBOL_EDIT_FRAME*>( m_toolMgr->GetToolHolder() );
649
650 if( frame && frame->GetCurSymbol() )
651 {
652 LIB_SYMBOL* symbol = frame->GetCurSymbol();
653 std::vector<KIID> selected;
654
655 // Cloning will clear the selected flags, but we want to keep them.
656 for( const SCH_ITEM& item : symbol->GetDrawItems() )
657 {
658 if( item.IsSelected() )
659 selected.push_back( item.m_Uuid );
660 }
661
662 symbol = new LIB_SYMBOL( *symbol );
663
664 // Restore selected flags.
665 for( SCH_ITEM& item : symbol->GetDrawItems() )
666 {
667 if( alg::contains( selected, item.m_Uuid ) )
668 item.SetSelected();
669 }
670
671 return symbol;
672 }
673 }
674
675 return aItem->Clone();
676}
677
678
680{
681 if( Empty() )
682 return;
683
684 // Symbol editor just saves copies of the whole symbol, so grab the first and discard the rest
685 SYMBOL_EDIT_FRAME* frame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_toolMgr->GetToolHolder() );
686 LIB_SYMBOL* copy = dynamic_cast<LIB_SYMBOL*>( m_entries.front().m_copy );
687 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
688
689 if( frame && copy )
690 {
691 frame->SetCurSymbol( copy, false );
692 m_toolMgr->ResetTools( TOOL_BASE::MODEL_RELOAD );
693 }
694
695 if( selTool )
696 selTool->RebuildSelection();
697
698 clear();
699}
700
701
703{
705}
706
707
708void SCH_COMMIT::RevertToCheckpoint( int aCheckpoint )
709{
710 KIGFX::VIEW* view = m_toolMgr->GetView();
711 SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_toolMgr->GetToolHolder() );
712 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
713 SCH_SHEET_LIST sheets;
714
715 if( m_entries.empty() )
716 return;
717
718 if( m_isLibEditor )
719 {
721 return;
722 }
723
724 SCHEMATIC* schematic = nullptr;
725 std::vector<SCH_ITEM*> bulkAddedItems;
726 std::vector<SCH_ITEM*> bulkRemovedItems;
727 std::vector<SCH_ITEM*> itemsChanged;
728
729 for( int ii = aCheckpoint; ii < (int) m_entries.size(); ++ii )
730 {
731 COMMIT_LINE& entry = m_entries[ii];
732 int changeType = entry.m_type & CHT_TYPE;
733 int changeFlags = entry.m_type & CHT_FLAGS;
734 SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( entry.m_item );
735 SCH_ITEM* copy = dynamic_cast<SCH_ITEM*>( entry.m_copy );
736 SCH_SCREEN* screen = dynamic_cast<SCH_SCREEN*>( entry.m_screen );
737
738 wxCHECK2( item && screen, continue );
739
740 if( !schematic )
741 schematic = item->Schematic();
742
743 switch( changeType )
744 {
745 case CHT_ADD:
746 if( !( changeFlags & CHT_DONE ) )
747 break;
748
749 if( view )
750 view->Remove( item );
751
752 screen->Remove( item );
753 bulkRemovedItems.push_back( item );
754 break;
755
756 case CHT_REMOVE:
757 item->SetConnectivityDirty();
758
759 if( !( changeFlags & CHT_DONE ) )
760 break;
761
762 item->ClearFlags( STRUCT_DELETED );
763
764 if( view )
765 view->Add( item );
766
767 screen->Append( item );
768 bulkAddedItems.push_back( item );
769 break;
770
771 case CHT_MODIFY:
772 {
773 wxCHECK2( copy, break );
774
775 if( view )
776 view->Remove( item );
777
778 bool unselect = !item->IsSelected();
779
780 item->SwapItemData( copy );
781
782 if( unselect )
783 {
784 item->ClearSelected();
785 item->RunOnChildren( []( SCH_ITEM* aChild )
786 {
787 aChild->ClearSelected();
788 },
790 }
791
792 // Special cases for items which have instance data
793 if( item->GetParent() && item->GetParent()->Type() == SCH_SYMBOL_T && item->Type() == SCH_FIELD_T )
794 {
795 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
796 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item->GetParent() );
797
798 if( field->GetId() == FIELD_T::REFERENCE )
799 {
800 // Lazy eval of sheet list; this is expensive even when unsorted
801 if( sheets.empty() )
802 sheets = schematic->Hierarchy();
803
804 SCH_SHEET_PATH sheet = sheets.FindSheetForScreen( screen );
805 symbol->SetRef( &sheet, field->GetText() );
806 }
807 }
808
809 // This must be called before any calls that require stable object pointers.
810 screen->Update( item );
811
812 // This hack is to prevent incorrectly parented symbol pins from breaking the
813 // connectivity algorithm.
814 if( item->Type() == SCH_SYMBOL_T )
815 {
816 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
817 SCH_SYMBOL* symbolCopy = static_cast<SCH_SYMBOL*>( copy );
818
819 symbol->UpdatePins();
820
821 if( symbol->GetSchSymbolLibraryName() != symbolCopy->GetSchSymbolLibraryName() )
822 screen->PruneUnusedLibSymbol( symbolCopy->GetSchSymbolLibraryName() );
823
824 CONNECTION_GRAPH* graph = schematic->ConnectionGraph();
825
826 graph->RemoveItem( symbolCopy );
827
828 // Lazy eval of sheet list; this is expensive even when unsorted
829 if( sheets.empty() )
830 sheets = schematic->Hierarchy();
831
832 SCH_SHEET_PATH sheet = sheets.FindSheetForScreen( screen );
833
834 for( SCH_PIN* pin : symbolCopy->GetPins( &sheet ) )
835 graph->RemoveItem( pin );
836 }
837
838 item->SetConnectivityDirty();
839
840 if( view )
841 view->Add( item );
842
843 itemsChanged.push_back( item );
844 break;
845 }
846
847 default:
848 wxASSERT( false );
849 break;
850 }
851
852 delete copy;
853 entry.m_copy = nullptr;
854 }
855
856 if( schematic )
857 {
858 if( bulkAddedItems.size() > 0 )
859 schematic->OnItemsAdded( bulkAddedItems );
860
861 if( bulkRemovedItems.size() > 0 )
862 schematic->OnItemsRemoved( bulkRemovedItems );
863
864 if( itemsChanged.size() > 0 )
865 schematic->OnItemsChanged( itemsChanged );
866 }
867
868 if( selTool )
869 selTool->RebuildSelection();
870
871 if( frame )
872 frame->RecalculateConnections( nullptr, NO_CLEANUP );
873
874 m_entries.erase( m_entries.begin() + aCheckpoint, m_entries.end() );
875}
876
877
879{
880 if( aID == niluuid )
881 return nullptr;
882
883 for( COMMIT_LINE& entry : m_entries )
884 {
885 if( entry.m_item && entry.m_item->IsSCH_ITEM() && entry.m_item->m_Uuid == aID )
886 return entry.m_item;
887 }
888
889 return nullptr;
890}
int index
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
Handles how to draw a screen (a board, a schematic ...)
Definition base_screen.h:37
bool Empty() const
Definition commit.h:145
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:102
COMMIT()
Definition commit.cpp:30
COMMIT_LINE * findEntry(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Search for an entry describing change for a particular item.
Definition commit.cpp:267
std::vector< COMMIT_LINE > m_entries
Definition commit.h:194
COMMIT & Removed(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been removed.
Definition commit.h:92
void clear()
Should be called in Push() & Revert() methods.
Definition commit.h:165
virtual COMMIT & Stage(EDA_ITEM *aItem, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Add a change of the item aItem of type aChangeType to the change list.
Definition commit.cpp:42
Calculate the connectivity of a schematic and generate netlists.
void RemoveItem(SCH_ITEM *aItem)
bool IsType(FRAME_T aType) const
The base class for create windows for drawing purpose.
A set of EDA_ITEMs (i.e., without duplicates).
Definition eda_group.h:43
void AddItem(EDA_ITEM *aItem)
Add item to group.
Definition eda_group.cpp:58
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:158
virtual EDA_GROUP * GetParentGroup() const
Definition eda_item.h:116
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void ClearSelected()
Definition eda_item.h:153
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:160
bool IsSelected() const
Definition eda_item.h:134
void SetSelected()
Definition eda_item.h:150
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition eda_item.h:214
EDA_ITEM * GetParent() const
Definition eda_item.h:112
virtual EDA_ITEM * Clone() const
Create a duplicate of this item with linked list members set to NULL.
Definition eda_item.cpp:278
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:168
static const TOOL_EVENT SelectedItemsModified
Selected item had a property changed (except movement)
Definition actions.h:352
static const TOOL_EVENT UnselectedEvent
Definition actions.h:346
void SetLink(EDA_ITEM *aItem)
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:63
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition view.cpp:297
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition view.cpp:412
Definition kiid.h:46
Define a library symbol object.
Definition lib_symbol.h:114
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition lib_symbol.h:843
void RunOnChildren(const std::function< void(SCH_ITEM *)> &aFunction, RECURSE_MODE aMode) override
A holder to handle information on schematic or board items.
void PushItem(const ITEM_PICKER &aItem)
Push aItem to the top of the list.
void SetDescription(const wxString &aDescription)
unsigned GetCount() const
Holds all the data relating to one schematic.
Definition schematic.h:149
void OnItemsAdded(std::vector< SCH_ITEM * > &aNewItems)
Must be used if Add() is used using a BULK_x ADD_MODE to generate a change event for listeners.
void OnItemsRemoved(std::vector< SCH_ITEM * > &aRemovedItems)
Must be used if Remove() is used using a BULK_x REMOVE_MODE to generate a change event for listeners.
SCH_SHEET_LIST Hierarchy() const
Return the full schematic flattened hierarchical sheet list.
void CleanUpConnections(SCH_COMMIT *aCommit, SCH_CLEANUP_FLAGS aCleanupFlags, const std::set< SCH_SCREEN * > &aLocalScreens={})
CONNECTION_GRAPH * ConnectionGraph() const
Definition schematic.h:319
void RecalculateConnections(SCH_COMMIT *aCommit, SCH_CLEANUP_FLAGS aCleanupFlags, TOOL_MANAGER *aToolManager, PROGRESS_REPORTER *aProgressReporter=nullptr, KIGFX::SCH_VIEW *aSchView=nullptr, std::function< void(SCH_ITEM *)> *aChangedItemHandler=nullptr, PICKED_ITEMS_LIST *aLastChangeList=nullptr, bool aCleanupDone=false)
Generate the connection data for the entire schematic hierarchy.
void RefreshHierarchy()
void OnItemsChanged(std::vector< SCH_ITEM * > &aItems)
Notify the schematic and its listeners that an item on the schematic has been modified in some way.
A shim class between EDA_DRAW_FRAME and several derived classes: SYMBOL_EDIT_FRAME,...
COMMIT & Stage(EDA_ITEM *aItem, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE) override
Add a change of the item aItem of type aChangeType to the change list.
bool m_isLibEditor
Definition sch_commit.h:91
EDA_ITEM * undoLevelItem(EDA_ITEM *aItem) const override
void Revert() override
Revert the commit by restoring the modified items state.
TOOL_MANAGER * m_toolMgr
Definition sch_commit.h:90
EDA_ITEM * ResolveItem(KIID &aID) override
Search for an item in this commit that matches the provided KIID.
void RemovedForCleanup(SCH_ITEM *aItem, SCH_SCREEN *aScreen)
Retain the pre-edit state of an item already removed from its screen by cleanup.
SCH_COMMIT(TOOL_MANAGER *aToolMgr)
virtual ~SCH_COMMIT()
void pushLibEdit(const wxString &aMessage, int aCommitFlags)
void revertLibEdit()
EDA_ITEM * makeImage(EDA_ITEM *aItem) const override
void pushSchEdit(const wxString &aMessage, int aCommitFlags)
void RevertToCheckpoint(int aCheckpoint) override
void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
Schematic editor (Eeschema) main window.
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
void SaveCopyInUndoList(SCH_SCREEN *aScreen, SCH_ITEM *aItemToCopy, UNDO_REDO aTypeCommand, bool aAppend)
Create a copy of the current schematic item, and put it in the undo list.
bool RecalculateConnections(SCH_COMMIT *aCommit, SCH_CLEANUP_FLAGS aCleanupFlags, PROGRESS_REPORTER *aProgressReporter=nullptr, bool aCleanupDone=false)
Generate the connection data for the entire schematic hierarchy.
SCH_SHEET_PATH & GetCurrentSheet() const
void UpdateHierarchyNavigator(bool aRefreshNetNavigator=true, bool aClear=false)
Update the hierarchy navigation tree and history.
void UpdateItem(EDA_ITEM *aItem, bool isAddOrDelete=false, bool aUpdateRtree=false) override
Mark an item for refresh.
void UpdateHopOveredWires(SCH_ITEM *aItem)
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:139
FIELD_T GetId() const
Definition sch_field.h:143
A set of SCH_ITEMs (i.e., without duplicates).
Definition sch_group.h:48
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
virtual bool IsConnectable() const
Definition sch_item.h:545
virtual void RunOnChildren(const std::function< void(SCH_ITEM *)> &aFunction, RECURSE_MODE aMode)
Definition sch_item.h:678
SCH_SCREEN * GetParentScreen() const
Definition sch_item.cpp:308
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:302
void SetConnectivityDirty(bool aDirty=true)
Set the dirty flag.
Definition sch_item.cpp:314
bool IsConnectivityDirty() const
Definition sch_item.h:630
virtual bool HasConnectivityChanges(const SCH_ITEM *aItem, const SCH_SHEET_PATH *aInstance=nullptr) const
Check if aItem has connectivity changes against this object.
Definition sch_item.h:654
bool IsGroupableType() const
Definition sch_item.cpp:144
void SwapItemData(SCH_ITEM *aImage)
Swap data between aItem and aImage.
Definition sch_item.cpp:840
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
static bool IsConnectivitySource(const SCH_ITEM *aItem)
Markers, bitmaps, plain shapes, graphic lines and groups carry nothing captured by connectivity.
void BumpConnectivityRevision(KICAD_T aChangedType=TYPE_NOT_INIT)
Increment the connectivity revisions of this screen.
bool Remove(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Remove aItem from the schematic associated with this screen.
void Update(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Refresh a changed item in the tree and invalidate connectivity.
bool CheckIfOnDrawList(const SCH_ITEM *aItem) const
bool PruneUnusedLibSymbol(const wxString &aName)
Remove the library symbol cached under aName if no symbol on this screen still references that name.
void RebuildSelection()
Rebuild the selection from the EDA_ITEMs' selection flags.
SCH_GROUP * GetEnteredGroup()
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
bool HasPinIdentityChanges(const SCH_SHEET &aOther) const
bool HasHierarchyChanges(const SCH_SHEET &aOther) const
Schematic symbol object.
Definition sch_symbol.h:73
wxString GetSchSymbolLibraryName() const
std::vector< const SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
void UpdatePins()
Updates the cache of SCH_PIN objects for each pin.
void SetRef(const SCH_SHEET_PATH *aSheet, const wxString &aReference)
Set the reference for the given sheet path for this symbol.
A foundation class for a tool operating on a schematic or symbol.
bool IsSymbolEditor() const
Returns true if the tool is running in the symbol editor.
int RemoveItemFromSel(const TOOL_EVENT &aEvent)
The symbol library editor main window.
void SetCurSymbol(LIB_SYMBOL *aSymbol, bool aUpdateZoom)
Take ownership of aSymbol and notes that it is the one currently being edited.
LIB_SYMBOL * GetCurSymbol() const
Return the current symbol being edited or NULL if none selected.
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
TOOL_MANAGER * GetManager() const
Return the instance of TOOL_MANAGER that takes care of the tool.
Definition tool_base.h:142
@ MODEL_RELOAD
Model changes (the sheet for a schematic)
Definition tool_base.h:76
Master controller class:
CHANGE_TYPE
Types of changes.
Definition commit.h:37
@ CHT_MODIFY
Definition commit.h:40
@ CHT_REMOVE
Definition commit.h:39
@ CHT_DONE
Flag to indicate the change is already applied.
Definition commit.h:43
@ CHT_TYPE
Definition commit.h:41
@ CHT_ADD
Definition commit.h:38
@ CHT_FLAGS
Definition commit.h:44
RECURSE_MODE
Definition eda_item.h:50
@ RECURSE
Definition eda_item.h:51
@ NO_RECURSE
Definition eda_item.h:52
#define SELECTED
Item was manually selected by the user.
#define SELECTED_BY_DRAG
Item was algorithmically selected as a dragged item.
#define EDA_ITEM_ALL_FLAGS
#define STRUCT_DELETED
flag indication structures to be erased
#define ENDPOINT
ends. (Used to support dragging.)
#define STARTPOINT
When a line is selected, these flags indicate which.
@ FRAME_SCH_SYMBOL_EDITOR
Definition frame_type.h:31
bool m_ConnectivityEngine
Use the schematic connectivity engine instead of CONNECTION_GRAPH.
KIID niluuid(0)
This file contains miscellaneous commonly used macros and functions.
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:96
#define SKIP_CONNECTIVITY
Definition sch_commit.h:41
#define SKIP_SET_DIRTY
Definition sch_commit.h:40
#define DELETE_REMOVED_ITEMS
Definition sch_commit.h:43
#define SKIP_UNDO
Definition sch_commit.h:38
Class to handle a set of SCH_ITEMs.
SCH_CLEANUP_FLAGS
Definition schematic.h:92
@ LOCAL_CLEANUP
Definition schematic.h:94
@ NO_CLEANUP
Definition schematic.h:93
@ GLOBAL_CLEANUP
Definition schematic.h:95
EDA_ITEM * m_copy
Optional copy of the item.
Definition commit.h:159
CHANGE_TYPE m_type
Modification type.
Definition commit.h:160
EDA_ITEM * m_item
Main item that is added/deleted/modified.
Definition commit.h:158
BASE_SCREEN * m_screen
Definition commit.h:161
@ REFERENCE
Field Reference of part, i.e. "IC21".
KIBIS_PIN * pin
@ AS_GLOBAL
Global action (toolbar/main menu event, global shortcut)
Definition tool_action.h:45
@ TA_MODEL_CHANGE
Model has changed (partial update).
Definition tool_event.h:117
@ TC_MESSAGE
Definition tool_event.h:54
@ SCH_LINE_T
Definition typeinfo.h:159
@ SCH_SYMBOL_T
Definition typeinfo.h:168
@ SCH_FIELD_T
Definition typeinfo.h:146
@ SCH_SHEET_T
Definition typeinfo.h:171
@ SCH_RULE_AREA_T
Definition typeinfo.h:166