KiCad PCB EDA Suite
Loading...
Searching...
No Matches
board_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 (C) 2016 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Tomasz Wlostowski <[email protected]>
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, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <macros.h>
23#include <pgm_base.h>
25#include <board.h>
27#include <footprint.h>
28#include <lset.h>
29#include <pad.h>
30#include <pcb_group.h>
31#include <pcb_track.h>
32#include <pcb_shape.h>
33#include <tool/tool_manager.h>
36#include <view/view.h>
37#include <board_commit.h>
38#include <tools/pcb_tool_base.h>
39#include <tools/pcb_actions.h>
42#include <teardrop/teardrop.h>
43#include <pcb_board_outline.h>
44
45#include <functional>
46#include <unordered_set>
48using namespace std::placeholders;
49
50
51// A no-geometry board item (net metadata or a geometric constraint) is never pushed into the
52// VIEW or the connectivity graph. VIEW::Add early-returns on an empty layer set but still
53// stamps the item's view private data, which a later VIEW::Remove would mis-index.
54static bool isNoGeometryItem( const BOARD_ITEM* aItem )
55{
56 return aItem->Type() == PCB_NETINFO_T || aItem->Type() == PCB_CONSTRAINT_T;
57}
58
59
61 COMMIT(),
62 m_toolMgr( aTool->GetManager() ),
63 m_isBoardEditor( false ),
64 m_isFootprintEditor( false )
65{
66 if( PCB_TOOL_BASE* pcb_tool = dynamic_cast<PCB_TOOL_BASE*>( aTool ) )
67 {
68 m_isBoardEditor = pcb_tool->IsBoardEditor();
69 m_isFootprintEditor = pcb_tool->IsFootprintEditor();
70 }
71}
72
73
75 COMMIT(),
76 m_toolMgr( aFrame->GetToolManager() ),
77 m_isBoardEditor( aFrame->IsType( FRAME_PCB_EDITOR ) ),
79{
80}
81
82
84 COMMIT(),
85 m_toolMgr( aMgr ),
86 m_isBoardEditor( false ),
87 m_isFootprintEditor( false )
88{
89 EDA_DRAW_FRAME* frame = dynamic_cast<EDA_DRAW_FRAME*>( aMgr->GetToolHolder() );
90
91 if( frame && frame->IsType( FRAME_PCB_EDITOR ) )
92 m_isBoardEditor = true;
93 else if( frame && frame->IsType( FRAME_FOOTPRINT_EDITOR ) )
95}
96
97
98BOARD_COMMIT::BOARD_COMMIT( TOOL_MANAGER* aMgr, bool aIsBoardEditor, bool aIsFootprintEditor ) :
99 COMMIT(),
100 m_toolMgr( aMgr ),
101 m_isBoardEditor( aIsBoardEditor ),
102 m_isFootprintEditor( aIsFootprintEditor )
103{
104}
105
106
108{
109 return static_cast<BOARD*>( m_toolMgr->GetModel() );
110}
111
112
113COMMIT& BOARD_COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType, BASE_SCREEN* aScreen, RECURSE_MODE aRecurse )
114{
115 if( aRecurse == RECURSE_MODE::RECURSE )
116 {
117 if( PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( aItem ) )
118 {
119 for( EDA_ITEM* member : group->GetItems() )
120 Stage( member, aChangeType, aScreen, aRecurse );
121 }
122 }
123
124 if( m_isBoardEditor && ( aChangeType & CHT_TYPE ) == CHT_REMOVE
125 && aItem->IsBOARD_ITEM()
126 && static_cast<BOARD_ITEM*>( aItem )->GetParentFootprint() )
127 {
128 if( m_deletedItems.find( { aItem, aScreen } ) == m_deletedItems.end() )
129 makeEntry( aItem, aChangeType, makeImage( aItem ), aScreen );
130
131 return *this;
132 }
133
134 return COMMIT::Stage( aItem, aChangeType );
135}
136
137
138COMMIT& BOARD_COMMIT::Stage( std::vector<EDA_ITEM*>& container, CHANGE_TYPE aChangeType, BASE_SCREEN* aScreen )
139{
140 return COMMIT::Stage( container, aChangeType, aScreen );
141}
142
143
145{
146 return COMMIT::Stage( aItems, aModFlag, aScreen );
147}
148
149
150void BOARD_COMMIT::propagateDamage( BOARD_ITEM* aChangedItem, std::vector<ZONE*>* aStaleZones,
151 std::vector<BOX2I>& aStaleRuleAreas )
152{
153 wxCHECK( aChangedItem, /* void */ );
154
155 if( aStaleZones && aChangedItem->Type() == PCB_ZONE_T )
156 aStaleZones->push_back( static_cast<ZONE*>( aChangedItem ) );
157
158 aChangedItem->RunOnChildren( std::bind( &BOARD_COMMIT::propagateDamage, this, _1, aStaleZones, aStaleRuleAreas ),
160
161 BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
162 BOX2I damageBBox = aChangedItem->GetBoundingBox();
163 LSET damageLayers = aChangedItem->GetLayerSet();
164
165 if( m_isBoardEditor && aChangedItem->Type() == PCB_ZONE_T )
166 {
167 // A named zone can have custom DRC rules targetting it.
168 if( !static_cast<ZONE*>( aChangedItem )->GetZoneName().IsEmpty() )
169 aStaleRuleAreas.push_back( damageBBox );
170 }
171
172 if( aStaleZones )
173 {
174 if( damageLayers.test( Edge_Cuts ) || damageLayers.test( Margin ) )
175 damageLayers = LSET::PhysicalLayersMask();
176 else
177 damageLayers &= LSET::AllCuMask();
178
179 if( damageLayers.any() )
180 {
181 for( ZONE* zone : board->Zones() )
182 {
183 if( zone->GetIsRuleArea() )
184 continue;
185
186 if( ( zone->GetLayerSet() & damageLayers ).any() && zone->GetBoundingBox().Intersects( damageBBox ) )
187 aStaleZones->push_back( zone );
188 }
189 }
190 }
191}
192
193
194void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
195{
196 KIGFX::PCB_VIEW* view = static_cast<KIGFX::PCB_VIEW*>( m_toolMgr->GetView() );
197 BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
198 PCB_BASE_FRAME* frame = dynamic_cast<PCB_BASE_FRAME*>( m_toolMgr->GetToolHolder() );
199 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
200 PCB_GROUP* enteredGroup = selTool && !( aCommitFlags & SKIP_ENTERED_GROUP ) ? selTool->GetEnteredGroup()
201 : nullptr;
202 // Notification info
203 PICKED_ITEMS_LIST undoList;
204 bool itemsDeselected = false;
205 bool selectedModified = false;
206
207 std::unordered_set<EDA_ITEM*> removedItems;
208
209 // Dirty flags and lists
210 bool solderMaskDirty = false;
211 bool autofillZones = false;
212 bool updateBoardBoundingBox = false;
213 std::vector<BOARD_ITEM*> staleTeardropPadsAndVias;
214 std::set<PCB_TRACK*> staleTeardropTracks;
215 std::vector<ZONE*> staleZonesStorage;
216 std::vector<ZONE*>* staleZones = nullptr;
217 std::vector<BOX2I> staleRuleAreas;
218
219 if( Empty() )
220 return;
221
222 undoList.SetDescription( aMessage );
223
224 TEARDROP_MANAGER teardropMgr( board, m_toolMgr );
225 std::shared_ptr<CONNECTIVITY_DATA> connectivity = board->GetConnectivity();
226
227 // Note: frame == nullptr happens in QA tests
228
229 std::vector<BOARD_ITEM*> bulkAddedItems;
230 std::vector<BOARD_ITEM*> bulkRemovedItems;
231 std::vector<BOARD_ITEM*> itemsChanged;
232
233 if( m_isBoardEditor && !( aCommitFlags & ZONE_FILL_OP )
234 && ( frame && frame->GetPcbNewSettings()->m_AutoRefillZones ) )
235 {
236 autofillZones = true;
237 staleZones = &staleZonesStorage;
238
239 for( ZONE* zone : board->Zones() )
240 zone->CacheBoundingBox();
241 }
242
243 for( COMMIT_LINE& entry : m_entries )
244 {
245 if( !entry.m_item || !entry.m_item->IsBOARD_ITEM() )
246 continue;
247
248 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( entry.m_item );
249
250 if( m_isBoardEditor )
251 {
252 if( boardItem->Type() == PCB_VIA_T || boardItem->Type() == PCB_FOOTPRINT_T
253 || boardItem->IsOnLayer( F_Mask ) || boardItem->IsOnLayer( B_Mask ) )
254 {
255 solderMaskDirty = true;
256 }
257
258 if( boardItem->GetLayer() == Edge_Cuts )
259 {
260 updateBoardBoundingBox = true;
261 }
262
263 if( !( aCommitFlags & SKIP_TEARDROPS ) )
264 {
265 if( boardItem->Type() == PCB_FOOTPRINT_T )
266 {
267 for( PAD* pad : static_cast<FOOTPRINT*>( boardItem )->Pads() )
268 staleTeardropPadsAndVias.push_back( pad );
269 }
270 else if( boardItem->Type() == PCB_PAD_T || boardItem->Type() == PCB_VIA_T )
271 {
272 staleTeardropPadsAndVias.push_back( boardItem );
273 }
274 else if( boardItem->Type() == PCB_TRACE_T || boardItem->Type() == PCB_ARC_T )
275 {
276 PCB_TRACK* track = static_cast<PCB_TRACK*>( boardItem );
277
278 staleTeardropTracks.insert( track );
279
280 std::vector<PAD*> connectedPads;
281 std::vector<PCB_VIA*> connectedVias;
282
283 connectivity->GetConnectedPadsAndVias( track, &connectedPads, &connectedVias );
284
285 for( PAD* pad : connectedPads )
286 staleTeardropPadsAndVias.push_back( pad );
287
288 for( PCB_VIA* via : connectedVias )
289 staleTeardropPadsAndVias.push_back( via );
290 }
291 }
292 }
293
294 if( boardItem->IsSelected() || ( m_isFootprintEditor && boardItem == board->GetFirstFootprint() ) )
295 selectedModified = true;
296 }
297
298 // Old teardrops must be removed before connectivity is rebuilt
299 if( !staleTeardropPadsAndVias.empty() || !staleTeardropTracks.empty() )
300 teardropMgr.RemoveTeardrops( *this, &staleTeardropPadsAndVias, &staleTeardropTracks );
301
302 auto updateComponentClasses =
303 [this]( BOARD_ITEM* boardItem )
304 {
305 if( boardItem->Type() != PCB_FOOTPRINT_T )
306 return;
307
308 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( boardItem );
310 };
311
312 // We don't know that anything will be added to the entered group, but it does no harm to
313 // add it to the commit anyway.
314 if( enteredGroup )
315 Modify( enteredGroup );
316
317
318 for( COMMIT_LINE& entry : m_entries )
319 {
320 if( !entry.m_item || !entry.m_item->IsBOARD_ITEM() )
321 continue;
322
323 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( entry.m_item );
324 int changeType = entry.m_type & CHT_TYPE;
325 int changeFlags = entry.m_type & CHT_FLAGS;
326
327 switch( changeType )
328 {
329 case CHT_ADD:
330 if( enteredGroup && boardItem->IsGroupableType() && !boardItem->GetParentGroup() )
331 enteredGroup->AddItem( boardItem );
332
333 if( !( aCommitFlags & SKIP_UNDO ) )
334 undoList.PushItem( ITEM_PICKER( nullptr, boardItem, UNDO_REDO::NEWITEM ) );
335
336 if( !( changeFlags & CHT_DONE ) )
337 {
338 // Markers are always stored at the board level, even in the footprint editor
339 if( m_isFootprintEditor && boardItem->Type() != PCB_MARKER_T )
340 {
341 if( FOOTPRINT* parentFP = board->GetFirstFootprint() )
342 parentFP->Add( boardItem );
343 }
344 else
345 {
346 board->Add( boardItem, ADD_MODE::BULK_INSERT ); // handles connectivity
347 bulkAddedItems.push_back( boardItem );
348 }
349 }
350
351 if( boardItem->Type() != PCB_MARKER_T )
352 propagateDamage( boardItem, staleZones, staleRuleAreas );
353
354 if( view && !isNoGeometryItem( boardItem ) )
355 view->Add( boardItem );
356
357 updateComponentClasses( boardItem );
358
359 break;
360
361 case CHT_REMOVE:
362 {
363 EDA_GROUP* parentGroup = boardItem->GetParentGroup();
364
365 if( !( aCommitFlags & SKIP_UNDO ) )
366 {
368 ITEM_PICKER itemWrapper( nullptr, boardItem, status );
369 itemWrapper.SetLink( entry.m_copy );
370 entry.m_copy = nullptr; // We've transferred ownership to the undo list
371 undoList.PushItem( itemWrapper );
372 }
373
374 if( boardItem->IsSelected() )
375 {
376 if( selTool )
377 selTool->RemoveItemFromSel( boardItem, true /* quiet mode */ );
378
379 itemsDeselected = true;
380 }
381
382 removedItems.insert( boardItem );
383
384 if( parentGroup && !( parentGroup->AsEdaItem()->GetFlags() & STRUCT_DELETED ) )
385 parentGroup->RemoveItem( boardItem );
386
387 if( boardItem->Type() != PCB_MARKER_T )
388 propagateDamage( boardItem, staleZones, staleRuleAreas );
389
390 switch( boardItem->Type() )
391 {
392 case PCB_FIELD_T:
393 static_cast<PCB_FIELD*>( boardItem )->SetVisible( false );
394
395 if( view )
396 view->Update( boardItem );
397
398 break;
399
400 case PCB_TEXT_T:
401 case PCB_PAD_T:
402 case PCB_SHAPE_T:
404 case PCB_GENERATOR_T:
405 case PCB_TEXTBOX_T:
406 case PCB_BARCODE_T:
407 case PCB_TABLE_T:
408 case PCB_TRACE_T:
409 case PCB_ARC_T:
410 case PCB_VIA_T:
412 case PCB_DIM_CENTER_T:
413 case PCB_DIM_RADIAL_T:
415 case PCB_DIM_LEADER_T:
416 case PCB_TARGET_T:
417 case PCB_POINT_T:
418 case PCB_ZONE_T:
419 case PCB_FOOTPRINT_T:
420 case PCB_GROUP_T:
421 if( view )
422 view->Remove( boardItem );
423
424 if( !( changeFlags & CHT_DONE ) )
425 {
426 if( m_isFootprintEditor && boardItem->Type() != PCB_MARKER_T )
427 {
428 if( FOOTPRINT* parentFP = board->GetFirstFootprint() )
429 parentFP->Remove( boardItem );
430 }
431 else if( FOOTPRINT* parentFP = boardItem->GetParentFootprint() )
432 {
433 parentFP->Remove( boardItem );
434 }
435 else
436 {
437 board->Remove( boardItem, REMOVE_MODE::BULK );
438 bulkRemovedItems.push_back( boardItem );
439 }
440 }
441
442 break;
443
444 case PCB_MARKER_T:
445 if( view )
446 view->Remove( boardItem );
447
448 if( !( changeFlags & CHT_DONE ) )
449 {
450 // Markers are always stored at the board level, even in the footprint editor
451 board->Remove( boardItem, REMOVE_MODE::BULK );
452 bulkRemovedItems.push_back( boardItem );
453 }
454
455 break;
456
457 // No-geometry items: never in the view, so skip view->Remove.
458 case PCB_CONSTRAINT_T:
459 if( !( changeFlags & CHT_DONE ) )
460 {
462 {
463 if( FOOTPRINT* parentFP = board->GetFirstFootprint() )
464 parentFP->Remove( boardItem );
465 }
466 else if( FOOTPRINT* parentFP = boardItem->GetParentFootprint() )
467 {
468 parentFP->Remove( boardItem );
469 }
470 else
471 {
472 board->Remove( boardItem, REMOVE_MODE::BULK );
473 bulkRemovedItems.push_back( boardItem );
474 }
475 }
476
477 break;
478
479 // Metadata items
480 case PCB_NETINFO_T:
481 board->Remove( boardItem, REMOVE_MODE::BULK );
482 bulkRemovedItems.push_back( boardItem );
483 break;
484
485 default: // other types do not need to (or should not) be handled
486 wxASSERT( false );
487 break;
488 }
489
490 // Removed items are owned by undo/redo, but a hidden field still belongs to its footprint
491 if( boardItem->Type() != PCB_FIELD_T )
492 boardItem->SetFlags( UR_TRANSIENT );
493
494 break;
495 }
496
497 case CHT_MODIFY:
498 {
499 BOARD_ITEM* boardItemCopy = static_cast<BOARD_ITEM*>( entry.m_copy );
500
501 if( !( aCommitFlags & SKIP_UNDO ) )
502 {
503 ITEM_PICKER itemWrapper( nullptr, boardItem, UNDO_REDO::CHANGED );
504 itemWrapper.SetLink( entry.m_copy );
505 entry.m_copy = nullptr; // We've transferred ownership to the undo list
506 undoList.PushItem( itemWrapper );
507 }
508
509 if( !( aCommitFlags & SKIP_CONNECTIVITY ) && !isNoGeometryItem( boardItem ) )
510 {
511 connectivity->MarkItemNetAsDirty( boardItemCopy );
512 connectivity->Update( boardItem );
513 }
514
515 if( boardItem->Type() != PCB_MARKER_T )
516 {
517 propagateDamage( boardItemCopy, staleZones, staleRuleAreas ); // before
518 propagateDamage( boardItem, staleZones, staleRuleAreas ); // after
519 }
520
521 updateComponentClasses( boardItem );
522
523 if( view && !isNoGeometryItem( boardItem ) )
524 view->Update( boardItem );
525
526 itemsChanged.push_back( boardItem );
527 break;
528 }
529
530 default:
531 UNIMPLEMENTED_FOR( boardItem->GetClass() );
532 break;
533 }
534
535 // Delete any copies we still have ownership of
536 delete entry.m_copy;
537 entry.m_copy = nullptr;
538
539 boardItem->ClearEditFlags();
540 boardItem->RunOnChildren(
541 [&]( BOARD_ITEM* item )
542 {
543 item->ClearEditFlags();
544 },
546 } // ... and regenerate them.
547
548 // Deselection above keys off the SELECTED flag on the removed item itself, but the selection
549 // may separately hold an owned descendant such as a pad, field or table cell. Descendants
550 // are freed along with the removed parent, so prune them here or the selection keeps a
551 // dangling pointer that the next repaint dereferences.
552 if( selTool && !removedItems.empty() )
553 {
554 for( EDA_ITEM* selectedItem : selTool->GetSelection().GetItems() )
555 {
556 for( EDA_ITEM* ancestor = selectedItem; ancestor; ancestor = ancestor->GetParent() )
557 {
558 if( removedItems.count( ancestor ) )
559 {
560 selTool->RemoveItemFromSel( selectedItem, true /* quiet mode */ );
561 itemsDeselected = true;
562 break;
563 }
564 }
565 }
566 }
567
568 // Invalidate component classes
570
571 if( m_isBoardEditor )
572 {
573 size_t originalCount = m_entries.size();
574
575 if( aCommitFlags & SKIP_CONNECTIVITY )
576 {
577 connectivity->ClearRatsnest();
578 connectivity->ClearLocalRatsnest();
579 }
580 else
581 {
582 connectivity->RecalculateRatsnest( this );
584 connectivity->ClearLocalRatsnest();
585
586 if( frame )
587 frame->GetCanvas()->RedrawRatsnest();
588
589 board->OnRatsnestChanged();
590 }
591
592 if( solderMaskDirty )
593 {
594 if( frame )
595 frame->HideSolderMask();
596 }
597
598 if( updateBoardBoundingBox && view )
599 {
600 if( PCB_BOARD_OUTLINE* outline = board->BoardOutline() )
601 {
602 board->UpdateBoardOutline();
603
604 if( view->HasItem( outline ) )
605 view->Update( outline );
606 else
607 view->Add( outline );
608 }
609 }
610
611 if( PCBNEW_SETTINGS* cfg = GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" ) )
612 {
613 if( !staleRuleAreas.empty() && ( cfg->m_Display.m_TrackClearance == SHOW_WITH_VIA_ALWAYS
614 || cfg->m_Display.m_PadClearance ) )
615 {
616 if( view )
617 view->UpdateCollidingItems( staleRuleAreas, { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T } );
618 }
619 }
620
621 if( !staleTeardropPadsAndVias.empty() || !staleTeardropTracks.empty() )
622 {
623 teardropMgr.UpdateTeardrops( *this, &staleTeardropPadsAndVias, &staleTeardropTracks );
624
625 // UpdateTeardrops() can modify the ratsnest data. So rebuild this ratsnest data
626 connectivity->RecalculateRatsnest( this );
627 }
628
629 // Log undo items for any connectivity or teardrop changes
630 for( size_t i = originalCount; i < m_entries.size(); ++i )
631 {
632 COMMIT_LINE& entry = m_entries[i];
633 BOARD_ITEM* boardItem = nullptr;
634 BOARD_ITEM* boardItemCopy = nullptr;
635
636 if( entry.m_item && entry.m_item->IsBOARD_ITEM() )
637 boardItem = static_cast<BOARD_ITEM*>( entry.m_item );
638
639 if( entry.m_copy && entry.m_copy->IsBOARD_ITEM() )
640 boardItemCopy = static_cast<BOARD_ITEM*>( entry.m_copy );
641
642 wxCHECK2( boardItem, continue );
643
644 if( !( aCommitFlags & SKIP_UNDO ) )
645 {
646 ITEM_PICKER itemWrapper( nullptr, boardItem, convert( entry.m_type & CHT_TYPE ) );
647 itemWrapper.SetLink( boardItemCopy );
648 undoList.PushItem( itemWrapper );
649 }
650 else
651 {
652 delete entry.m_copy;
653 }
654
655 if( view && !isNoGeometryItem( boardItem ) )
656 {
657 if( ( entry.m_type & CHT_TYPE ) == CHT_ADD )
658 view->Add( boardItem );
659 else if( ( entry.m_type & CHT_TYPE ) == CHT_REMOVE )
660 view->Remove( boardItem );
661 else
662 view->Update( boardItem );
663 }
664 }
665 }
666
667 if( bulkAddedItems.size() > 0 || bulkRemovedItems.size() > 0 || itemsChanged.size() > 0 )
668 board->OnItemsCompositeUpdate( bulkAddedItems, bulkRemovedItems, itemsChanged );
669
670 if( frame )
671 {
672 if( !( aCommitFlags & SKIP_UNDO ) )
673 {
674 if( aCommitFlags & APPEND_UNDO )
676 else
677 frame->SaveCopyInUndoList( undoList, UNDO_REDO::UNSPECIFIED );
678 }
679 }
680
681 m_toolMgr->PostEvent( { TC_MESSAGE, TA_MODEL_CHANGE, AS_GLOBAL } );
682
683 if( itemsDeselected )
685
686 if( autofillZones )
687 {
688 ZONE_FILLER_TOOL* zoneFillerTool = m_toolMgr->GetTool<ZONE_FILLER_TOOL>();
689
690 for( ZONE* zone : *staleZones )
691 zoneFillerTool->DirtyZone( zone );
692
694 }
695
697
698 if( selectedModified )
700
701 if( frame )
702 {
703 if( !( aCommitFlags & SKIP_SET_DIRTY ) )
704 frame->OnModify();
705 else
707
708 // Ensure the message panel is updated after committing changes.
709 // By default (i.e. if no event posted), display the updated board info
710 if( !itemsDeselected && !autofillZones && !selectedModified )
711 {
712 std::vector<MSG_PANEL_ITEM> msg_list;
713 board->GetMsgPanelInfo( frame, msg_list );
714 frame->SetMsgPanel( msg_list );
715 }
716 }
717
718 clear();
719}
720
721
723{
724 // Easiest way to disallow both a parent and one of its children appearing in the list
725 // is to only ever add the parent when either can be legal (ie: in the board editor).
726 if( m_isBoardEditor && aItem->IsBOARD_ITEM() )
727 {
728 if( FOOTPRINT* footprint = static_cast<BOARD_ITEM*>( aItem )->GetParentFootprint() )
729 return footprint;
730 }
731
732 EDA_ITEM* parent = aItem->GetParent();
733
734 if( parent && parent->Type() == PCB_TABLE_T )
735 return parent;
736
737 return aItem;
738}
739
740
742{
743 return MakeImage( aItem );
744}
745
746
748{
749 EDA_ITEM* clone = aItem->Clone();
750 clone->SetFlags( UR_TRANSIENT );
751
752 return clone;
753}
754
755
757{
758 PICKED_ITEMS_LIST undoList;
759 KIGFX::VIEW* view = m_toolMgr->GetView(); // null in headless sessions
760 BOARD* board = (BOARD*) m_toolMgr->GetModel();
761 std::shared_ptr<CONNECTIVITY_DATA> connectivity = board->GetConnectivity();
762
763 board->IncrementTimeStamp(); // clear caches
764
765 auto updateComponentClasses =
766 [this]( BOARD_ITEM* boardItem )
767 {
768 if( boardItem->Type() != PCB_FOOTPRINT_T )
769 return;
770
771 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( boardItem );
773 };
774
775 std::vector<BOARD_ITEM*> bulkAddedItems;
776 std::vector<BOARD_ITEM*> bulkRemovedItems;
777 std::vector<BOARD_ITEM*> itemsChanged;
778 std::vector<BOARD_ITEM*> itemsToDelete;
779
780 for( COMMIT_LINE& entry : m_entries )
781 {
782 if( !entry.m_item || !entry.m_item->IsBOARD_ITEM() )
783 continue;
784
785 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( entry.m_item );
786 int changeType = entry.m_type & CHT_TYPE;
787 int changeFlags = entry.m_type & CHT_FLAGS;
788
789 switch( changeType )
790 {
791 case CHT_ADD:
792 if( changeFlags & CHT_DONE )
793 {
794 if( view && !isNoGeometryItem( boardItem ) )
795 view->Remove( boardItem );
796
797 connectivity->Remove( boardItem );
798
800 {
801 if( FOOTPRINT* parentFP = board->GetFirstFootprint() )
802 parentFP->Remove( boardItem );
803 }
804 else if( FOOTPRINT* parentFP = boardItem->GetParentFootprint() )
805 {
806 parentFP->Remove( boardItem );
807 }
808 else
809 {
810 board->Remove( boardItem, REMOVE_MODE::BULK );
811 bulkRemovedItems.push_back( boardItem );
812 }
813 }
814
815 // Defer deletion until after OnItemsCompositeUpdate so that
816 // board listeners do not receive dangling pointers.
817 itemsToDelete.push_back( boardItem );
818 entry.m_item = nullptr;
819 continue;
820
821 case CHT_REMOVE:
822 {
823 if( !( changeFlags & CHT_DONE ) )
824 break;
825
826 if( view && !isNoGeometryItem( boardItem ) )
827 view->Add( boardItem );
828
830 {
831 if( FOOTPRINT* parentFP = board->GetFirstFootprint() )
832 parentFP->Add( boardItem, ADD_MODE::INSERT );
833 }
834 else if( FOOTPRINT* parentFP = boardItem->GetParentFootprint() )
835 {
836 parentFP->Add( boardItem, ADD_MODE::INSERT );
837 }
838 else
839 {
840 board->Add( boardItem, ADD_MODE::INSERT );
841 bulkAddedItems.push_back( boardItem );
842 }
843
844 updateComponentClasses( boardItem );
845 break;
846 }
847
848 case CHT_MODIFY:
849 {
850 if( view && !isNoGeometryItem( boardItem ) )
851 view->Remove( boardItem );
852
853 connectivity->Remove( boardItem );
854
855 wxASSERT( entry.m_copy && entry.m_copy->IsBOARD_ITEM() );
856 BOARD_ITEM* boardItemCopy = static_cast<BOARD_ITEM*>( entry.m_copy );
857 boardItem->SwapItemData( boardItemCopy );
858
859 if( view && !isNoGeometryItem( boardItem ) )
860 view->Add( boardItem );
861
862 connectivity->Add( boardItem );
863 itemsChanged.push_back( boardItem );
864
865 updateComponentClasses( boardItem );
866 break;
867 }
868
869 default:
870 UNIMPLEMENTED_FOR( boardItem->GetClass() );
871 break;
872 }
873
874 // Delete any copies we still have ownership of
875 delete entry.m_copy;
876 entry.m_copy = nullptr;
877
878 boardItem->ClearEditFlags();
879 }
880
881 // Invalidate component classes
883
884 if( bulkAddedItems.size() > 0 || bulkRemovedItems.size() > 0 || itemsChanged.size() > 0 )
885 board->OnItemsCompositeUpdate( bulkAddedItems, bulkRemovedItems, itemsChanged );
886
887 if( m_isBoardEditor )
888 {
889 for( BOARD_ITEM* item : itemsToDelete )
890 connectivity->Remove( item );
891 }
892
893 for( BOARD_ITEM* item : itemsToDelete )
894 delete item;
895
896 if( m_isBoardEditor )
897 {
898 connectivity->RecalculateRatsnest();
900 board->OnRatsnestChanged();
901 }
902
903 if( PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>() )
904 selTool->RebuildSelection();
905
906 // Property panel needs to know about the reselect
908
909 clear();
910}
static bool isNoGeometryItem(const BOARD_ITEM *aItem)
#define SKIP_CONNECTIVITY
#define SKIP_SET_DIRTY
#define APPEND_UNDO
#define SKIP_UNDO
#define ZONE_FILL_OP
#define SKIP_ENTERED_GROUP
#define SKIP_TEARDROPS
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
Handles how to draw a screen (a board, a schematic ...)
Definition base_screen.h:37
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
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.
BOARD * GetBoard() const
virtual void Revert() override
Revert the commit by restoring the modified items state.
static EDA_ITEM * MakeImage(EDA_ITEM *aItem)
EDA_ITEM * undoLevelItem(EDA_ITEM *aItem) const override
bool m_isBoardEditor
EDA_ITEM * makeImage(EDA_ITEM *aItem) const override
void propagateDamage(BOARD_ITEM *aItem, std::vector< ZONE * > *aStaleZones, std::vector< BOX2I > &aStaleRuleAreas)
TOOL_MANAGER * m_toolMgr
bool m_isFootprintEditor
BOARD_COMMIT(EDA_DRAW_FRAME *aFrame)
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:83
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:295
bool IsGroupableType() const
void SwapItemData(BOARD_ITEM *aImage)
Swap data between aItem and aImage.
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition board_item.h:377
FOOTPRINT * GetParentFootprint() const
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:315
virtual void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const
Invoke a function on all children.
Definition board_item.h:233
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:373
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1355
PCB_BOARD_OUTLINE * BoardOutline()
Definition board.h:436
void UpdateBoardOutline()
Definition board.cpp:4151
const ZONES & Zones() const
Definition board.h:425
void OnItemsCompositeUpdate(std::vector< BOARD_ITEM * > &aAddedItems, std::vector< BOARD_ITEM * > &aRemovedItems, std::vector< BOARD_ITEM * > &aChangedItems)
Notify the board and its listeners that items on the board have been modified in a composite operatio...
Definition board.cpp:3744
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition board.h:599
void IncrementTimeStamp()
Definition board.cpp:284
void OnRatsnestChanged()
Notify the board and its listeners that the ratsnest has been recomputed.
Definition board.cpp:3751
void GetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList) override
Populate aList of MSG_PANEL_ITEM objects with it's internal state for display purposes.
Definition board.cpp:2579
void UpdateRatsnestExclusions()
Update the visibility flags on the current unconnected ratsnest lines.
Definition board.cpp:361
COMPONENT_CLASS_MANAGER & GetComponentClassManager()
Gets the component class manager.
Definition board.h:1543
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition board.cpp:1503
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition board.h:646
std::set< std::pair< EDA_ITEM *, BASE_SCREEN * > > m_deletedItems
Definition commit.h:182
bool Empty() const
Definition commit.h:134
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
virtual void makeEntry(EDA_ITEM *aItem, CHANGE_TYPE aType, EDA_ITEM *aCopy=nullptr, BASE_SCREEN *aScreen=nullptr)
Definition commit.cpp:199
std::vector< COMMIT_LINE > m_entries
Definition commit.h:183
void clear()
Should be called in Push() & Revert() methods.
Definition commit.h:154
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
void InvalidateComponentClasses()
Invalidates any caches component classes and recomputes caches if required.
void RebuildRequiredCaches(FOOTPRINT *aFootprint=nullptr) const
Rebuilds any caches that may be required by custom assignment rules.
bool IsType(FRAME_T aType) const
The base class for create windows for drawing purpose.
void SetMsgPanel(const std::vector< MSG_PANEL_ITEM > &aList)
Clear the message panel and populates it with the contents of aList.
A set of EDA_ITEMs (i.e., without duplicates).
Definition eda_group.h:42
void RemoveItem(EDA_ITEM *aItem)
Remove item from group.
Definition eda_group.cpp:77
virtual EDA_ITEM * AsEdaItem()=0
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
virtual void ClearEditFlags()
Definition eda_item.h:166
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:135
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:152
virtual EDA_GROUP * GetParentGroup() const
Definition eda_item.h:114
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
bool IsSelected() const
Definition eda_item.h:132
EDA_ITEM * GetParent() const
Definition eda_item.h:110
virtual EDA_ITEM * Clone() const
Create a duplicate of this item with linked list members set to NULL.
Definition eda_item.cpp:143
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:155
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition actions.h:350
static const TOOL_EVENT UnselectedEvent
Definition actions.h:344
void SetLink(EDA_ITEM *aItem)
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const override
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition pcb_view.cpp:87
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1) override
Add a VIEW_ITEM to the view.
Definition pcb_view.cpp:53
void UpdateCollidingItems(const std::vector< BOX2I > &aStaleAreas, std::initializer_list< KICAD_T > aTypes)
Sets the KIGFX::REPAINT on all items matching aTypes which intersect aStaleAreas.
Definition pcb_view.cpp:118
virtual void Remove(VIEW_ITEM *aItem) override
Remove a VIEW_ITEM from the view.
Definition pcb_view.cpp:70
bool IsBOARD_ITEM() const
Definition view_item.h:98
virtual wxString GetClass() const =0
Return the class name.
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:300
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition view.cpp:404
bool HasItem(const VIEW_ITEM *aItem) const
Indicates whether or not the given item has been added to the view.
Definition view.cpp:1821
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:604
static const LSET & PhysicalLayersMask()
Return a mask holding all layers which are physically realized.
Definition lset.cpp:693
Definition pad.h:61
DISPLAY_OPTIONS m_Display
static TOOL_ACTION rehatchShapes
static TOOL_ACTION zoneFillDirty
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
PCBNEW_SETTINGS * GetPcbNewSettings() const
void OnModify() override
Must be called after a change in order to set the "modify" flag and update other data structures and ...
virtual void SaveCopyInUndoList(EDA_ITEM *aItemToCopy, UNDO_REDO aTypeCommand)
Create a new entry in undo list of commands.
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
virtual void Update3DView(bool aMarkDirty, bool aRefresh, const wxString *aTitle=nullptr)
Update the 3D view, if the viewer is opened by this frame.
virtual void AppendCopyToUndoList(const PICKED_ITEMS_LIST &aItemsList, UNDO_REDO aTypeCommand)
As SaveCopyInUndoList, but appends the changes to the last undo item on the stack.
void RedrawRatsnest()
Return the bounding box of the view that should be used if model is not valid.
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:51
The selection tool: currently supports:
PCB_GROUP * GetEnteredGroup()
PCB_SELECTION & GetSelection()
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)
int RemoveItemFromSel(const TOOL_EVENT &aEvent)
const std::deque< EDA_ITEM * > GetItems() const
Definition selection.h:125
TEARDROP_MANAGER manage and build teardrop areas A teardrop area is a polygonal area (a copper ZONE) ...
Definition teardrop.h:91
void UpdateTeardrops(BOARD_COMMIT &aCommit, const std::vector< BOARD_ITEM * > *dirtyPadsAndVias, const std::set< PCB_TRACK * > *dirtyTracks, bool aForceFullUpdate=false)
Update teardrops on a list of items.
Definition teardrop.cpp:225
void RemoveTeardrops(BOARD_COMMIT &aCommit, const std::vector< BOARD_ITEM * > *dirtyPadsAndVias, const std::set< PCB_TRACK * > *dirtyTracks)
Remove teardrops connected to any dirty pads, vias or tracks.
Definition teardrop.cpp:180
Base abstract interface for all kinds of tools.
Definition tool_base.h:62
Master controller class:
TOOLS_HOLDER * GetToolHolder() const
Handle actions specific to filling copper zones.
void DirtyZone(ZONE *aZone)
Handle a list of polygons defining a copper zone.
Definition zone.h:70
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:48
@ RECURSE
Definition eda_item.h:49
@ NO_RECURSE
Definition eda_item.h:50
#define STRUCT_DELETED
flag indication structures to be erased
#define UR_TRANSIENT
indicates the item is owned by the undo/redo stack
@ FRAME_PCB_EDITOR
Definition frame_type.h:38
@ FRAME_FOOTPRINT_EDITOR
Definition frame_type.h:39
@ Edge_Cuts
Definition layer_ids.h:108
@ B_Mask
Definition layer_ids.h:94
@ F_Mask
Definition layer_ids.h:93
@ Margin
Definition layer_ids.h:109
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:92
Class to handle a set of BOARD_ITEMs.
@ SHOW_WITH_VIA_ALWAYS
see class PGM_BASE
T * GetAppSettings(const char *aFilename)
EDA_ITEM * m_copy
Optional copy of the item.
Definition commit.h:148
CHANGE_TYPE m_type
Modification type.
Definition commit.h:149
EDA_ITEM * m_item
Main item that is added/deleted/modified.
Definition commit.h:147
@ 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
@ PCB_CONSTRAINT_T
class PCB_CONSTRAINT, a geometric constraint between board items
Definition typeinfo.h:238
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:81
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:99
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:96
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition typeinfo.h:84
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:90
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:97
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:104
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:86
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:101
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:85
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:82
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:83
@ PCB_MARKER_T
class PCB_MARKER, a marker used to show something
Definition typeinfo.h:92
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:94
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition typeinfo.h:100
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:79
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:95
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:80
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:91
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:87
@ PCB_NETINFO_T
class NETINFO_ITEM, a description of a net
Definition typeinfo.h:103
@ PCB_POINT_T
class PCB_POINT, a 0-dimensional point
Definition typeinfo.h:106
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:89
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:98
UNDO_REDO
Undo Redo considerations: Basically we have 3 cases New item Deleted item Modified item there is also...