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