KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_move_tool.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 CERN
5 * Copyright (C) 2019-2024 KiCad Developers, see AUTHORS.txt for contributors.
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 <cmath>
26#include <wx/log.h>
27#include <trigo.h>
29#include <tool/tool_manager.h>
33#include <ee_actions.h>
34#include <sch_commit.h>
35#include <eda_item.h>
36#include <sch_item.h>
37#include <sch_symbol.h>
38#include <sch_sheet.h>
39#include <sch_sheet_pin.h>
40#include <sch_line.h>
41#include <sch_junction.h>
42#include <sch_edit_frame.h>
43#include <eeschema_id.h>
44#include <pgm_base.h>
45#include <view/view_controls.h>
47#include "sch_move_tool.h"
48
49
50// For adding to or removing from selections
51#define QUIET_MODE true
52
53
55 EE_TOOL_BASE<SCH_EDIT_FRAME>( "eeschema.InteractiveMove" ),
56 m_inMoveTool( false ),
57 m_moveInProgress( false ),
58 m_isDrag( false ),
59 m_moveOffset( 0, 0 )
60{
61}
62
63
65{
67
68 auto moveCondition =
69 []( const SELECTION& aSel )
70 {
71 if( aSel.Empty() || SELECTION_CONDITIONS::OnlyTypes( { SCH_MARKER_T } )( aSel ) )
72 return false;
73
75 return false;
76
77 return true;
78 };
79
80 // Add move actions to the selection tool menu
81 //
83
84 selToolMenu.AddItem( EE_ACTIONS::move, moveCondition, 150 );
85 selToolMenu.AddItem( EE_ACTIONS::drag, moveCondition, 150 );
86 selToolMenu.AddItem( EE_ACTIONS::alignToGrid, moveCondition, 150 );
87
88 return true;
89}
90
91
92void SCH_MOVE_TOOL::orthoLineDrag( SCH_COMMIT* aCommit, SCH_LINE* line, const VECTOR2I& splitDelta,
93 int& xBendCount, int& yBendCount, const EE_GRID_HELPER& grid )
94{
95 // If the move is not the same angle as this move, then we need to do something special with
96 // the unselected end to maintain orthogonality. Either drag some connected line that is the
97 // same angle as the move or add two lines to make a 90 degree connection
98 if( !EDA_ANGLE( splitDelta ).IsParallelTo( line->Angle() ) || line->GetLength() == 0 )
99 {
100 VECTOR2I unselectedEnd = line->HasFlag( STARTPOINT ) ? line->GetEndPoint()
101 : line->GetStartPoint();
102 VECTOR2I selectedEnd = line->HasFlag( STARTPOINT ) ? line->GetStartPoint()
103 : line->GetEndPoint();
104
105 // Look for pre-existing lines we can drag with us instead of creating new ones
106 bool foundAttachment = false;
107 bool foundJunction = false;
108 bool foundPin = false;
109 SCH_LINE* foundLine = nullptr;
110
111 for( EDA_ITEM* cItem : m_lineConnectionCache[line] )
112 {
113 foundAttachment = true;
114
115 // If the move is the same angle as a connected line, we can shrink/extend that line
116 // endpoint
117 switch( cItem->Type() )
118 {
119 case SCH_LINE_T:
120 {
121 SCH_LINE* cLine = static_cast<SCH_LINE*>( cItem );
122
123 // A matching angle on a non-zero-length line means lengthen/shorten will work
124 if( EDA_ANGLE( splitDelta ).IsParallelTo( cLine->Angle() )
125 && cLine->GetLength() != 0 )
126 {
127 foundLine = cLine;
128 }
129
130 // Zero length lines are lines that this algorithm has shortened to 0 so they also
131 // work but we should prefer using a segment with length and angle matching when
132 // we can (otherwise the zero length line will draw overlapping segments on them)
133 if( !foundLine && cLine->GetLength() == 0 )
134 foundLine = cLine;
135
136 break;
137 }
138 case SCH_JUNCTION_T:
139 foundJunction = true;
140 break;
141
142 case SCH_PIN_T:
143 foundPin = true;
144 break;
145
146 case SCH_SHEET_T:
147 for( const auto& pair : m_specialCaseSheetPins )
148 {
149 if( pair.first->IsConnected( selectedEnd ) )
150 {
151 foundPin = true;
152 break;
153 }
154 }
155
156 break;
157
158 default:
159 break;
160 }
161 }
162
163 // Ok... what if our original line is length zero from moving in its direction, and the
164 // last added segment of the 90 bend we are connected to is zero from moving it in its
165 // direction after it was added?
166 //
167 // If we are moving in original direction, we should lengthen the original drag wire.
168 // Otherwise we should lengthen the new wire.
169 bool preferOriginalLine = false;
170
171 if( foundLine
172 && foundLine->GetLength() == 0
173 && line->GetLength() == 0
174 && EDA_ANGLE( splitDelta ).IsParallelTo( line->GetStoredAngle() ) )
175 {
176 preferOriginalLine = true;
177 }
178 // If we have found an attachment, but not a line, we want to check if it's a junction.
179 // These are special-cased and get a single line added instead of a 90-degree bend. Except
180 // when we're on a pin, because pins always need bends, and junctions are just added to
181 // pins for visual clarity.
182 else if( !foundLine && foundJunction && !foundPin )
183 {
184 // Create a new wire ending at the unselected end
185 foundLine = new SCH_LINE( unselectedEnd, line->GetLayer() );
186 foundLine->SetFlags( IS_NEW );
187 foundLine->SetLastResolvedState( line );
188 m_frame->AddToScreen( foundLine, m_frame->GetScreen() );
189 m_newDragLines.insert( foundLine );
190
191 // We just broke off of the existing items, so replace all of them with our new
192 // end connection.
194 m_lineConnectionCache[line].clear();
195 m_lineConnectionCache[line].emplace_back( foundLine );
196 }
197
198 // We want to drag our found line if it's in the same angle as the move or zero length,
199 // but if the original drag line is also zero and the same original angle we should extend
200 // that one first
201 if( foundLine && !preferOriginalLine )
202 {
203 // Move the connected line found oriented in the direction of our move.
204 //
205 // Make sure we grab the right endpoint, it's not always STARTPOINT since the user can
206 // draw a box of lines. We need to only move one though, and preferably the start point,
207 // in case we have a zero length line that we are extending (we want the foundLine
208 // start point to be attached to the unselected end of our drag line).
209 //
210 // Also, new lines are added already so they'll be in the undo list, skip adding them.
211
212 if( !foundLine->HasFlag( IS_CHANGED ) && !foundLine->HasFlag( IS_NEW ) )
213 {
214 aCommit->Modify( (SCH_ITEM*) foundLine, m_frame->GetScreen() );
215
216 if( !foundLine->IsSelected() )
217 m_changedDragLines.insert( foundLine );
218 }
219
220 if( foundLine->GetStartPoint() == unselectedEnd )
221 foundLine->MoveStart( splitDelta );
222 else if( foundLine->GetEndPoint() == unselectedEnd )
223 foundLine->MoveEnd( splitDelta );
224
225 updateItem( foundLine, true );
226
227 SCH_LINE* bendLine = nullptr;
228
229 if( m_lineConnectionCache.count( foundLine ) == 1
230 && m_lineConnectionCache[foundLine][0]->Type() == SCH_LINE_T )
231 {
232 bendLine = static_cast<SCH_LINE*>( m_lineConnectionCache[foundLine][0] );
233 }
234
235 // Remerge segments we've created if this is a segment that we've added whose only
236 // other connection is also an added segment
237 //
238 // bendLine is first added segment at the original attachment point, foundLine is the
239 // orthogonal line between bendLine and this line
240 if( foundLine->HasFlag( IS_NEW )
241 && foundLine->GetLength() == 0
242 && bendLine && bendLine->HasFlag( IS_NEW ) )
243 {
244 if( line->HasFlag( STARTPOINT ) )
245 line->SetEndPoint( bendLine->GetEndPoint() );
246 else
247 line->SetStartPoint( bendLine->GetEndPoint() );
248
249 // Update our cache of the connected items.
250
251 // First, re-attach our drag labels to the original line being re-merged.
252 for( EDA_ITEM* candidate : m_lineConnectionCache[bendLine] )
253 {
254 SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( candidate );
255
256 if( label && m_specialCaseLabels.count( label ) )
257 m_specialCaseLabels[label].attachedLine = line;
258 }
259
261 m_lineConnectionCache[bendLine].clear();
262 m_lineConnectionCache[foundLine].clear();
263
264 m_frame->RemoveFromScreen( bendLine, m_frame->GetScreen() );
265 m_frame->RemoveFromScreen( foundLine, m_frame->GetScreen() );
266
267 m_newDragLines.erase( bendLine );
268 m_newDragLines.erase( foundLine );
269
270 delete bendLine;
271 delete foundLine;
272 }
273 //Ok, move the unselected end of our item
274 else
275 {
276 if( line->HasFlag( STARTPOINT ) )
277 line->MoveEnd( splitDelta );
278 else
279 line->MoveStart( splitDelta );
280 }
281
282 updateItem( line, true );
283 }
284 else if( line->GetLength() == 0 )
285 {
286 // We didn't find another line to shorten/lengthen, (or we did but it's also zero)
287 // so now is a good time to use our existing zero-length original line
288 }
289 // Either no line was at the "right" angle, or this was a junction, pin, sheet, etc. We
290 // need to add segments to keep the soon-to-move unselected end connected to these items.
291 //
292 // To keep our drag selections all the same, we'll move our unselected end point and then
293 // put wires between it and its original endpoint.
294 else if( foundAttachment && line->IsOrthogonal() )
295 {
296 VECTOR2D lineGrid = grid.GetGridSize( grid.GetItemGrid( line ) );
297
298 // The bend counter handles a group of wires all needing their offset one grid movement
299 // further out from each other to not overlap. The absolute value stuff finds the
300 // direction of the line and hence the the bend increment on that axis
301 unsigned int xMoveBit = splitDelta.x != 0;
302 unsigned int yMoveBit = splitDelta.y != 0;
303 int xLength = abs( unselectedEnd.x - selectedEnd.x );
304 int yLength = abs( unselectedEnd.y - selectedEnd.y );
305 int xMove = ( xLength - ( xBendCount * lineGrid.x ) )
306 * sign( selectedEnd.x - unselectedEnd.x );
307 int yMove = ( yLength - ( yBendCount * lineGrid.y ) )
308 * sign( selectedEnd.y - unselectedEnd.y );
309
310 // Create a new wire ending at the unselected end, we'll move the new wire's start
311 // point to the unselected end
312 SCH_LINE* a = new SCH_LINE( unselectedEnd, line->GetLayer() );
313 a->MoveStart( VECTOR2I( xMove, yMove ) );
314 a->SetFlags( IS_NEW );
315 a->SetConnectivityDirty( true );
316 a->SetLastResolvedState( line );
318 m_newDragLines.insert( a );
319
320 SCH_LINE* b = new SCH_LINE( a->GetStartPoint(), line->GetLayer() );
321 b->MoveStart( VECTOR2I( splitDelta.x, splitDelta.y ) );
322 b->SetFlags( IS_NEW | STARTPOINT );
323 b->SetConnectivityDirty( true );
324 b->SetLastResolvedState( line );
326 m_newDragLines.insert( b );
327
328 xBendCount += yMoveBit;
329 yBendCount += xMoveBit;
330
331 // Ok move the unselected end of our item
332 if( line->HasFlag( STARTPOINT ) )
333 {
334 line->MoveEnd( VECTOR2I( splitDelta.x ? splitDelta.x : xMove,
335 splitDelta.y ? splitDelta.y : yMove ) );
336 }
337 else
338 {
339 line->MoveStart( VECTOR2I( splitDelta.x ? splitDelta.x : xMove,
340 splitDelta.y ? splitDelta.y : yMove ) );
341 }
342
343 // Update our cache of the connected items. First, attach our drag labels to the line
344 // left behind.
345 for( EDA_ITEM* candidate : m_lineConnectionCache[line] )
346 {
347 SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( candidate );
348
349 if( label && m_specialCaseLabels.count( label ) )
350 m_specialCaseLabels[label].attachedLine = a;
351 }
352
353 // We just broke off of the existing items, so replace all of them with our new end
354 // connection.
356 m_lineConnectionCache[b].emplace_back( a );
357 m_lineConnectionCache[line].clear();
358 m_lineConnectionCache[line].emplace_back( b );
359 }
360 // Original line has no attachments, just move the unselected end
361 else if( !foundAttachment )
362 {
363 if( line->HasFlag( STARTPOINT ) )
364 line->MoveEnd( splitDelta );
365 else
366 line->MoveStart( splitDelta );
367 }
368 }
369}
370
371
372int SCH_MOVE_TOOL::Main( const TOOL_EVENT& aEvent )
373{
375
376 if( SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() ) )
377 {
378 bool isSlice = false;
379
380 if( m_isDrag )
381 isSlice = aEvent.Parameter<bool>();
382
383 wxCHECK( aEvent.SynchronousState(), 0 );
384 aEvent.SynchronousState()->store( STS_RUNNING );
385
386 if( doMoveSelection( aEvent, commit, isSlice ) )
387 aEvent.SynchronousState()->store( STS_FINISHED );
388 else
389 aEvent.SynchronousState()->store( STS_CANCELLED );
390 }
391 else
392 {
393 SCH_COMMIT localCommit( m_toolMgr );
394
395 if( doMoveSelection( aEvent, &localCommit, false ) )
396 localCommit.Push( m_isDrag ? _( "Drag" ) : _( "Move" ) );
397 else
398 localCommit.Revert();
399 }
400
401 return 0;
402}
403
404
405bool SCH_MOVE_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, SCH_COMMIT* aCommit, bool aIsSlice )
406{
407 EESCHEMA_SETTINGS* cfg = nullptr;
408
409 try
410 {
412 }
413 catch( const std::runtime_error& e )
414 {
415 wxCHECK_MSG( false, false, e.what() );
416 }
417
420 bool wasDragging = m_moveInProgress && m_isDrag;
421
422 m_anchorPos.reset();
423
424 if( m_moveInProgress )
425 {
426 if( m_isDrag != wasDragging )
427 {
429
430 if( sel && !sel->IsNew() )
431 {
432 // Reset the selected items so we can start again with the current m_isDrag
433 // state.
434 aCommit->Revert();
435
438 m_moveInProgress = false;
439 controls->SetAutoPan( false );
440
441 // And give it a kick so it doesn't have to wait for the first mouse movement
442 // to refresh.
444 }
445 }
446 else
447 {
448 // The tool hotkey is interpreted as a click when already dragging/moving
450 }
451
452 return false;
453 }
454
455 if( m_inMoveTool ) // Must come after m_moveInProgress checks above...
456 return false;
457
459
460 EE_SELECTION& userSelection = m_selectionTool->GetSelection();
461
462 // If a single pin is selected, promote the move selection to its parent symbol
463 if( userSelection.GetSize() == 1 )
464 {
465 EDA_ITEM* selItem = userSelection.Front();
466
467 if( selItem->Type() == SCH_PIN_T )
468 {
469 EDA_ITEM* parent = selItem->GetParent();
470
471 if( parent->Type() == SCH_SYMBOL_T )
472 {
474 m_selectionTool->AddItemToSel( parent );
475 }
476 }
477 }
478
479 // Be sure that there is at least one item that we can move. If there's no selection try
480 // looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection).
482 true );
483 bool unselect = selection.IsHover();
484
485 // Keep an original copy of the starting points for cleanup after the move
486 std::vector<DANGLING_END_ITEM> internalPoints;
487
488 Activate();
489
490 // Must be done after Activate() so that it gets set into the correct context
491 controls->ShowCursor( true );
492
493 m_frame->PushTool( aEvent );
494
495 if( selection.Empty() )
496 {
497 // Note that it's important to go through push/pop even when the selection is empty.
498 // This keeps other tools from having to special-case an empty move.
499 m_frame->PopTool( aEvent );
500 return false;
501 }
502
503 bool restore_state = false;
504 TOOL_EVENT copy = aEvent;
505 TOOL_EVENT* evt = &copy;
506 VECTOR2I prevPos;
508
509 m_cursor = controls->GetCursorPosition();
510
511 // Main loop: keep receiving events
512 do
513 {
514 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
515 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
516 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
517
519 || evt->IsAction( &EE_ACTIONS::move )
520 || evt->IsAction( &EE_ACTIONS::drag )
521 || evt->IsMotion()
522 || evt->IsDrag( BUT_LEFT )
524 {
525 if( !m_moveInProgress ) // Prepare to start moving/dragging
526 {
527 SCH_ITEM* sch_item = (SCH_ITEM*) selection.Front();
528 bool placingNewItems = sch_item && sch_item->IsNew();
529
530 //------------------------------------------------------------------------
531 // Setup a drag or a move
532 //
533 m_dragAdditions.clear();
534 m_specialCaseLabels.clear();
536 internalPoints.clear();
538
539 for( SCH_ITEM* it : m_frame->GetScreen()->Items() )
540 {
541 it->ClearFlags( SELECTED_BY_DRAG );
542
543 if( !it->IsSelected() )
544 it->ClearFlags( STARTPOINT | ENDPOINT );
545 }
546
547 // Drag of split items start over top of their other segment so
548 // we want to skip grabbing the segments we split from
549 if( m_isDrag && !aIsSlice )
550 {
551 EDA_ITEMS connectedDragItems;
552
553 // Add connections to the selection for a drag.
554 // Do all non-labels/entries first so we don't add junctions to drag
555 // when the line will eventually be drag selected.
556 std::vector<SCH_ITEM*> stageTwo;
557
558 for( EDA_ITEM* edaItem : selection )
559 {
560 SCH_ITEM* item = static_cast<SCH_ITEM*>( edaItem );
561 std::vector<VECTOR2I> connections;
562
563 switch( item->Type() )
564 {
565 case SCH_LABEL_T:
566 case SCH_HIER_LABEL_T:
569 stageTwo.emplace_back(item);
570 break;
571
572 case SCH_LINE_T:
573 static_cast<SCH_LINE*>( item )->GetSelectedPoints( connections );
574 break;
575 default:
576 connections = item->GetConnectionPoints();
577 }
578
579 for( const VECTOR2I& point : connections )
580 getConnectedDragItems( aCommit, item, point, connectedDragItems );
581 }
582
583 // Go back and get all label connections now that we can test for drag-selected
584 // lines the labels might be on
585 for( SCH_ITEM* item : stageTwo )
586 {
587 for( const VECTOR2I& point : item->GetConnectionPoints() )
588 getConnectedDragItems( aCommit, item, point, connectedDragItems );
589 }
590
591 for( EDA_ITEM* item : connectedDragItems )
592 {
593 m_dragAdditions.push_back( item->m_Uuid );
595 }
596
597 // Pre-cache all connections of our selected objects so we can keep track of
598 // what they were originally connected to as we drag them around
599 for( EDA_ITEM* edaItem : selection )
600 {
601 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( edaItem );
602
603 if( schItem->Type() == SCH_LINE_T )
604 {
605 SCH_LINE* line = static_cast<SCH_LINE*>( schItem );
606
607 //Also store the original angle of the line, is needed later to decide
608 //which segment to extend when they've become zero length
609 line->StoreAngle();
610
611 for( const VECTOR2I& point : line->GetConnectionPoints() )
612 getConnectedItems( line, point, m_lineConnectionCache[line] );
613 }
614 }
615 }
616 else
617 {
618 // Mark the edges of the block with dangling flags for a move.
619 for( EDA_ITEM* item : selection )
620 static_cast<SCH_ITEM*>( item )->GetEndPoints( internalPoints );
621
622 std::vector<DANGLING_END_ITEM> endPointsByType = internalPoints;
623 std::vector<DANGLING_END_ITEM> endPointsByPos = endPointsByType;
625 endPointsByPos );
626
627 for( EDA_ITEM* item : selection )
628 static_cast<SCH_ITEM*>( item )->UpdateDanglingState( endPointsByType,
629 endPointsByPos );
630 }
631
632
633 // Generic setup
634 snapLayer = grid.GetSelectionGrid( selection );
635
636 for( EDA_ITEM* item : selection )
637 {
638 if( item->IsNew() )
639 {
640 // Item was added to commit in a previous command
641 }
642 else if( item->GetParent() && item->GetParent()->IsSelected() )
643 {
644 // Item will be (or has been) added to commit by parent
645 }
646 else
647 {
648 aCommit->Modify( item, m_frame->GetScreen() );
649 }
650
651 item->SetFlags( IS_MOVING );
652
653 if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( item ) )
654 schItem->SetStoredPos( schItem->GetPosition() );
655 }
656
657 // Set up the starting position and move/drag offset
658 //
659 m_cursor = controls->GetCursorPosition();
660
661 if( evt->IsAction( &EE_ACTIONS::restartMove ) )
662 {
663 wxASSERT_MSG( m_anchorPos, "Should be already set from previous cmd" );
664 }
665 else if( placingNewItems )
666 {
667 m_anchorPos = selection.GetReferencePoint();
668 }
669
670 if( m_anchorPos )
671 {
672 VECTOR2I delta = m_cursor - (*m_anchorPos);
673 bool isPasted = false;
674
675 // Drag items to the current cursor position
676 for( EDA_ITEM* item : selection )
677 {
678 // Don't double move pins, fields, etc.
679 if( item->GetParent() && item->GetParent()->IsSelected() )
680 continue;
681
682 moveItem( item, delta );
683 updateItem( item, false );
684
685 isPasted |= ( item->GetFlags() & IS_PASTED ) != 0;
686 item->ClearFlags( IS_PASTED );
687 }
688
689 // The first time pasted items are moved we need to store the position of the
690 // cursor so that rotate while moving works as expected (instead of around the
691 // original anchor point
692 if( isPasted )
693 selection.SetReferencePoint( m_cursor );
694
696 }
697 // For some items, moving the cursor to anchor is not good (for instance large
698 // hierarchical sheets or symbols can have the anchor outside the view)
699 else if( selection.Size() == 1 && !sch_item->IsMovableFromAnchorPoint() )
700 {
703 }
704 else
705 {
707 {
708 // User wants to warp the mouse
709 m_cursor = grid.BestDragOrigin( m_cursor, snapLayer, selection );
710 selection.SetReferencePoint( m_cursor );
711 }
712 else
713 {
714 // User does not want to warp the mouse
716 }
717 }
718
719 controls->SetCursorPosition( m_cursor, false );
720
721 prevPos = m_cursor;
722 controls->SetAutoPan( true );
723 m_moveInProgress = true;
724 }
725
726 //------------------------------------------------------------------------
727 // Follow the mouse
728 //
729 m_cursor = grid.BestSnapAnchor( controls->GetCursorPosition( false ),
730 snapLayer, selection );
731
732 VECTOR2I delta( m_cursor - prevPos );
734
735 // We need to check if the movement will change the net offset direction on the
736 // X an Y axes. This is because we remerge added bend lines in realtime, and we
737 // also account for the direction of the move when adding bend lines. So, if the
738 // move direction changes, we need to split it into a move that gets us back to
739 // zero, then the rest of the move.
740 std::vector<VECTOR2I> splitMoves;
741
743 {
744 splitMoves.emplace_back( VECTOR2I( -1 * m_moveOffset.x, 0 ) );
745 splitMoves.emplace_back( VECTOR2I( delta.x + m_moveOffset.x, 0 ) );
746 }
747 else
748 {
749 splitMoves.emplace_back( VECTOR2I( delta.x, 0 ) );
750 }
751
753 {
754 splitMoves.emplace_back( VECTOR2I( 0, -1 * m_moveOffset.y ) );
755 splitMoves.emplace_back( VECTOR2I( 0, delta.y + m_moveOffset.y ) );
756 }
757 else
758 {
759 splitMoves.emplace_back( VECTOR2I( 0, delta.y ) );
760 }
761
762
764 prevPos = m_cursor;
765
766 // Used for tracking how far off a drag end should have its 90 degree elbow added
767 int xBendCount = 1;
768 int yBendCount = 1;
769
770 // Split the move into X and Y moves so we can correctly drag orthogonal lines
771 for( const VECTOR2I& splitDelta : splitMoves )
772 {
773 // Skip non-moves
774 if( splitDelta == VECTOR2I( 0, 0 ) )
775 continue;
776
777 for( EDA_ITEM* item : selection.GetItemsSortedByTypeAndXY( ( delta.x >= 0 ),
778 ( delta.y >= 0 ) ) )
779 {
780 // Don't double move pins, fields, etc.
781 if( item->GetParent() && item->GetParent()->IsSelected() )
782 continue;
783
784 SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
785
786 // Only partially selected drag lines in orthogonal line mode need special
787 // handling
788 if( m_isDrag
789 && cfg->m_Drawing.line_mode != LINE_MODE::LINE_MODE_FREE
790 && line
791 && line->HasFlag( STARTPOINT ) != line->HasFlag( ENDPOINT ) )
792 {
793 orthoLineDrag( aCommit, line, splitDelta, xBendCount, yBendCount, grid );
794 }
795
796 // Move all other items normally, including the selected end of partially
797 // selected lines
798 moveItem( item, splitDelta );
799 updateItem( item, false );
800
801 // Update any lines connected to sheet pins to the sheet pin's location
802 // (which may not exactly follow the splitDelta as the pins are constrained
803 // along the sheet edges.
804 for( const auto& [pin, lineEnd] : m_specialCaseSheetPins )
805 {
806 if( lineEnd.second && lineEnd.first->HasFlag( STARTPOINT ) )
807 lineEnd.first->SetStartPoint( pin->GetPosition() );
808 else if( !lineEnd.second && lineEnd.first->HasFlag( ENDPOINT ) )
809 lineEnd.first->SetEndPoint( pin->GetPosition() );
810 }
811 }
812 }
813
814 if( selection.HasReferencePoint() )
815 selection.SetReferencePoint( selection.GetReferencePoint() + delta );
816
818 }
819
820 //------------------------------------------------------------------------
821 // Handle cancel
822 //
823 else if( evt->IsCancelInteractive() || evt->IsActivate() )
824 {
825 if( evt->IsCancelInteractive() )
827
828 if( m_moveInProgress )
829 {
830 if( evt->IsActivate() )
831 {
832 // Allowing other tools to activate during a move runs the risk of race
833 // conditions in which we try to spool up both event loops at once.
834
835 if( m_isDrag )
836 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel drag." ) );
837 else
838 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel move." ) );
839
840 evt->SetPassEvent( false );
841 continue;
842 }
843
844 evt->SetPassEvent( false );
845 restore_state = true;
846 }
847
849
850 break;
851 }
852 //------------------------------------------------------------------------
853 // Handle TOOL_ACTION special cases
854 //
855 else if( evt->Action() == TA_UNDO_REDO_PRE )
856 {
857 unselect = true;
858 break;
859 }
860 else if( evt->IsAction( &ACTIONS::doDelete ) )
861 {
862 evt->SetPassEvent();
863 // Exit on a delete; there will no longer be anything to drag.
864 break;
865 }
866 else if( evt->IsAction( &ACTIONS::duplicate ) )
867 {
868 wxBell();
869 }
870 else if( evt->IsAction( &EE_ACTIONS::rotateCW ) )
871 {
873 }
874 else if( evt->IsAction( &EE_ACTIONS::rotateCCW ) )
875 {
877 }
878 else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
879 {
882 {
883 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
884 int unit = *evt->GetCommandId() - ID_POPUP_SCH_SELECT_UNIT;
885
886 if( symbol )
887 {
888 m_frame->SelectUnit( symbol, unit );
890 }
891 }
892 else if( *evt->GetCommandId() >= ID_POPUP_SCH_SELECT_BASE
894 {
895 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
896 int bodyStyle = ( *evt->GetCommandId() - ID_POPUP_SCH_SELECT_BASE ) + 1;
897
898 if( symbol && symbol->GetBodyStyle() != bodyStyle )
899 {
900 m_frame->FlipBodyStyle( symbol );
902 }
903 }
904 }
905 else if( evt->IsAction( &EE_ACTIONS::highlightNet )
907 {
908 // These don't make any sense during a move. Eat them.
909 }
910 //------------------------------------------------------------------------
911 // Handle context menu
912 //
913 else if( evt->IsClick( BUT_RIGHT ) )
914 {
915 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
916 }
917 //------------------------------------------------------------------------
918 // Handle drop
919 //
920 else if( evt->IsMouseUp( BUT_LEFT )
921 || evt->IsClick( BUT_LEFT )
922 || evt->IsDblClick( BUT_LEFT ) )
923 {
924 break; // Finish
925 }
926 else
927 {
928 evt->SetPassEvent();
929 }
930
931 controls->SetAutoPan( m_moveInProgress );
932
933 } while( ( evt = Wait() ) ); //Should be assignment not equality test
934
935 // Create a selection of original selection, drag selected/changed items, and new
936 // bend lines for later before we clear them in the aCommit. We'll need these
937 // to check for new junctions needed, etc.
938 EE_SELECTION selectionCopy( selection );
939
940 for( SCH_LINE* line : m_newDragLines )
941 selectionCopy.Add( line );
942
943 for( SCH_LINE* line : m_changedDragLines )
944 selectionCopy.Add( line );
945
946 // Save whatever new bend lines and changed lines survived the drag
947 for( SCH_LINE* newLine : m_newDragLines )
948 {
949 newLine->ClearEditFlags();
950 aCommit->Added( newLine, m_frame->GetScreen() );
951 }
952
953 // These lines have been changed, but aren't selected. We need
954 // to manually clear these edit flags or they'll stick around.
955 for( SCH_LINE* oldLine : m_changedDragLines )
956 oldLine->ClearEditFlags();
957
958 m_newDragLines.clear();
959 m_changedDragLines.clear();
960
961 controls->ForceCursorPosition( false );
962 controls->ShowCursor( false );
963 controls->SetAutoPan( false );
964
965 m_moveOffset = { 0, 0 };
966 m_anchorPos.reset();
967
968 if( restore_state )
969 {
971 }
972 else
973 {
974 // One last update after exiting loop (for slower stuff, such as updating SCREEN's RTree).
975 for( EDA_ITEM* item : selection )
976 {
977 updateItem( item, true );
978
979 if( SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( item ) )
980 sch_item->SetConnectivityDirty( true );
981 }
982
983 if( selection.GetSize() == 1 && selection.Front()->IsNew() )
984 m_frame->SaveCopyForRepeatItem( static_cast<SCH_ITEM*>( selection.Front() ) );
985
987
988 // If we move items away from a junction, we _may_ want to add a junction there
989 // to denote the state.
990 for( const DANGLING_END_ITEM& it : internalPoints )
991 {
992 if( m_frame->GetScreen()->IsExplicitJunctionNeeded( it.GetPosition()) )
993 m_frame->AddJunction( aCommit, m_frame->GetScreen(), it.GetPosition() );
994 }
995
997 lwbTool->TrimOverLappingWires( aCommit, &selectionCopy );
998 lwbTool->AddJunctionsIfNeeded( aCommit, &selectionCopy );
999
1000 // This needs to run prior to `RecalculateConnections` because we need to identify
1001 // the lines that are newly dangling
1002 if( m_isDrag && !aIsSlice )
1003 trimDanglingLines( aCommit );
1004
1005 // Auto-rotate any moved labels
1006 for( EDA_ITEM* item : selection )
1007 m_frame->AutoRotateItem( m_frame->GetScreen(), static_cast<SCH_ITEM*>( item ) );
1008
1009 m_frame->SchematicCleanUp( aCommit );
1010 }
1011
1012 for( EDA_ITEM* item : m_frame->GetScreen()->Items() )
1013 item->ClearEditFlags();
1014
1015 // ensure any selected item not in screen main list (for instance symbol fields)
1016 // has its edit flags cleared
1017 for( EDA_ITEM* item : selectionCopy )
1018 item->ClearEditFlags();
1019
1020 if( unselect )
1022 else
1023 m_selectionTool->RebuildSelection(); // Schematic cleanup might have merged lines, etc.
1024
1025 m_dragAdditions.clear();
1026 m_lineConnectionCache.clear();
1027 m_moveInProgress = false;
1028 m_frame->PopTool( aEvent );
1029
1030 return !restore_state;
1031}
1032
1033
1035{
1036 // Need a local cleanup first to ensure we remove unneeded junctions
1037 m_frame->SchematicCleanUp( aCommit, m_frame->GetScreen() );
1038
1039 std::set<SCH_ITEM*> danglers;
1040
1041 std::function<void( SCH_ITEM* )> changeHandler =
1042 [&]( SCH_ITEM* aChangedItem ) -> void
1043 {
1044 m_toolMgr->GetView()->Update( aChangedItem, KIGFX::REPAINT );
1045
1046 // Delete newly dangling lines:
1047 // Find split segments (one segment is new, the other is changed) that
1048 // we aren't dragging and don't have selected
1049 if( aChangedItem->HasFlag( IS_BROKEN) && aChangedItem->IsDangling()
1050 && !aChangedItem->IsSelected() )
1051 {
1052 danglers.insert( aChangedItem );
1053 }
1054 };
1055
1056 m_frame->GetScreen()->TestDanglingEnds( nullptr, &changeHandler );
1057
1058 for( SCH_ITEM* line : danglers )
1059 {
1060 line->SetFlags( STRUCT_DELETED );
1061 aCommit->Removed( line, m_frame->GetScreen() );
1062
1063 updateItem( line, false );
1065 }
1066}
1067
1068
1069void SCH_MOVE_TOOL::getConnectedItems( SCH_ITEM* aOriginalItem, const VECTOR2I& aPoint,
1070 EDA_ITEMS& aList )
1071{
1072 EE_RTREE& items = m_frame->GetScreen()->Items();
1073 EE_RTREE::EE_TYPE itemsOverlapping = items.Overlapping( aOriginalItem->GetBoundingBox() );
1074 SCH_ITEM* foundJunction = nullptr;
1075 SCH_ITEM* foundSymbol = nullptr;
1076
1077 // If you're connected to a junction, you're only connected to the junction.
1078 //
1079 // But, if you're connected to a junction on a pin, you're only connected to the pin. This
1080 // is because junctions and pins have different logic for how bend lines are generated and
1081 // we need to prioritize the pin version in some cases.
1082 for( SCH_ITEM* item : itemsOverlapping )
1083 {
1084 if( item != aOriginalItem && item->IsConnected( aPoint ) )
1085 {
1086 if( item->Type() == SCH_JUNCTION_T )
1087 foundJunction = item;
1088 else if( item->Type() == SCH_SYMBOL_T )
1089 foundSymbol = item;
1090 }
1091 }
1092
1093 if( foundSymbol && foundJunction )
1094 {
1095 aList.push_back( foundSymbol );
1096 return;
1097 }
1098
1099 if( foundJunction )
1100 {
1101 aList.push_back( foundJunction );
1102 return;
1103 }
1104
1105
1106 for( SCH_ITEM* test : itemsOverlapping )
1107 {
1108 if( test == aOriginalItem || !test->CanConnect( aOriginalItem ) )
1109 continue;
1110
1111 switch( test->Type() )
1112 {
1113 case SCH_LINE_T:
1114 {
1115 SCH_LINE* line = static_cast<SCH_LINE*>( test );
1116
1117 // When getting lines for the connection cache, it's important that we only add
1118 // items at the unselected end, since that is the only end that is handled specially.
1119 // Fully selected lines, and the selected end of a partially selected line, are moved
1120 // around normally and don't care about their connections.
1121 if( ( line->HasFlag( STARTPOINT ) && aPoint == line->GetStartPoint() )
1122 || ( line->HasFlag( ENDPOINT ) && aPoint == line->GetEndPoint() ) )
1123 {
1124 continue;
1125 }
1126
1127 if( test->IsConnected( aPoint ) )
1128 aList.push_back( test );
1129
1130 // Labels can connect to a wire (or bus) anywhere along the length
1131 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( aOriginalItem ) )
1132 {
1133 if( static_cast<SCH_LINE*>( test )->HitTest( label->GetPosition(), 1 ) )
1134 aList.push_back( test );
1135 }
1136
1137 break;
1138 }
1139
1140 case SCH_SHEET_T:
1141 if( aOriginalItem->Type() == SCH_LINE_T )
1142 {
1143 SCH_LINE* line = static_cast<SCH_LINE*>( aOriginalItem );
1144
1145 for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( test )->GetPins() )
1146 {
1147 if( pin->IsConnected( aPoint ) )
1148 {
1149 if( pin->IsSelected() )
1150 m_specialCaseSheetPins[pin] = { line,
1151 line->GetStartPoint() == aPoint };
1152
1153 aList.push_back( pin );
1154 }
1155 }
1156 }
1157
1158 break;
1159
1160 case SCH_SYMBOL_T:
1161 case SCH_JUNCTION_T:
1162 case SCH_NO_CONNECT_T:
1163 if( test->IsConnected( aPoint ) )
1164 aList.push_back( test );
1165
1166 break;
1167
1168 case SCH_LABEL_T:
1169 case SCH_GLOBAL_LABEL_T:
1170 case SCH_HIER_LABEL_T:
1172 // Labels can connect to a wire (or bus) anywhere along the length
1173 if( aOriginalItem->Type() == SCH_LINE_T && test->CanConnect( aOriginalItem ) )
1174 {
1175 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( test );
1176 SCH_LINE* line = static_cast<SCH_LINE*>( aOriginalItem );
1177
1178 if( line->HitTest( label->GetPosition(), 1 ) )
1179 aList.push_back( label );
1180 }
1181
1182 break;
1183
1186 if( aOriginalItem->Type() == SCH_LINE_T && test->CanConnect( aOriginalItem ) )
1187 {
1188 SCH_TEXT* label = static_cast<SCH_TEXT*>( test );
1189 SCH_LINE* line = static_cast<SCH_LINE*>( aOriginalItem );
1190
1191 if( line->HitTest( aPoint, 1 ) )
1192 aList.push_back( label );
1193 }
1194
1195 break;
1196
1197 default:
1198 break;
1199 }
1200 }
1201}
1202
1203
1205 const VECTOR2I& aPoint, EDA_ITEMS& aList )
1206{
1207 EE_RTREE& items = m_frame->GetScreen()->Items();
1208 EE_RTREE::EE_TYPE itemsOverlappingRTree = items.Overlapping( aSelectedItem->GetBoundingBox() );
1209 std::vector<SCH_ITEM*> itemsConnectable;
1210 bool ptHasUnselectedJunction = false;
1211
1212 auto makeNewWire =
1213 [this]( SCH_COMMIT* commit, SCH_ITEM* fixed, SCH_ITEM* selected, const VECTOR2I& start,
1214 const VECTOR2I& end )
1215 {
1216 SCH_LINE* newWire;
1217
1218 // Add a new newWire between the fixed item and the selected item so the selected
1219 // item can be dragged.
1220 if( fixed->GetLayer() == LAYER_BUS_JUNCTION || fixed->GetLayer() == LAYER_BUS
1221 || selected->GetLayer() == LAYER_BUS )
1222 {
1223 newWire = new SCH_LINE( start, LAYER_BUS );
1224 }
1225 else
1226 {
1227 newWire = new SCH_LINE( start, LAYER_WIRE );
1228 }
1229
1230 newWire->SetFlags( IS_NEW );
1231 newWire->SetConnectivityDirty( true );
1232
1233 if( dynamic_cast<const SCH_LINE*>( selected ) )
1234 newWire->SetLastResolvedState( selected );
1235 else if( dynamic_cast<const SCH_LINE*>( fixed ) )
1236 newWire->SetLastResolvedState( fixed );
1237
1238 newWire->SetEndPoint( end );
1239 m_frame->AddToScreen( newWire, m_frame->GetScreen() );
1240 commit->Added( newWire, m_frame->GetScreen() );
1241
1242 return newWire;
1243 };
1244
1245 auto makeNewJunction =
1246 [this]( SCH_COMMIT* commit, SCH_LINE* line, const VECTOR2I& pt )
1247 {
1248 SCH_JUNCTION* junction = new SCH_JUNCTION( pt );
1249 junction->SetFlags( IS_NEW );
1250 junction->SetConnectivityDirty( true );
1251 junction->SetLastResolvedState( line );
1252
1253 if( line->IsBus() )
1254 junction->SetLayer( LAYER_BUS_JUNCTION );
1255
1256 m_frame->AddToScreen( junction, m_frame->GetScreen() );
1257 commit->Added( junction, m_frame->GetScreen() );
1258
1259 return junction;
1260 };
1261
1262 for( SCH_ITEM* item : itemsOverlappingRTree )
1263 {
1264 if( item->Type() == SCH_SHEET_T )
1265 {
1266 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
1267
1268 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
1269 {
1270 if( !pin->IsSelected() && pin->GetPosition() == aSelectedItem->GetPosition()
1271 && pin->CanConnect( aSelectedItem ) )
1272 {
1273 itemsConnectable.push_back( pin );
1274 }
1275 }
1276
1277 continue;
1278 }
1279
1280 // Skip ourselves, skip already selected items (but not lines, they need both ends tested)
1281 // and skip unconnectable items
1282 if( item == aSelectedItem || ( item->Type() != SCH_LINE_T && item->IsSelected() )
1283 || !item->CanConnect( aSelectedItem ) )
1284 {
1285 continue;
1286 }
1287
1288 itemsConnectable.push_back( item );
1289 }
1290
1291 for( SCH_ITEM* item : itemsConnectable )
1292 {
1293 if( item->Type() == SCH_JUNCTION_T && item->IsConnected( aPoint ) && !item->IsSelected() )
1294 {
1295 ptHasUnselectedJunction = true;
1296 break;
1297 }
1298 }
1299
1300 SCH_LINE* newWire = nullptr;
1301
1302 for( SCH_ITEM* test : itemsConnectable )
1303 {
1304 KICAD_T testType = test->Type();
1305
1306 switch( testType )
1307 {
1308 case SCH_LINE_T:
1309 {
1310 // Select the connected end of wires/bus connections that don't have an unselected
1311 // junction isolating them from the drag
1312 if( ptHasUnselectedJunction )
1313 break;
1314
1315 SCH_LINE* line = static_cast<SCH_LINE*>( test );
1316
1317 if( line->GetStartPoint() == aPoint )
1318 {
1319 // It's possible to manually select one end of a line and get a drag
1320 // connected other end, so we set the flag and then early exit the loop
1321 // later if the other drag items like labels attached to the line have
1322 // already been grabbed during the partial selection process.
1323 line->SetFlags( STARTPOINT );
1324
1325 if( line->HasFlag( SELECTED ) || line->HasFlag( SELECTED_BY_DRAG ) )
1326 {
1327 continue;
1328 }
1329 else
1330 {
1331 line->SetFlags( SELECTED_BY_DRAG );
1332 aList.push_back( line );
1333 }
1334 }
1335 else if( line->GetEndPoint() == aPoint )
1336 {
1337 line->SetFlags( ENDPOINT );
1338
1339 if( line->HasFlag( SELECTED ) || line->HasFlag( SELECTED_BY_DRAG ) )
1340 {
1341 continue;
1342 }
1343 else
1344 {
1345 line->SetFlags( SELECTED_BY_DRAG );
1346 aList.push_back( line );
1347 }
1348 }
1349 else
1350 {
1351 switch( aSelectedItem->Type() )
1352 {
1353 // These items can connect anywhere along a line
1356 case SCH_LABEL_T:
1357 case SCH_HIER_LABEL_T:
1358 case SCH_GLOBAL_LABEL_T:
1360 // Only add a line if this line is unselected; if the label and line are both
1361 // selected they'll move together
1362 if( line->HitTest( aPoint, 1 ) && !line->HasFlag( SELECTED )
1363 && !line->HasFlag( SELECTED_BY_DRAG ) )
1364 {
1365 newWire = makeNewWire( aCommit, line, aSelectedItem, aPoint, aPoint );
1366 newWire->SetFlags( SELECTED_BY_DRAG | STARTPOINT );
1367 newWire->StoreAngle( ( line->Angle() + ANGLE_90 ).Normalize() );
1368 aList.push_back( newWire );
1369
1370 if( aPoint != line->GetStartPoint() && aPoint != line->GetEndPoint() )
1371 {
1372 // Split line in half
1373 if( !line->IsNew() )
1374 aCommit->Modify( line, m_frame->GetScreen() );
1375
1376 VECTOR2I oldEnd = line->GetEndPoint();
1377 line->SetEndPoint( aPoint );
1378
1379 makeNewWire( aCommit, line, line, aPoint, oldEnd );
1380 makeNewJunction( aCommit, line, aPoint );
1381 }
1382 else
1383 {
1384 m_lineConnectionCache[ newWire ] = { line };
1385 m_lineConnectionCache[ line ] = { newWire };
1386 }
1387 }
1388 break;
1389
1390 default:
1391 break;
1392 }
1393
1394 break;
1395 }
1396
1397 // Since only one end is going to move, the movement vector of any labels attached to
1398 // it is scaled by the proportion of the line length the label is from the moving end.
1399 for( SCH_ITEM* item : items.Overlapping( line->GetBoundingBox() ) )
1400 {
1401 SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( item );
1402
1403 if( !label || label->IsSelected() )
1404 continue; // These will be moved on their own because they're selected
1405
1406 if( label->HasFlag( SELECTED_BY_DRAG ) )
1407 continue;
1408
1409 if( label->CanConnect( line ) && line->HitTest( label->GetPosition(), 1 ) )
1410 {
1411 label->SetFlags( SELECTED_BY_DRAG );
1412 aList.push_back( label );
1413
1415 info.attachedLine = line;
1416 info.originalLabelPos = label->GetPosition();
1417 m_specialCaseLabels[label] = info;
1418 }
1419 }
1420
1421 break;
1422 }
1423
1424 case SCH_SHEET_T:
1425 for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( test )->GetPins() )
1426 {
1427 if( pin->IsConnected( aPoint ) )
1428 {
1429 if( pin->IsSelected() && aSelectedItem->Type() == SCH_LINE_T )
1430 {
1431 SCH_LINE* line = static_cast<SCH_LINE*>( aSelectedItem );
1432 m_specialCaseSheetPins[ pin ] = { line, line->GetStartPoint() == aPoint };
1433 }
1434 else if( !newWire )
1435 {
1436 // Add a new wire between the sheetpin and the selected item so the
1437 // selected item can be dragged.
1438 newWire = makeNewWire( aCommit, pin, aSelectedItem, aPoint, aPoint );
1439 newWire->SetFlags( SELECTED_BY_DRAG | STARTPOINT );
1440 aList.push_back( newWire );
1441 }
1442 }
1443 }
1444
1445 break;
1446
1447 case SCH_SYMBOL_T:
1448 case SCH_JUNCTION_T:
1449 if( test->IsConnected( aPoint ) && !newWire )
1450 {
1451 // Add a new wire between the symbol or junction and the selected item so
1452 // the selected item can be dragged.
1453 newWire = makeNewWire( aCommit, test, aSelectedItem, aPoint, aPoint );
1454 newWire->SetFlags( SELECTED_BY_DRAG | STARTPOINT );
1455 aList.push_back( newWire );
1456 }
1457
1458 break;
1459
1460 case SCH_NO_CONNECT_T:
1461 // Select no-connects that are connected to items being moved.
1462 if( !test->HasFlag( SELECTED_BY_DRAG ) && test->IsConnected( aPoint ) )
1463 {
1464 aList.push_back( test );
1465 test->SetFlags( SELECTED_BY_DRAG );
1466 }
1467
1468 break;
1469
1470 case SCH_LABEL_T:
1471 case SCH_GLOBAL_LABEL_T:
1472 case SCH_HIER_LABEL_T:
1474 case SCH_SHEET_PIN_T:
1475 // Performance optimization:
1476 if( test->HasFlag( SELECTED_BY_DRAG ) )
1477 break;
1478
1479 // Select labels that are connected to a wire (or bus) being moved.
1480 if( aSelectedItem->Type() == SCH_LINE_T && test->CanConnect( aSelectedItem ) )
1481 {
1482 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( test );
1483 SCH_LINE* line = static_cast<SCH_LINE*>( aSelectedItem );
1484
1485 bool oneEndFixed = !line->HasFlag( STARTPOINT ) || !line->HasFlag( ENDPOINT );
1486
1487 if( line->HitTest( label->GetTextPos(), 1 ) )
1488 {
1489 if( ( !line->HasFlag( STARTPOINT )
1490 && label->GetPosition() == line->GetStartPoint() )
1491 || ( !line->HasFlag( ENDPOINT )
1492 && label->GetPosition() == line->GetEndPoint() ) )
1493 {
1494 //If we have a line selected at only one end, don't grab labels
1495 //connected directly to the unselected endpoint
1496 break;
1497 }
1498 else
1499 {
1500 label->SetFlags( SELECTED_BY_DRAG );
1501 aList.push_back( label );
1502
1503 if( oneEndFixed )
1504 {
1506 info.attachedLine = line;
1507 info.originalLabelPos = label->GetPosition();
1508 m_specialCaseLabels[label] = info;
1509 }
1510 }
1511 }
1512 }
1513 else if( test->IsConnected( aPoint ) && !newWire )
1514 {
1515 // Add a new wire between the label and the selected item so the selected item
1516 // can be dragged.
1517 newWire = makeNewWire( aCommit, test, aSelectedItem, aPoint, aPoint );
1518 newWire->SetFlags( SELECTED_BY_DRAG | STARTPOINT );
1519 aList.push_back( newWire );
1520 }
1521
1522 break;
1523
1526 // Performance optimization:
1527 if( test->HasFlag( SELECTED_BY_DRAG ) )
1528 break;
1529
1530 // Select bus entries that are connected to a bus being moved.
1531 if( aSelectedItem->Type() == SCH_LINE_T && test->CanConnect( aSelectedItem ) )
1532 {
1533 SCH_LINE* line = static_cast<SCH_LINE*>( aSelectedItem );
1534
1535 if( ( !line->HasFlag( STARTPOINT ) && test->IsConnected( line->GetStartPoint() ) )
1536 || ( !line->HasFlag( ENDPOINT ) && test->IsConnected( line->GetEndPoint() ) ) )
1537 {
1538 // If we have a line selected at only one end, don't grab bus entries
1539 // connected directly to the unselected endpoint
1540 continue;
1541 }
1542
1543 for( VECTOR2I& point : test->GetConnectionPoints() )
1544 {
1545 if( line->HitTest( point, 1 ) )
1546 {
1547 test->SetFlags( SELECTED_BY_DRAG );
1548 aList.push_back( test );
1549
1550 // A bus entry needs its wire & label as well
1551 std::vector<VECTOR2I> ends = test->GetConnectionPoints();
1552 VECTOR2I otherEnd;
1553
1554 if( ends[0] == point )
1555 otherEnd = ends[1];
1556 else
1557 otherEnd = ends[0];
1558
1559 getConnectedDragItems( aCommit, test, otherEnd, aList );
1560
1561 // No need to test the other end of the bus entry
1562 break;
1563 }
1564 }
1565 }
1566
1567 break;
1568
1569 default:
1570 break;
1571 }
1572 }
1573}
1574
1575
1576void SCH_MOVE_TOOL::moveItem( EDA_ITEM* aItem, const VECTOR2I& aDelta )
1577{
1578 switch( aItem->Type() )
1579 {
1580 case SCH_LINE_T:
1581 {
1582 SCH_LINE* line = static_cast<SCH_LINE*>( aItem );
1583
1584 if( aItem->HasFlag( STARTPOINT ) || !m_isDrag )
1585 line->MoveStart( aDelta );
1586
1587 if( aItem->HasFlag( ENDPOINT ) || !m_isDrag )
1588 line->MoveEnd( aDelta );
1589
1590 break;
1591 }
1592
1593 case SCH_PIN_T:
1594 case SCH_FIELD_T:
1595 {
1596 SCH_ITEM* parent = (SCH_ITEM*) aItem->GetParent();
1597 VECTOR2I delta( aDelta );
1598
1599 if( parent && parent->Type() == SCH_SYMBOL_T )
1600 {
1601 SCH_SYMBOL* symbol = (SCH_SYMBOL*) aItem->GetParent();
1602 TRANSFORM transform = symbol->GetTransform().InverseTransform();
1603
1604 delta = transform.TransformCoordinate( delta );
1605 }
1606
1607 static_cast<SCH_ITEM*>( aItem )->Move( delta );
1608
1609 // If we're moving a field with respect to its parent then it's no longer auto-placed
1610 if( aItem->Type() == SCH_FIELD_T && parent && !parent->IsSelected() )
1611 parent->ClearFieldsAutoplaced();
1612
1613 break;
1614 }
1615
1616 case SCH_SHEET_PIN_T:
1617 {
1618 SCH_SHEET_PIN* pin = (SCH_SHEET_PIN*) aItem;
1619
1620 pin->SetStoredPos( pin->GetStoredPos() + aDelta );
1621 pin->ConstrainOnEdge( pin->GetStoredPos(), true );
1622 break;
1623 }
1624
1625 case SCH_LABEL_T:
1627 case SCH_GLOBAL_LABEL_T:
1628 case SCH_HIER_LABEL_T:
1629 {
1630 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( aItem );
1631
1632 if( m_specialCaseLabels.count( label ) )
1633 {
1635 SEG currentLine( info.attachedLine->GetStartPoint(), info.attachedLine->GetEndPoint() );
1636 label->SetPosition( currentLine.NearestPoint( info.originalLabelPos ) );
1637 }
1638 else
1639 {
1640 label->Move( aDelta );
1641 }
1642
1643 break;
1644 }
1645
1646 default:
1647 static_cast<SCH_ITEM*>( aItem )->Move( aDelta );
1648 break;
1649 }
1650
1651 aItem->SetFlags( IS_MOVING );
1652}
1653
1654
1656{
1659 GRID_HELPER_GRIDS selectionGrid = grid.GetSelectionGrid( selection );
1660 SCH_COMMIT commit( m_toolMgr );
1661
1662 auto doMoveItem =
1663 [&]( EDA_ITEM* item, const VECTOR2I& delta )
1664 {
1665 commit.Modify( item, m_frame->GetScreen() );
1666
1667 // Ensure only one end is moved when calling moveItem
1668 // i.e. we are in drag mode
1669 bool tmp_isDrag = m_isDrag;
1670 m_isDrag = true;
1671 moveItem( item, delta );
1672 m_isDrag = tmp_isDrag;
1673
1674 item->ClearFlags( IS_MOVING );
1675 updateItem( item, true );
1676 };
1677
1678 for( SCH_ITEM* it : m_frame->GetScreen()->Items() )
1679 {
1680 if( !it->IsSelected() )
1681 it->ClearFlags( STARTPOINT | ENDPOINT );
1682
1683 if( !selection.IsHover() && it->IsSelected() )
1684 it->SetFlags( STARTPOINT | ENDPOINT );
1685
1686 it->SetStoredPos( it->GetPosition() );
1687
1688 if( it->Type() == SCH_SHEET_T )
1689 {
1690 for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( it )->GetPins() )
1691 pin->SetStoredPos( pin->GetPosition() );
1692 }
1693 }
1694
1695 for( EDA_ITEM* item : selection )
1696 {
1697 if( item->Type() == SCH_LINE_T )
1698 {
1699 SCH_LINE* line = static_cast<SCH_LINE*>( item );
1700 std::vector<int> flags{ STARTPOINT, ENDPOINT };
1701 std::vector<VECTOR2I> pts{ line->GetStartPoint(), line->GetEndPoint() };
1702
1703 for( int ii = 0; ii < 2; ++ii )
1704 {
1705 EDA_ITEMS drag_items{ item };
1706 line->ClearFlags();
1707 line->SetFlags( SELECTED );
1708 line->SetFlags( flags[ii] );
1709 getConnectedDragItems( &commit, line, pts[ii], drag_items );
1710 std::set<EDA_ITEM*> unique_items( drag_items.begin(), drag_items.end() );
1711
1712 VECTOR2I delta = grid.AlignGrid( pts[ii], selectionGrid ) - pts[ii];
1713
1714 if( delta != VECTOR2I( 0, 0 ) )
1715 {
1716 for( EDA_ITEM* dragItem : unique_items )
1717 {
1718 if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
1719 continue;
1720
1721 doMoveItem( dragItem, delta );
1722 }
1723 }
1724 }
1725 }
1726 else if( item->Type() == SCH_FIELD_T || item->Type() == SCH_TEXT_T )
1727 {
1728 VECTOR2I delta = grid.AlignGrid( item->GetPosition(), selectionGrid ) - item->GetPosition();
1729
1730 if( delta != VECTOR2I( 0, 0 ) )
1731 doMoveItem( item, delta );
1732 }
1733 else if( item->Type() == SCH_SHEET_T )
1734 {
1735 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
1736 VECTOR2I topLeft = sheet->GetPosition();
1737 VECTOR2I bottomRight = topLeft + sheet->GetSize();
1738 VECTOR2I tl_delta = grid.AlignGrid( topLeft, selectionGrid ) - topLeft;
1739 VECTOR2I br_delta = grid.AlignGrid( bottomRight, selectionGrid ) - bottomRight;
1740
1741 if( tl_delta != VECTOR2I( 0, 0 ) || br_delta != VECTOR2I( 0, 0 ) )
1742 {
1743 doMoveItem( sheet, tl_delta );
1744
1745 VECTOR2I newSize = (VECTOR2I) sheet->GetSize() - tl_delta + br_delta;
1746 sheet->SetSize( VECTOR2I( newSize.x, newSize.y ) );
1747 updateItem( sheet, true );
1748 }
1749
1750 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
1751 {
1752 VECTOR2I newPos;
1753
1754 if( pin->GetSide() == SHEET_SIDE::TOP || pin->GetSide() == SHEET_SIDE::LEFT )
1755 newPos = pin->GetPosition() + tl_delta;
1756 else
1757 newPos = pin->GetPosition() + br_delta;
1758
1759 VECTOR2I delta = grid.AlignGrid( newPos - pin->GetPosition(), selectionGrid );
1760
1761 if( delta != VECTOR2I( 0, 0 ) )
1762 {
1763 EDA_ITEMS drag_items;
1764 getConnectedDragItems( &commit, pin, pin->GetConnectionPoints()[0],
1765 drag_items );
1766
1767 doMoveItem( pin, delta );
1768
1769 for( EDA_ITEM* dragItem : drag_items )
1770 {
1771 if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
1772 continue;
1773
1774 doMoveItem( dragItem, delta );
1775 }
1776 }
1777 }
1778 }
1779 else
1780 {
1781 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
1782 std::vector<VECTOR2I> connections = schItem->GetConnectionPoints();
1783 EDA_ITEMS drag_items;
1784
1785 for( const VECTOR2I& point : connections )
1786 getConnectedDragItems( &commit, schItem, point, drag_items );
1787
1788 std::map<VECTOR2I, int> shifts;
1789 VECTOR2I most_common( 0, 0 );
1790 int max_count = 0;
1791
1792 for( const VECTOR2I& conn : connections )
1793 {
1794 VECTOR2I gridpt = grid.AlignGrid( conn, selectionGrid ) - conn;
1795
1796 shifts[gridpt]++;
1797
1798 if( shifts[gridpt] > max_count )
1799 {
1800 most_common = gridpt;
1801 max_count = shifts[most_common];
1802 }
1803 }
1804
1805 if( most_common != VECTOR2I( 0, 0 ) )
1806 {
1807 doMoveItem( item, most_common );
1808
1809 for( EDA_ITEM* dragItem : drag_items )
1810 {
1811 if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
1812 continue;
1813
1814 doMoveItem( dragItem, most_common );
1815 }
1816 }
1817 }
1818 }
1819
1821 lwbTool->TrimOverLappingWires( &commit, &selection );
1822 lwbTool->AddJunctionsIfNeeded( &commit, &selection );
1823
1825
1826 m_frame->SchematicCleanUp( &commit );
1827 commit.Push( _( "Align Items to Grid" ) );
1828 return 0;
1829}
1830
1831
1833{
1834 // Remove new bend lines added during the drag
1835 for( SCH_LINE* newLine : m_newDragLines )
1836 {
1837 m_frame->RemoveFromScreen( newLine, m_frame->GetScreen() );
1838 delete newLine;
1839 }
1840
1841 m_newDragLines.clear();
1842}
1843
1844
1846{
1847 Go( &SCH_MOVE_TOOL::Main, EE_ACTIONS::move.MakeEvent() );
1848 Go( &SCH_MOVE_TOOL::Main, EE_ACTIONS::drag.MakeEvent() );
1850}
1851
1852
static TOOL_ACTION duplicate
Definition: actions.h:77
static TOOL_ACTION doDelete
Definition: actions.h:78
static TOOL_ACTION cursorClick
Definition: actions.h:169
static TOOL_ACTION refreshPreview
Definition: actions.h:149
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:105
COMMIT & Added(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition: commit.h:86
COMMIT & Removed(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Modify a given item in the model.
Definition: commit.h:98
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
static void sort_dangling_end_items(std::vector< DANGLING_END_ITEM > &aItemListByType, std::vector< DANGLING_END_ITEM > &aItemListByPos)
Both contain the same information.
Definition: sch_item.cpp:615
Helper class used to store the state of schematic items that can be connected to other schematic item...
Definition: sch_item.h:95
bool IsParallelTo(EDA_ANGLE aAngle) const
Definition: eda_angle.h:148
void ShowInfoBarMsg(const wxString &aMsg, bool aShowCloseButton=false)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an info icon on the left of...
WX_INFOBAR * GetInfoBar()
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:243
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition: eda_item.cpp:77
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:127
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: eda_item.h:129
bool IsSelected() const
Definition: eda_item.h:110
EDA_ITEM * GetParent() const
Definition: eda_item.h:103
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition: eda_item.h:131
bool IsNew() const
Definition: eda_item.h:107
const VECTOR2I & GetTextPos() const
Definition: eda_text.h:253
static TOOL_ACTION alignToGrid
Definition: ee_actions.h:126
static TOOL_ACTION highlightNet
Definition: ee_actions.h:308
static TOOL_ACTION move
Definition: ee_actions.h:127
static TOOL_ACTION clearSelection
Clears the current selection.
Definition: ee_actions.h:56
static TOOL_ACTION drag
Definition: ee_actions.h:128
static TOOL_ACTION rotateCCW
Definition: ee_actions.h:131
static TOOL_ACTION restartMove
Definition: ee_actions.h:260
static TOOL_ACTION rotateCW
Definition: ee_actions.h:130
static TOOL_ACTION selectOnPCB
Definition: ee_actions.h:261
static const std::vector< KICAD_T > MovableItems
Definition: ee_collectors.h:43
Implements an R-tree for fast spatial and type indexing of schematic items.
Definition: sch_rtree.h:40
EE_TYPE Overlapping(const BOX2I &aRect) const
Definition: sch_rtree.h:243
EE_SELECTION & RequestSelection(const std::vector< KICAD_T > &aScanTypes={ SCH_LOCATE_ANY_T }, bool aPromoteCellSelections=false)
Return either an existing selection (filtered), or the selection at the current cursor position if th...
int ClearSelection(const TOOL_EVENT &aEvent)
Select all visible items in sheet.
void RebuildSelection()
Rebuild the selection from the EDA_ITEMs' selection flags.
EE_SELECTION & GetSelection()
A foundation class for a tool operating on a schematic or symbol.
Definition: ee_tool_base.h:48
void updateItem(EDA_ITEM *aItem, bool aUpdateRTree) const
Similar to getView()->Update(), but handles items that are redrawn by their parents and updating the ...
Definition: ee_tool_base.h:109
EE_SELECTION_TOOL * m_selectionTool
Definition: ee_tool_base.h:200
bool Init() override
Init() is called once upon a registration of the tool.
Definition: ee_tool_base.h:64
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:302
An interface for classes handling user events controlling the view behavior such as zooming,...
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
virtual void ShowCursor(bool aEnabled)
Enable or disables display of cursor.
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
virtual void SetCursorPosition(const VECTOR2D &aPosition, bool aWarpView=true, bool aTriggeredByArrows=false, long aArrowCommand=0)=0
Move cursor to the requested position expressed in world coordinates.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition: view.cpp:1687
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition: pgm_base.h:142
void AddToScreen(EDA_ITEM *aItem, SCH_SCREEN *aScreen=nullptr)
Add an item to the screen (and view) aScreen is the screen the item is located on,...
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
void RemoveFromScreen(EDA_ITEM *aItem, SCH_SCREEN *aScreen)
Remove an item from the screen (and view) aScreen is the screen the item is located on,...
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Revert the commit by restoring the modified items state.
Definition: sch_commit.cpp:432
virtual void Revert() override
Definition: sch_commit.cpp:510
Schematic editor (Eeschema) main window.
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
void SchematicCleanUp(SCH_COMMIT *aCommit, SCH_SCREEN *aScreen=nullptr)
Perform routine schematic cleaning including breaking wire and buses and deleting identical objects s...
void FlipBodyStyle(SCH_SYMBOL *aSymbol)
Definition: picksymbol.cpp:176
void SelectUnit(SCH_SYMBOL *aSymbol, int aUnit)
Definition: picksymbol.cpp:95
void AutoRotateItem(SCH_SCREEN *aScreen, SCH_ITEM *aItem)
Automatically set the rotation of an item (if the item supports it)
SCH_JUNCTION * AddJunction(SCH_COMMIT *aCommit, SCH_SCREEN *aScreen, const VECTOR2I &aPos)
void SaveCopyForRepeatItem(const SCH_ITEM *aItem)
Clone aItem and owns that clone in this container.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:166
virtual bool CanConnect(const SCH_ITEM *aItem) const
Definition: sch_item.h:444
int GetBodyStyle() const
Definition: sch_item.h:232
void ClearFieldsAutoplaced()
Definition: sch_item.h:551
void SetLayer(SCH_LAYER_ID aLayer)
Definition: sch_item.h:282
void SetConnectivityDirty(bool aDirty=true)
Definition: sch_item.h:512
bool IsConnected(const VECTOR2I &aPoint) const
Test the item to see if it is connected to aPoint.
Definition: sch_item.cpp:212
virtual bool IsMovableFromAnchorPoint() const
Definition: sch_item.h:246
virtual std::vector< VECTOR2I > GetConnectionPoints() const
Add all the connection points for this item to aPoints.
Definition: sch_item.h:464
void SetLastResolvedState(const SCH_ITEM *aItem) override
Definition: sch_junction.h:57
void Move(const VECTOR2I &aMoveVector) override
Move the item by aMoveVector to a new position.
Definition: sch_label.cpp:378
void SetPosition(const VECTOR2I &aPosition) override
Definition: sch_label.cpp:371
bool CanConnect(const SCH_ITEM *aItem) const override
Definition: sch_label.h:149
Tool responsible for drawing/placing items (symbols, wires, buses, labels, etc.)
int AddJunctionsIfNeeded(SCH_COMMIT *aCommit, EE_SELECTION *aSelection)
Handle the addition of junctions to a selection of objects.
int TrimOverLappingWires(SCH_COMMIT *aCommit, EE_SELECTION *aSelection)
Logic to remove wires when overlapping correct items.
static bool IsDrawingLineWireOrBus(const SELECTION &aSelection)
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:41
bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const override
Test if aPosition is inside or on the boundary of this item.
Definition: sch_line.cpp:810
void StoreAngle()
Saves the current line angle.
Definition: sch_line.h:112
std::vector< VECTOR2I > GetConnectionPoints() const override
Add all the connection points for this item to aPoints.
Definition: sch_line.cpp:710
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: sch_line.cpp:227
EDA_ANGLE Angle() const
Gets the angle between the start and end lines.
Definition: sch_line.h:103
VECTOR2I GetEndPoint() const
Definition: sch_line.h:141
VECTOR2I GetStartPoint() const
Definition: sch_line.h:136
void MoveEnd(const VECTOR2I &aMoveVector)
Definition: sch_line.cpp:168
void SetLastResolvedState(const SCH_ITEM *aItem) override
Definition: sch_line.h:152
void MoveStart(const VECTOR2I &aMoveVector)
Definition: sch_line.cpp:162
double GetLength() const
Definition: sch_line.cpp:243
void SetEndPoint(const VECTOR2I &aPosition)
Definition: sch_line.h:142
bool Init() override
Init() is called once upon a registration of the tool.
VECTOR2I m_cursor
void trimDanglingLines(SCH_COMMIT *aCommit)
bool m_isDrag
Items (such as wires) which were added to the selection for a drag.
Definition: sch_move_tool.h:95
void orthoLineDrag(SCH_COMMIT *aCommit, SCH_LINE *line, const VECTOR2I &splitDelta, int &xBendCount, int &yBendCount, const EE_GRID_HELPER &grid)
Clears the new drag lines and removes them from the screen.
std::unordered_set< SCH_LINE * > m_newDragLines
Lines changed by drag algorithm that weren't selected.
bool doMoveSelection(const TOOL_EVENT &aEvent, SCH_COMMIT *aCommit, bool aIsSlice)
OPT_VECTOR2I m_anchorPos
int Main(const TOOL_EVENT &aEvent)
Run an interactive move of the selected items, or the item under the cursor.
bool m_inMoveTool
< Re-entrancy guard
Definition: sch_move_tool.h:91
std::vector< KIID > m_dragAdditions
Cache of the line's original connections before dragging started.
Definition: sch_move_tool.h:98
void moveItem(EDA_ITEM *aItem, const VECTOR2I &aDelta)
Find additional items for a drag operation.
std::unordered_set< SCH_LINE * > m_changedDragLines
void setTransitions() override
Cleanup dangling lines left after a drag.
void getConnectedItems(SCH_ITEM *aOriginalItem, const VECTOR2I &aPoint, EDA_ITEMS &aList)
std::map< SCH_LINE *, EDA_ITEMS > m_lineConnectionCache
Lines added at bend points dynamically during the move.
bool m_moveInProgress
Definition: sch_move_tool.h:94
void getConnectedDragItems(SCH_COMMIT *aCommit, SCH_ITEM *fixed, const VECTOR2I &selected, EDA_ITEMS &aList)
VECTOR2I m_moveOffset
Last cursor position (needed for getModificationPoint() to avoid changes of edit reference point).
std::map< SCH_LABEL_BASE *, SPECIAL_CASE_LABEL_INFO > m_specialCaseLabels
int AlignToGrid(const TOOL_EVENT &aEvent)
Align selected elements to the grid.
void clearNewDragLines()
Set up handlers for various events.
std::map< SCH_SHEET_PIN *, std::pair< SCH_LINE *, bool > > m_specialCaseSheetPins
void TestDanglingEnds(const SCH_SHEET_PATH *aPath=nullptr, std::function< void(SCH_ITEM *)> *aChangedHandler=nullptr) const
Test all of the connectable objects in the schematic for unused connection points.
EE_RTREE & Items()
Gets the full RTree, usually for iterating.
Definition: sch_screen.h:108
bool IsExplicitJunctionNeeded(const VECTOR2I &aPosition) const
Indicates that a junction dot is necessary at the given location, and does not yet exist.
Definition: sch_screen.cpp:496
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet_pin.h:66
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:57
void SetSize(const VECTOR2I &aSize)
Definition: sch_sheet.h:113
VECTOR2I GetSize() const
Definition: sch_sheet.h:112
VECTOR2I GetPosition() const override
Definition: sch_sheet.h:400
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition: sch_sheet.h:181
Schematic symbol object.
Definition: sch_symbol.h:104
TRANSFORM & GetTransform()
Definition: sch_symbol.h:288
VECTOR2I GetPosition() const override
Definition: sch_text.h:141
Definition: seg.h:42
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition: seg.cpp:327
static SELECTION_CONDITION OnlyTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if the selected items are only of given types.
int AddItemToSel(const TOOL_EVENT &aEvent)
int RemoveItemsFromSel(const TOOL_EVENT &aEvent)
virtual void Add(EDA_ITEM *aItem)
Definition: selection.cpp:42
VECTOR2I GetReferencePoint() const
Definition: selection.cpp:171
bool IsHover() const
Definition: selection.h:84
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition: selection.h:100
EDA_ITEM * Front() const
Definition: selection.h:172
void SetReferencePoint(const VECTOR2I &aP)
Definition: selection.cpp:180
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:110
std::vector< EDA_ITEM * > GetItemsSortedByTypeAndXY(bool leftBeforeRight=true, bool topBeforeBottom=true) const
Returns a copy of this selection of items sorted by their X then Y position.
Definition: selection.cpp:223
bool HasReferencePoint() const
Definition: selection.h:211
T * GetAppSettings()
Returns a handle to the a given settings by type If the settings have already been loaded,...
virtual void PopTool(const TOOL_EVENT &aEvent)
Pops a tool from the stack.
bool GetMoveWarpsCursor() const
Indicate that a move operation should warp the mouse pointer to the origin of the move object.
Definition: tools_holder.h:150
virtual void PushTool(const TOOL_EVENT &aEvent)
NB: the definition of "tool" is different at the user level.
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:218
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
Generic, UI-independent tool event.
Definition: tool_event.h:167
bool DisableGridSnapping() const
Definition: tool_event.h:363
bool IsCancelInteractive() const
Indicate the event should restart/end an ongoing interactive tool's event loop (eg esc key,...
Definition: tool_event.cpp:221
TOOL_ACTIONS Action() const
These give a tool a method of informing the TOOL_MANAGER that a particular event should be passed on ...
Definition: tool_event.h:246
bool IsActivate() const
Definition: tool_event.h:337
COMMIT * Commit() const
Returns information about difference between current mouse cursor position and the place where draggi...
Definition: tool_event.h:275
bool IsClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:209
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:307
int Modifier(int aMask=MD_MODIFIER_MASK) const
Definition: tool_event.h:358
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
Definition: tool_event.cpp:82
T Parameter() const
Return a parameter assigned to the event.
Definition: tool_event.h:460
bool IsDblClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:215
std::atomic< SYNCRONOUS_TOOL_STATE > * SynchronousState() const
Definition: tool_event.h:272
std::optional< int > GetCommandId() const
Definition: tool_event.h:520
void SetPassEvent(bool aPass=true)
Returns if it this event has a valid position (true for mouse events and context-menu or hotkey-based...
Definition: tool_event.h:252
bool IsMouseUp(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:317
bool IsMotion() const
Definition: tool_event.h:322
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
TOOL_MENU & GetToolMenu()
std::unique_ptr< TOOL_MENU > m_menu
The functions below are not yet implemented - their interface may change.
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Suspend execution of the tool until an event specified in aEventList arrives.
void Activate()
Run the tool.
void PostEvent(const TOOL_EVENT &aEvent)
Put an event to the event queue to be processed at the end of event processing cycle.
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Definition: tool_manager.h:150
bool PostAction(const std::string &aActionName, T aParam)
Run the specified action after the current action (coroutine) ends.
Definition: tool_manager.h:235
bool RunSynchronousAction(const TOOL_ACTION &aAction, COMMIT *aCommit, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Definition: tool_manager.h:197
KIGFX::VIEW * GetView() const
Definition: tool_manager.h:391
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:44
for transforming drawing coordinates for a wxDC device context.
Definition: transform.h:46
TRANSFORM InverseTransform() const
Calculate the Inverse mirror/rotation transform.
Definition: transform.cpp:59
VECTOR2I TransformCoordinate(const VECTOR2I &aPoint) const
Calculate a new coordinate according to the mirror/rotation transform.
Definition: transform.cpp:44
void Dismiss() override
Dismisses the infobar and updates the containing layout and AUI manager (if one is provided).
Definition: wx_infobar.cpp:190
#define _(s)
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:403
std::vector< EDA_ITEM * > EDA_ITEMS
Define list of drawing items for screens.
Definition: eda_item.h:536
#define IS_PASTED
Modifier on IS_NEW which indicates it came from clipboard.
#define IS_CHANGED
Item was edited, and modified.
#define IS_NEW
New item, just created.
#define SELECTED
Item was manually selected by the user.
#define SELECTED_BY_DRAG
Item was algorithmically selected as a dragged item.
#define IS_BROKEN
Is a segment just broken by BreakSegment.
#define STRUCT_DELETED
flag indication structures to be erased
#define ENDPOINT
ends. (Used to support dragging.)
#define IS_MOVING
Item being moved.
#define STARTPOINT
When a line is selected, these flags indicate which.
@ ID_POPUP_SCH_SELECT_UNIT
Definition: eeschema_id.h:82
@ ID_POPUP_SCH_SELECT_BASE
Definition: eeschema_id.h:88
@ ID_POPUP_SCH_SELECT_ALT
Definition: eeschema_id.h:89
@ ID_POPUP_SCH_SELECT_UNIT_END
Definition: eeschema_id.h:86
GRID_HELPER_GRIDS
Definition: grid_helper.h:42
@ GRID_CURRENT
Definition: grid_helper.h:44
@ LAYER_WIRE
Definition: layer_ids.h:356
@ LAYER_BUS
Definition: layer_ids.h:357
@ LAYER_BUS_JUNCTION
Definition: layer_ids.h:400
@ REPAINT
Item needs to be redrawn.
Definition: view_item.h:57
bool signbit(T v)
Integral version of std::signbit that works all compilers.
Definition: kicad_algo.h:198
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: pgm_base.cpp:1060
see class PGM_BASE
#define QUIET_MODE
The EE_TYPE struct provides a type-specific auto-range iterator to the RTree.
Definition: sch_rtree.h:192
constexpr int delta
@ TA_CHOICE_MENU_CHOICE
Definition: tool_event.h:97
@ TA_UNDO_REDO_PRE
Definition: tool_event.h:105
@ MD_SHIFT
Definition: tool_event.h:142
@ STS_CANCELLED
Definition: tool_event.h:160
@ STS_FINISHED
Definition: tool_event.h:159
@ STS_RUNNING
Definition: tool_event.h:158
@ BUT_LEFT
Definition: tool_event.h:131
@ BUT_RIGHT
Definition: tool_event.h:132
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:78
@ SCH_LINE_T
Definition: typeinfo.h:163
@ SCH_NO_CONNECT_T
Definition: typeinfo.h:160
@ SCH_SYMBOL_T
Definition: typeinfo.h:172
@ SCH_FIELD_T
Definition: typeinfo.h:150
@ SCH_DIRECTIVE_LABEL_T
Definition: typeinfo.h:171
@ SCH_LABEL_T
Definition: typeinfo.h:167
@ SCH_SHEET_T
Definition: typeinfo.h:174
@ SCH_MARKER_T
Definition: typeinfo.h:158
@ SCH_HIER_LABEL_T
Definition: typeinfo.h:169
@ SCH_BUS_BUS_ENTRY_T
Definition: typeinfo.h:162
@ SCH_SHEET_PIN_T
Definition: typeinfo.h:173
@ SCH_TEXT_T
Definition: typeinfo.h:151
@ SCH_BUS_WIRE_ENTRY_T
Definition: typeinfo.h:161
@ SCH_GLOBAL_LABEL_T
Definition: typeinfo.h:168
@ SCH_JUNCTION_T
Definition: typeinfo.h:159
@ SCH_PIN_T
Definition: typeinfo.h:153
constexpr int sign(T val)
Definition: util.h:159
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691