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