1 /*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2016 CERN
5 * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25 #include <board.h>
26 #include <footprint.h>
27 #include <pcb_group.h>
28 #include <tool/tool_manager.h>
29 #include <tools/pcb_selection_tool.h>
30 #include <view/view.h>
31 #include <board_commit.h>
32 #include <tools/pcb_tool_base.h>
33 #include <tools/pcb_actions.h>
34 #include <connectivity/connectivity_data.h>
35
36 #include <functional>
37 using namespace std::placeholders;
38
39
BOARD_COMMIT(TOOL_MANAGER * aToolMgr)40 BOARD_COMMIT::BOARD_COMMIT( TOOL_MANAGER* aToolMgr ) :
41 m_toolMgr( aToolMgr ),
42 m_isFootprintEditor( false ),
43 m_resolveNetConflicts( false )
44 {
45 }
46
47
BOARD_COMMIT(PCB_TOOL_BASE * aTool)48 BOARD_COMMIT::BOARD_COMMIT( PCB_TOOL_BASE* aTool ) :
49 m_resolveNetConflicts( false )
50 {
51 m_toolMgr = aTool->GetManager();
52 m_isFootprintEditor = aTool->IsFootprintEditor();
53 }
54
55
BOARD_COMMIT(EDA_DRAW_FRAME * aFrame)56 BOARD_COMMIT::BOARD_COMMIT( EDA_DRAW_FRAME* aFrame ) :
57 m_resolveNetConflicts( false )
58 {
59 m_toolMgr = aFrame->GetToolManager();
60 m_isFootprintEditor = aFrame->IsType( FRAME_FOOTPRINT_EDITOR );
61 }
62
63
~BOARD_COMMIT()64 BOARD_COMMIT::~BOARD_COMMIT()
65 {
66 }
67
68
Stage(EDA_ITEM * aItem,CHANGE_TYPE aChangeType)69 COMMIT& BOARD_COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType )
70 {
71 // if aItem belongs a footprint, the full footprint will be saved
72 // because undo/redo does not handle "sub items" modifications
73 if( aItem && aItem->Type() != PCB_FOOTPRINT_T && aChangeType == CHT_MODIFY )
74 {
75 EDA_ITEM* item = aItem->GetParent();
76
77 if( item && item->Type() == PCB_FOOTPRINT_T ) // means aItem belongs a footprint
78 aItem = item;
79 }
80
81 return COMMIT::Stage( aItem, aChangeType );
82 }
83
84
Stage(std::vector<EDA_ITEM * > & container,CHANGE_TYPE aChangeType)85 COMMIT& BOARD_COMMIT::Stage( std::vector<EDA_ITEM*>& container, CHANGE_TYPE aChangeType )
86 {
87 return COMMIT::Stage( container, aChangeType );
88 }
89
90
Stage(const PICKED_ITEMS_LIST & aItems,UNDO_REDO aModFlag)91 COMMIT& BOARD_COMMIT::Stage( const PICKED_ITEMS_LIST& aItems, UNDO_REDO aModFlag )
92 {
93 return COMMIT::Stage( aItems, aModFlag );
94 }
95
96
Push(const wxString & aMessage,bool aCreateUndoEntry,bool aSetDirtyBit)97 void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry, bool aSetDirtyBit )
98 {
99 // Objects potentially interested in changes:
100 PICKED_ITEMS_LIST undoList;
101 KIGFX::VIEW* view = m_toolMgr->GetView();
102 BOARD* board = (BOARD*) m_toolMgr->GetModel();
103 PCB_BASE_FRAME* frame = dynamic_cast<PCB_BASE_FRAME*>( m_toolMgr->GetToolHolder() );
104 auto connectivity = board->GetConnectivity();
105 std::set<EDA_ITEM*> savedModules;
106 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
107 bool itemsDeselected = false;
108
109 std::vector<BOARD_ITEM*> bulkAddedItems;
110 std::vector<BOARD_ITEM*> bulkRemovedItems;
111 std::vector<BOARD_ITEM*> itemsChanged;
112
113 if( Empty() )
114 return;
115
116 for( COMMIT_LINE& ent : m_changes )
117 {
118 int changeType = ent.m_type & CHT_TYPE;
119 int changeFlags = ent.m_type & CHT_FLAGS;
120 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( ent.m_item );
121
122 // Module items need to be saved in the undo buffer before modification
123 if( m_isFootprintEditor )
124 {
125 // Be sure that we are storing a footprint
126 if( ent.m_item->Type() != PCB_FOOTPRINT_T )
127 ent.m_item = ent.m_item->GetParent();
128
129 // We have not saved the footprint yet, so let's create an entry
130 if( savedModules.count( ent.m_item ) == 0 )
131 {
132 if( !ent.m_copy )
133 {
134 wxASSERT( changeType != CHT_MODIFY ); // too late to make a copy..
135 ent.m_copy = ent.m_item->Clone();
136 }
137
138 wxASSERT( ent.m_item->Type() == PCB_FOOTPRINT_T );
139 wxASSERT( ent.m_copy->Type() == PCB_FOOTPRINT_T );
140
141 if( aCreateUndoEntry && frame )
142 {
143 ITEM_PICKER itemWrapper( nullptr, ent.m_item, UNDO_REDO::CHANGED );
144 itemWrapper.SetLink( ent.m_copy );
145 undoList.PushItem( itemWrapper );
146 frame->SaveCopyInUndoList( undoList, UNDO_REDO::CHANGED );
147 }
148
149 savedModules.insert( ent.m_item );
150 static_cast<FOOTPRINT*>( ent.m_item )->SetLastEditTime();
151 }
152 }
153
154 switch( changeType )
155 {
156 case CHT_ADD:
157 {
158 if( m_isFootprintEditor )
159 {
160 // footprints inside footprints are not supported yet
161 wxASSERT( boardItem->Type() != PCB_FOOTPRINT_T );
162
163 boardItem->SetParent( board->Footprints().front() );
164
165 if( !( changeFlags & CHT_DONE ) )
166 board->Footprints().front()->Add( boardItem );
167 }
168 else if( boardItem->Type() == PCB_PAD_T ||
169 boardItem->Type() == PCB_FP_TEXT_T ||
170 boardItem->Type() == PCB_FP_SHAPE_T ||
171 boardItem->Type() == PCB_FP_ZONE_T )
172 {
173 wxASSERT( boardItem->GetParent() &&
174 boardItem->GetParent()->Type() == PCB_FOOTPRINT_T );
175 }
176 else
177 {
178 if( aCreateUndoEntry )
179 undoList.PushItem( ITEM_PICKER( nullptr, boardItem, UNDO_REDO::NEWITEM ) );
180
181 if( !( changeFlags & CHT_DONE ) )
182 {
183 board->Add( boardItem, ADD_MODE::BULK_INSERT ); // handles connectivity
184 bulkAddedItems.push_back( boardItem );
185 }
186 }
187
188 if( view && boardItem->Type() != PCB_NETINFO_T )
189 view->Add( boardItem );
190
191 break;
192 }
193
194 case CHT_REMOVE:
195 {
196 PCB_GROUP* parentGroup = boardItem->GetParentGroup();
197
198 if( !m_isFootprintEditor && aCreateUndoEntry )
199 undoList.PushItem( ITEM_PICKER( nullptr, boardItem, UNDO_REDO::DELETED ) );
200
201 if( boardItem->IsSelected() )
202 {
203 selTool->RemoveItemFromSel( boardItem, true /* quiet mode */ );
204 itemsDeselected = true;
205 }
206
207 switch( boardItem->Type() )
208 {
209 // Footprint items
210 case PCB_PAD_T:
211 case PCB_FP_SHAPE_T:
212 case PCB_FP_TEXT_T:
213 case PCB_FP_ZONE_T:
214 // This level can only handle footprint children in the footprint editor as
215 // only in that case has the entire footprint (and all its children) already
216 // been saved for undo.
217 wxASSERT( m_isFootprintEditor );
218
219 if( boardItem->Type() == PCB_FP_TEXT_T )
220 {
221 FP_TEXT* text = static_cast<FP_TEXT*>( boardItem );
222
223 // don't allow deletion of Reference or Value
224 if( text->GetType() != FP_TEXT::TEXT_is_DIVERS )
225 break;
226 }
227
228 if( parentGroup && !( parentGroup->GetFlags() & STRUCT_DELETED ) )
229 parentGroup->RemoveItem( boardItem );
230
231 if( view )
232 view->Remove( boardItem );
233
234 if( !( changeFlags & CHT_DONE ) )
235 {
236 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( boardItem->GetParent() );
237 wxASSERT( footprint && footprint->Type() == PCB_FOOTPRINT_T );
238 footprint->Delete( boardItem );
239 }
240
241 break;
242
243 // Board items
244 case PCB_SHAPE_T: // a shape (normally not on copper layers)
245 case PCB_TEXT_T: // a text on a layer
246 case PCB_TRACE_T: // a track segment (segment on a copper layer)
247 case PCB_ARC_T: // an arced track segment (segment on a copper layer)
248 case PCB_VIA_T: // a via (like track segment on a copper layer)
249 case PCB_DIM_ALIGNED_T: // a dimension (graphic item)
250 case PCB_DIM_CENTER_T:
251 case PCB_DIM_ORTHOGONAL_T:
252 case PCB_DIM_LEADER_T: // a leader dimension
253 case PCB_TARGET_T: // a target (graphic item)
254 case PCB_MARKER_T: // a marker used to show something
255 case PCB_ZONE_T:
256 if( view )
257 view->Remove( boardItem );
258
259 if( !( changeFlags & CHT_DONE ) )
260 {
261 board->Remove( boardItem, REMOVE_MODE::BULK );
262 bulkRemovedItems.push_back( boardItem );
263 }
264
265 break;
266
267 case PCB_FOOTPRINT_T:
268 {
269 // No support for nested footprints (yet)
270 wxASSERT( !m_isFootprintEditor );
271
272 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( boardItem );
273
274 if( view )
275 view->Remove( footprint );
276
277 footprint->ClearFlags();
278
279 if( !( changeFlags & CHT_DONE ) )
280 {
281 board->Remove( footprint, REMOVE_MODE::BULK ); // handles connectivity
282 bulkRemovedItems.push_back( footprint );
283 }
284 }
285 break;
286
287 case PCB_GROUP_T:
288 if( view )
289 view->Remove( boardItem );
290
291 if( !( changeFlags & CHT_DONE ) )
292 {
293 if( m_isFootprintEditor )
294 board->GetFirstFootprint()->Remove( boardItem );
295 else
296 {
297 board->Remove( boardItem, REMOVE_MODE::BULK );
298 bulkRemovedItems.push_back( boardItem );
299 }
300 }
301 break;
302
303 // Metadata items
304 case PCB_NETINFO_T:
305 board->Remove( boardItem, REMOVE_MODE::BULK );
306 bulkRemovedItems.push_back( boardItem );
307 break;
308
309 default: // other types do not need to (or should not) be handled
310 wxASSERT( false );
311 break;
312 }
313
314 break;
315 }
316
317 case CHT_MODIFY:
318 {
319 if( !m_isFootprintEditor && aCreateUndoEntry )
320 {
321 ITEM_PICKER itemWrapper( nullptr, boardItem, UNDO_REDO::CHANGED );
322 wxASSERT( ent.m_copy );
323 itemWrapper.SetLink( ent.m_copy );
324 undoList.PushItem( itemWrapper );
325 }
326
327 if( ent.m_copy )
328 connectivity->MarkItemNetAsDirty( static_cast<BOARD_ITEM*>( ent.m_copy ) );
329
330 connectivity->Update( boardItem );
331
332 if( view )
333 {
334 view->Update( boardItem );
335
336 if( m_isFootprintEditor )
337 {
338 static_cast<FOOTPRINT*>( boardItem )->RunOnChildren(
339 [&]( BOARD_ITEM* aChild )
340 {
341 view->Update( aChild );
342 });
343 }
344 }
345
346 itemsChanged.push_back( boardItem );
347
348 // if no undo entry is needed, the copy would create a memory leak
349 if( !aCreateUndoEntry )
350 delete ent.m_copy;
351
352 break;
353 }
354
355 default:
356 wxASSERT( false );
357 break;
358 }
359 }
360
361 if( bulkAddedItems.size() > 0 )
362 board->FinalizeBulkAdd( bulkAddedItems );
363
364 if( bulkRemovedItems.size() > 0 )
365 board->FinalizeBulkRemove( bulkRemovedItems );
366
367 if( itemsChanged.size() > 0 )
368 board->OnItemsChanged( itemsChanged );
369
370 if( !m_isFootprintEditor )
371 {
372 size_t num_changes = m_changes.size();
373
374 if( m_resolveNetConflicts )
375 connectivity->PropagateNets( this, PROPAGATE_MODE::RESOLVE_CONFLICTS );
376
377 connectivity->RecalculateRatsnest( this );
378 connectivity->ClearDynamicRatsnest();
379
380 if( frame )
381 frame->GetCanvas()->RedrawRatsnest();
382
383 if( m_changes.size() > num_changes )
384 {
385 for( size_t i = num_changes; i < m_changes.size(); ++i )
386 {
387 COMMIT_LINE& ent = m_changes[i];
388
389 // This should only be modifications from the connectivity algo
390 wxASSERT( ( ent.m_type & CHT_TYPE ) == CHT_MODIFY );
391
392 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( ent.m_item );
393
394 if( aCreateUndoEntry )
395 {
396 ITEM_PICKER itemWrapper( nullptr, boardItem, UNDO_REDO::CHANGED );
397 wxASSERT( ent.m_copy );
398 itemWrapper.SetLink( ent.m_copy );
399 undoList.PushItem( itemWrapper );
400 }
401 else
402 {
403 delete ent.m_copy;
404 }
405
406 if( view )
407 view->Update( boardItem );
408 }
409 }
410 }
411
412 if( !m_isFootprintEditor && aCreateUndoEntry && frame )
413 frame->SaveCopyInUndoList( undoList, UNDO_REDO::UNSPECIFIED );
414
415 m_toolMgr->PostEvent( { TC_MESSAGE, TA_MODEL_CHANGE, AS_GLOBAL } );
416
417 if( itemsDeselected )
418 m_toolMgr->PostEvent( EVENTS::UnselectedEvent );
419
420 if( frame )
421 {
422 if( aSetDirtyBit )
423 frame->OnModify();
424 else
425 frame->Update3DView( true, frame->GetDisplayOptions().m_Live3DRefresh );
426 }
427
428 clear();
429 }
430
431
parentObject(EDA_ITEM * aItem) const432 EDA_ITEM* BOARD_COMMIT::parentObject( EDA_ITEM* aItem ) const
433 {
434 switch( aItem->Type() )
435 {
436 case PCB_PAD_T:
437 case PCB_FP_SHAPE_T:
438 case PCB_FP_TEXT_T:
439 case PCB_FP_ZONE_T:
440 return aItem->GetParent();
441
442 case PCB_ZONE_T:
443 wxASSERT( !dynamic_cast<FOOTPRINT*>( aItem->GetParent() ) );
444 return aItem;
445
446 default:
447 break;
448 }
449
450 return aItem;
451 }
452
453
Revert()454 void BOARD_COMMIT::Revert()
455 {
456 PICKED_ITEMS_LIST undoList;
457 KIGFX::VIEW* view = m_toolMgr->GetView();
458 BOARD* board = (BOARD*) m_toolMgr->GetModel();
459 auto connectivity = board->GetConnectivity();
460
461 std::vector<BOARD_ITEM*> bulkAddedItems;
462 std::vector<BOARD_ITEM*> bulkRemovedItems;
463 std::vector<BOARD_ITEM*> itemsChanged;
464
465 for( auto it = m_changes.rbegin(); it != m_changes.rend(); ++it )
466 {
467 COMMIT_LINE& ent = *it;
468 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( ent.m_item );
469 BOARD_ITEM* copy = static_cast<BOARD_ITEM*>( ent.m_copy );
470 int changeType = ent.m_type & CHT_TYPE;
471 int changeFlags = ent.m_type & CHT_FLAGS;
472
473 switch( changeType )
474 {
475 case CHT_ADD:
476 if( !( changeFlags & CHT_DONE ) )
477 break;
478
479 view->Remove( item );
480 connectivity->Remove( item );
481 board->Remove( item, REMOVE_MODE::BULK );
482 bulkRemovedItems.push_back( item );
483 break;
484
485 case CHT_REMOVE:
486 if( !( changeFlags & CHT_DONE ) )
487 break;
488
489 view->Add( item );
490 connectivity->Add( item );
491 board->Add( item, ADD_MODE::INSERT );
492 bulkAddedItems.push_back( item );
493 break;
494
495 case CHT_MODIFY:
496 {
497 view->Remove( item );
498 connectivity->Remove( item );
499
500 item->SwapData( copy );
501
502 view->Add( item );
503 connectivity->Add( item );
504 board->OnItemChanged( item );
505 itemsChanged.push_back( item );
506
507 delete copy;
508 break;
509 }
510
511 default:
512 wxASSERT( false );
513 break;
514 }
515 }
516
517 if( bulkAddedItems.size() > 0 )
518 board->FinalizeBulkAdd( bulkAddedItems );
519
520 if( bulkRemovedItems.size() > 0 )
521 board->FinalizeBulkRemove( bulkRemovedItems );
522
523 if( itemsChanged.size() > 0 )
524 board->OnItemsChanged( itemsChanged );
525
526 if ( !m_isFootprintEditor )
527 connectivity->RecalculateRatsnest();
528
529 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
530 selTool->RebuildSelection();
531
532 clear();
533 }
534
535