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