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 updateItem( line, true );
326
327 // Update our cache of the connected items. First, attach our drag labels to the line
328 // left behind.
329 for( EDA_ITEM* candidate : m_lineConnectionCache[line] )
330 {
331 SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( candidate );
332
333 if( label && m_specialCaseLabels.count( label ) )
334 m_specialCaseLabels[label].attachedLine = a;
335 }
336
337 // We just broke off of the existing items, so replace all of them with our new end
338 // connection.
340 m_lineConnectionCache[b].emplace_back( a );
341 m_lineConnectionCache[line].clear();
342 m_lineConnectionCache[line].emplace_back( b );
343 }
344 // Original line has no attachments, just move the unselected end
345 else if( !foundAttachment )
346 {
347 if( line->HasFlag( STARTPOINT ) )
348 line->MoveEnd( splitDelta );
349 else
350 line->MoveStart( splitDelta );
351 }
352 }
353}
354
355
356int SCH_MOVE_TOOL::Main( const TOOL_EVENT& aEvent )
357{
358 EESCHEMA_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<EESCHEMA_SETTINGS>();
361 bool wasDragging = m_moveInProgress && m_isDrag;
362
363 m_anchorPos.reset();
364
365 if( aEvent.IsAction( &EE_ACTIONS::move ) )
366 m_isDrag = false;
367 else if( aEvent.IsAction( &EE_ACTIONS::drag ) )
368 m_isDrag = true;
369 else if( aEvent.IsAction( &EE_ACTIONS::moveActivate ) )
371 else
372 return 0;
373
374 if( m_moveInProgress )
375 {
376 if( m_isDrag != wasDragging )
377 {
379
380 if( sel && !sel->IsNew() )
381 {
382 // Reset the selected items so we can start again with the current m_isDrag
383 // state.
387 m_moveInProgress = false;
388 controls->SetAutoPan( false );
389
390 // And give it a kick so it doesn't have to wait for the first mouse movement
391 // to refresh.
393 }
394 }
395 else
396 {
397 // The tool hotkey is interpreted as a click when already dragging/moving
399 }
400
401 return 0;
402 }
403
404 if( m_inMoveTool ) // Must come after m_moveInProgress checks above...
405 return 0;
406
408
409 // Be sure that there is at least one item that we can move. If there's no selection try
410 // looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection).
412 bool unselect = selection.IsHover();
413
414 // Keep an original copy of the starting points for cleanup after the move
415 std::vector<DANGLING_END_ITEM> internalPoints;
416
417 Activate();
418 // Must be done after Activate() so that it gets set into the correct context
419 controls->ShowCursor( true );
420
421 m_frame->PushTool( aEvent );
422
423 if( selection.Empty() )
424 {
425 // Note that it's important to go through push/pop even when the selection is empty.
426 // This keeps other tools from having to special-case an empty move.
427 m_frame->PopTool( aEvent );
428 return 0;
429 }
430
431 bool restore_state = false;
432 bool chain_commands = false;
433 TOOL_EVENT copy = aEvent;
434 TOOL_EVENT* evt = &copy;
435 VECTOR2I prevPos;
436 int snapLayer = UNDEFINED_LAYER;
437
438 m_cursor = controls->GetCursorPosition();
439
440 // Main loop: keep receiving events
441 do
442 {
444 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
445 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
446
449 || evt->IsAction( &EE_ACTIONS::move )
450 || evt->IsAction( &EE_ACTIONS::drag )
451 || evt->IsMotion()
452 || evt->IsDrag( BUT_LEFT )
454 {
455 if( !m_moveInProgress ) // Prepare to start moving/dragging
456 {
457 SCH_ITEM* sch_item = (SCH_ITEM*) selection.Front();
458 bool appendUndo = sch_item && sch_item->IsNew();
459 bool placingNewItems = sch_item && sch_item->IsNew();
460
461 //------------------------------------------------------------------------
462 // Setup a drag or a move
463 //
464 m_dragAdditions.clear();
465 m_specialCaseLabels.clear();
467 internalPoints.clear();
469
470 for( SCH_ITEM* it : m_frame->GetScreen()->Items() )
471 {
472 it->ClearFlags( SELECTED_BY_DRAG );
473
474 if( !it->IsSelected() )
475 it->ClearFlags( STARTPOINT | ENDPOINT );
476 }
477
478 if( m_isDrag )
479 {
480 EDA_ITEMS connectedDragItems;
481
482 // Add connections to the selection for a drag.
483 //
484 for( EDA_ITEM* edaItem : selection )
485 {
486 SCH_ITEM* item = static_cast<SCH_ITEM*>( edaItem );
487 std::vector<VECTOR2I> connections;
488
489 if( item->Type() == SCH_LINE_T )
490 static_cast<SCH_LINE*>( item )->GetSelectedPoints( connections );
491 else
492 connections = item->GetConnectionPoints();
493
494 for( VECTOR2I point : connections )
495 getConnectedDragItems( item, point, connectedDragItems, appendUndo );
496 }
497
498 for( EDA_ITEM* item : connectedDragItems )
499 {
500 m_dragAdditions.push_back( item->m_Uuid );
502 }
503
504 // Pre-cache all connections of our selected objects so we can keep track of
505 // what they were originally connected to as we drag them around
506 for( EDA_ITEM* edaItem : selection )
507 {
508 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( edaItem );
509
510 if( schItem->Type() == SCH_LINE_T )
511 {
512 SCH_LINE* line = static_cast<SCH_LINE*>( schItem );
513
514 //Also store the original angle of the line, is needed later to decide
515 //which segment to extend when they've become zero length
516 line->StoreAngle();
517
518 for( VECTOR2I point : line->GetConnectionPoints() )
519 getConnectedItems( line, point, m_lineConnectionCache[line] );
520 }
521 }
522 }
523 else
524 {
525 // Mark the edges of the block with dangling flags for a move.
526 for( EDA_ITEM* item : selection )
527 static_cast<SCH_ITEM*>( item )->GetEndPoints( internalPoints );
528
529 for( EDA_ITEM* item : selection )
530 static_cast<SCH_ITEM*>( item )->UpdateDanglingState( internalPoints );
531 }
532 // Generic setup
533 //
534 for( EDA_ITEM* item : selection )
535 {
536 if( static_cast<SCH_ITEM*>( item )->IsConnectable() )
537 {
538 if( snapLayer == LAYER_GRAPHICS )
539 snapLayer = LAYER_ANY;
540 else
541 snapLayer = LAYER_CONNECTABLE;
542 }
543 else
544 {
545 if( snapLayer == LAYER_CONNECTABLE )
546 snapLayer = LAYER_ANY;
547 else
548 snapLayer = LAYER_GRAPHICS;
549 }
550
551 if( item->IsNew() )
552 {
553 if( item->HasFlag( SELECTED_BY_DRAG ) && m_isDrag )
554 {
555 // Item was added in getConnectedDragItems
556 saveCopyInUndoList( (SCH_ITEM*) item, UNDO_REDO::NEWITEM, appendUndo );
557 appendUndo = true;
558 }
559 else
560 {
561 // Item was added in a previous command (and saved to undo by
562 // that command)
563 }
564 }
565 else if( item->GetParent() && item->GetParent()->IsSelected() )
566 {
567 // Item will be (or has been) saved to undo by parent
568 }
569 else
570 {
571 saveCopyInUndoList( (SCH_ITEM*) item, UNDO_REDO::CHANGED, appendUndo );
572 appendUndo = true;
573 }
574
575 SCH_ITEM* schItem = (SCH_ITEM*) item;
576 schItem->SetStoredPos( schItem->GetPosition() );
577 }
578
579 // Set up the starting position and move/drag offset
580 //
581 m_cursor = controls->GetCursorPosition();
582
583 if( evt->IsAction( &EE_ACTIONS::restartMove ) )
584 {
585 wxASSERT_MSG( m_anchorPos, "Should be already set from previous cmd" );
586 }
587 else if( placingNewItems )
588 {
589 m_anchorPos = selection.GetReferencePoint();
590 }
591
592 if( m_anchorPos )
593 {
594 VECTOR2I delta = m_cursor - (*m_anchorPos);
595 bool isPasted = false;
596
597 // Drag items to the current cursor position
598 for( EDA_ITEM* item : selection )
599 {
600 // Don't double move pins, fields, etc.
601 if( item->GetParent() && item->GetParent()->IsSelected() )
602 continue;
603
604 moveItem( item, delta );
605 updateItem( item, false );
606
607 isPasted |= ( item->GetFlags() & IS_PASTED ) != 0;
608 item->ClearFlags( IS_PASTED );
609 }
610
611 // The first time pasted items are moved we need to store the position of the
612 // cursor so that rotate while moving works as expected (instead of around the
613 // original anchor point
614 if( isPasted )
615 selection.SetReferencePoint( m_cursor );
616
618 }
619 // For some items, moving the cursor to anchor is not good (for instance large
620 // hierarchical sheets or symbols can have the anchor outside the view)
621 else if( selection.Size() == 1 && !sch_item->IsMovableFromAnchorPoint() )
622 {
625 }
626 else
627 {
629 {
630 // User wants to warp the mouse
631 m_cursor = grid.BestDragOrigin( m_cursor, snapLayer, selection );
632 selection.SetReferencePoint( m_cursor );
633 }
634 else
635 {
636 // User does not want to warp the mouse
638 }
639 }
640
641 controls->SetCursorPosition( m_cursor, false );
643
644 prevPos = m_cursor;
645 controls->SetAutoPan( true );
646 m_moveInProgress = true;
647 }
648
649 //------------------------------------------------------------------------
650 // Follow the mouse
651 //
652 m_cursor = grid.BestSnapAnchor( controls->GetCursorPosition( false ),
653 snapLayer, selection );
654
655 VECTOR2I delta( m_cursor - prevPos );
657
658 // We need to check if the movement will change the net offset direction on the
659 // X an Y axes. This is because we remerge added bend lines in realtime, and we
660 // also account for the direction of the move when adding bend lines. So, if the
661 // move direction changes, we need to split it into a move that gets us back to
662 // zero, then the rest of the move.
663 std::vector<VECTOR2I> splitMoves;
664
666 {
667 splitMoves.emplace_back( VECTOR2I( -1 * m_moveOffset.x, 0 ) );
668 splitMoves.emplace_back( VECTOR2I( delta.x + m_moveOffset.x, 0 ) );
669 }
670 else
671 {
672 splitMoves.emplace_back( VECTOR2I( delta.x, 0 ) );
673 }
674
676 {
677 splitMoves.emplace_back( VECTOR2I( 0, -1 * m_moveOffset.y ) );
678 splitMoves.emplace_back( VECTOR2I( 0, delta.y + m_moveOffset.y ) );
679 }
680 else
681 {
682 splitMoves.emplace_back( VECTOR2I( 0, delta.y ) );
683 }
684
685
687 prevPos = m_cursor;
688
689 // Used for tracking how far off a drag end should have its 90 degree elbow added
690 int xBendCount = 1;
691 int yBendCount = 1;
692
693 // Split the move into X and Y moves so we can correctly drag orthogonal lines
694 for( VECTOR2I splitDelta : splitMoves )
695 {
696 // Skip non-moves
697 if( splitDelta == VECTOR2I( 0, 0 ) )
698 continue;
699
700 for( EDA_ITEM* item : selection.GetItemsSortedByTypeAndXY( ( delta.x >= 0 ),
701 ( delta.y >= 0 ) ) )
702 {
703 // Don't double move pins, fields, etc.
704 if( item->GetParent() && item->GetParent()->IsSelected() )
705 continue;
706
707 SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
708
709 // Only partially selected drag lines in orthogonal line mode need special
710 // handling
711 if( m_isDrag
713 && line
714 && line->HasFlag( STARTPOINT ) != line->HasFlag( ENDPOINT ) )
715 {
716 orthoLineDrag( line, splitDelta, xBendCount, yBendCount, grid );
717 }
718
719 // Move all other items normally, including the selected end of partially
720 // selected lines
721 moveItem( item, splitDelta );
722 updateItem( item, false );
723
724 // Update any lines connected to sheet pins to the sheet pin's location
725 // (which may not exactly follow the splitDelta as the pins are constrained
726 // along the sheet edges.
727 for( const auto& [pin, lineEnd] : m_specialCaseSheetPins )
728 {
729 if( lineEnd.second )
730 lineEnd.first->SetStartPoint( pin->GetPosition() );
731 else
732 lineEnd.first->SetEndPoint( pin->GetPosition() );
733 }
734 }
735 }
736
737 if( selection.HasReferencePoint() )
738 selection.SetReferencePoint( selection.GetReferencePoint() + delta );
739
741 }
742
743 //------------------------------------------------------------------------
744 // Handle cancel
745 //
746 else if( evt->IsCancelInteractive() || evt->IsActivate() )
747 {
748 if( evt->IsCancelInteractive() )
750
751 if( m_moveInProgress )
752 {
753 if( evt->IsActivate() )
754 {
755 // Allowing other tools to activate during a move runs the risk of race
756 // conditions in which we try to spool up both event loops at once.
757
758 if( m_isDrag )
759 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel drag." ) );
760 else
761 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel move." ) );
762
763 evt->SetPassEvent( false );
764 continue;
765 }
766
767 evt->SetPassEvent( false );
768 restore_state = true;
769 }
770
772
773 break;
774 }
775 //------------------------------------------------------------------------
776 // Handle TOOL_ACTION special cases
777 //
778 else if( evt->Action() == TA_UNDO_REDO_PRE )
779 {
780 unselect = true;
781 break;
782 }
783 else if( evt->IsAction( &ACTIONS::doDelete ) )
784 {
785 evt->SetPassEvent();
786 // Exit on a delete; there will no longer be anything to drag.
787 break;
788 }
789 else if( evt->IsAction( &ACTIONS::duplicate ) )
790 {
791 if( selection.Front()->IsNew() )
792 {
793 // This doesn't really make sense; we'll just end up dragging a stack of
794 // objects so we ignore the duplicate and just carry on.
795 continue;
796 }
797
798 // Move original back and exit. The duplicate will run in its own loop.
799 restore_state = true;
800 unselect = false;
801 chain_commands = true;
802 break;
803 }
804 else if( evt->IsAction( &EE_ACTIONS::rotateCW ) )
805 {
807 }
808 else if( evt->IsAction( &EE_ACTIONS::rotateCCW ) )
809 {
811 }
812 else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
813 {
816 {
817 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
818 int unit = *evt->GetCommandId() - ID_POPUP_SCH_SELECT_UNIT_CMP;
819
820 if( symbol )
821 {
822 m_frame->SelectUnit( symbol, unit );
824 }
825 }
826 }
827 else if( evt->IsAction( &EE_ACTIONS::highlightNet )
829 {
830 // These don't make any sense during a move. Eat them.
831 }
832 //------------------------------------------------------------------------
833 // Handle context menu
834 //
835 else if( evt->IsClick( BUT_RIGHT ) )
836 {
838 }
839 //------------------------------------------------------------------------
840 // Handle drop
841 //
842 else if( evt->IsMouseUp( BUT_LEFT )
843 || evt->IsClick( BUT_LEFT )
844 || evt->IsDblClick( BUT_LEFT ) )
845 {
846 break; // Finish
847 }
848 else
849 {
850 evt->SetPassEvent();
851 }
852
853 controls->SetAutoPan( m_moveInProgress );
854
855 } while( ( evt = Wait() ) ); //Should be assignment not equality test
856
857 // Create a selection of original selection, drag selected/changed items, and new
858 // bend lines for later before we clear them in the commit. We'll need these
859 // to check for new junctions needed, etc.
860 EE_SELECTION selectionCopy( selection );
861
862 for( SCH_LINE* line : m_newDragLines )
863 selectionCopy.Add( line );
864
865 for( SCH_LINE* line : m_changedDragLines )
866 selectionCopy.Add( line );
867
868 // Save whatever new bend lines and changed lines survived the drag
870
871 controls->ForceCursorPosition( false );
872 controls->ShowCursor( false );
873 controls->SetAutoPan( false );
874
875 if( !chain_commands )
876 m_moveOffset = { 0, 0 };
877
878 m_anchorPos.reset();
879
880 if( restore_state )
881 {
884 }
885 else
886 {
887 // One last update after exiting loop (for slower stuff, such as updating SCREEN's RTree).
888 for( EDA_ITEM* item : selection )
889 updateItem( item, true );
890
892
893 // If we move items away from a junction, we _may_ want to add a junction there
894 // to denote the state.
895 for( const DANGLING_END_ITEM& it : internalPoints )
896 {
897 if( m_frame->GetScreen()->IsExplicitJunctionNeeded( it.GetPosition()) )
898 m_frame->AddJunction( m_frame->GetScreen(), it.GetPosition(), true, false );
899 }
900
901 m_toolMgr->RunAction( EE_ACTIONS::trimOverlappingWires, true, &selectionCopy );
902 m_toolMgr->RunAction( EE_ACTIONS::addNeededJunctions, true, &selectionCopy );
903
904 // This needs to run prior to `RecalculateConnections` because we need to identify
905 // the lines that are newly dangling
906 if( m_isDrag )
908
910 m_frame->OnModify();
911 }
912
913 for( EDA_ITEM* item : selectionCopy )
914 item->ClearEditFlags();
915
916 if( unselect )
918 else
919 m_selectionTool->RebuildSelection(); // Schematic cleanup might have merged lines, etc.
920
921 m_dragAdditions.clear();
922 m_lineConnectionCache.clear();
923 m_moveInProgress = false;
924 m_frame->PopTool( aEvent );
925 return 0;
926}
927
928
930{
931 // Need a local cleanup first to ensure we remove unneeded junctions
933
934 std::set<SCH_ITEM*> danglers;
935
936 std::function<void( SCH_ITEM* )> changeHandler =
937 [&]( SCH_ITEM* aChangedItem ) -> void
938 {
939 m_toolMgr->GetView()->Update( aChangedItem, KIGFX::REPAINT );
940
941 // Delete newly dangling lines:
942 // Find split segments (one segment is new, the other is changed) that
943 // we aren't dragging and don't have selected
944 if( aChangedItem->IsDangling() && !aChangedItem->IsSelected()
945 && ( aChangedItem->IsNew() || !aChangedItem->IsDragging() )
946 && aChangedItem->IsType( { SCH_LINE_T } ) )
947 {
948 danglers.insert( aChangedItem );
949 }
950 };
951
952 m_frame->GetScreen()->TestDanglingEnds( nullptr, &changeHandler );
953
954 for( SCH_ITEM* line : danglers )
955 {
956 line->SetFlags( STRUCT_DELETED );
957 saveCopyInUndoList( line, UNDO_REDO::DELETED, true );
958
959 updateItem( line, false );
960
961 m_frame->RemoveFromScreen( line, m_frame->GetScreen() );
962 }
963}
964
965
966void SCH_MOVE_TOOL::getConnectedItems( SCH_ITEM* aOriginalItem, const VECTOR2I& aPoint,
967 EDA_ITEMS& aList )
968{
969 EE_RTREE& items = m_frame->GetScreen()->Items();
970 EE_RTREE::EE_TYPE itemsOverlapping = items.Overlapping( aOriginalItem->GetBoundingBox() );
971 SCH_ITEM* foundJunction = nullptr;
972 SCH_ITEM* foundSymbol = nullptr;
973
974 // If you're connected to a junction, you're only connected to the junction.
975 //
976 // But, if you're connected to a junction on a pin, you're only connected to the pin. This
977 // is because junctions and pins have different logic for how bend lines are generated and
978 // we need to prioritize the pin version in some cases.
979 for( SCH_ITEM* item : itemsOverlapping )
980 {
981 if( item != aOriginalItem && item->IsConnected( aPoint ) )
982 {
983 if( item->Type() == SCH_JUNCTION_T )
984 foundJunction = item;
985 else if( item->Type() == SCH_SYMBOL_T )
986 foundSymbol = item;
987 }
988 }
989
990 if( foundSymbol && foundJunction )
991 {
992 aList.push_back( foundSymbol );
993 return;
994 }
995
996 if( foundJunction )
997 {
998 aList.push_back( foundJunction );
999 return;
1000 }
1001
1002
1003 for( SCH_ITEM* test : itemsOverlapping )
1004 {
1005 if( test == aOriginalItem || !test->CanConnect( aOriginalItem ) )
1006 continue;
1007
1008 switch( test->Type() )
1009 {
1010 case SCH_LINE_T:
1011 {
1012 SCH_LINE* line = static_cast<SCH_LINE*>( test );
1013
1014 // When getting lines for the connection cache, it's important that we only add
1015 // items at the unselected end, since that is the only end that is handled specially.
1016 // Fully selected lines, and the selected end of a partially selected line, are moved
1017 // around normally and don't care about their connections.
1018 if( ( line->HasFlag( STARTPOINT ) && aPoint == line->GetStartPoint() )
1019 || ( line->HasFlag( ENDPOINT ) && aPoint == line->GetEndPoint() ) )
1020 {
1021 continue;
1022 }
1023
1024 if( test->IsConnected( aPoint ) )
1025 aList.push_back( test );
1026
1027 // Labels can connect to a wire (or bus) anywhere along the length
1028 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( aOriginalItem ) )
1029 {
1030 if( static_cast<SCH_LINE*>( test )->HitTest( label->GetPosition(), 1 ) )
1031 aList.push_back( test );
1032 }
1033
1034 break;
1035 }
1036
1037 case SCH_SHEET_T:
1038 if( aOriginalItem->Type() == SCH_LINE_T )
1039 {
1040 SCH_LINE* line = static_cast<SCH_LINE*>( aOriginalItem );
1041
1042 for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( test )->GetPins() )
1043 {
1044 if( pin->IsConnected( aPoint ) )
1045 m_specialCaseSheetPins[ pin ] = { line, line->GetStartPoint() == aPoint };
1046 }
1047 }
1048
1050
1051 case SCH_SYMBOL_T:
1052 case SCH_JUNCTION_T:
1053 case SCH_NO_CONNECT_T:
1054 if( test->IsConnected( aPoint ) )
1055 aList.push_back( test );
1056
1057 break;
1058
1059 case SCH_LABEL_T:
1060 case SCH_GLOBAL_LABEL_T:
1061 case SCH_HIER_LABEL_T:
1063 // Labels can connect to a wire (or bus) anywhere along the length
1064 if( aOriginalItem->Type() == SCH_LINE_T && test->CanConnect( aOriginalItem ) )
1065 {
1066 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( test );
1067 SCH_LINE* line = static_cast<SCH_LINE*>( aOriginalItem );
1068
1069 if( line->HitTest( label->GetPosition(), 1 ) )
1070 aList.push_back( label );
1071 }
1072
1073 break;
1074
1077 if( aOriginalItem->Type() == SCH_LINE_T && test->CanConnect( aOriginalItem ) )
1078 {
1079 SCH_TEXT* label = static_cast<SCH_TEXT*>( test );
1080 SCH_LINE* line = static_cast<SCH_LINE*>( aOriginalItem );
1081
1082 if( line->HitTest( aPoint, 1 ) )
1083 aList.push_back( label );
1084 }
1085
1086 break;
1087
1088 default:
1089 break;
1090 }
1091 }
1092}
1093
1094
1095void SCH_MOVE_TOOL::getConnectedDragItems( SCH_ITEM* aSelectedItem, const VECTOR2I& aPoint,
1096 EDA_ITEMS& aList, bool& aAppendUndo )
1097{
1098 EE_RTREE& items = m_frame->GetScreen()->Items();
1099 EE_RTREE::EE_TYPE itemsOverlappingRTree = items.Overlapping( aSelectedItem->GetBoundingBox() );
1100 std::vector<SCH_ITEM*> itemsConnectable;
1101 bool ptHasUnselectedJunction = false;
1102
1103 auto makeNewWire =
1104 [&]( SCH_ITEM* fixed, SCH_ITEM* selected, const VECTOR2I& start, const VECTOR2I& end )
1105 {
1106 SCH_LINE* newWire;
1107
1108 // Add a new newWire between the fixed item and the selected item so the selected
1109 // item can be dragged.
1110 if( fixed->GetLayer() == LAYER_BUS_JUNCTION || selected->GetLayer() == LAYER_BUS )
1111 newWire = new SCH_LINE( start, LAYER_BUS );
1112 else
1113 newWire = new SCH_LINE( start, LAYER_WIRE );
1114
1115 newWire->SetFlags( IS_NEW );
1116 newWire->SetConnectivityDirty( true );
1117 newWire->SetLastResolvedState( selected );
1118
1119 newWire->SetEndPoint( end );
1120 m_frame->AddToScreen( newWire, m_frame->GetScreen() );
1121
1122 return newWire;
1123 };
1124
1125 auto makeNewJunction =
1126 [&]( SCH_LINE* line, const VECTOR2I pt )
1127 {
1128 SCH_JUNCTION* junction = new SCH_JUNCTION( pt );
1129 junction->SetFlags( IS_NEW );
1130 junction->SetConnectivityDirty( true );
1131 junction->SetLastResolvedState( line );
1132
1133 m_frame->AddToScreen( junction, m_frame->GetScreen() );
1134
1135 return junction;
1136 };
1137
1138 for( SCH_ITEM* item : itemsOverlappingRTree )
1139 {
1140 // Skip ourselves, skip already selected items (but not lines, they need both ends tested)
1141 // and skip unconnectable items
1142 if( item == aSelectedItem || ( item->Type() != SCH_LINE_T && item->IsSelected() )
1143 || !item->CanConnect( aSelectedItem ) )
1144 {
1145 continue;
1146 }
1147
1148 itemsConnectable.push_back( item );
1149 }
1150
1151 for( SCH_ITEM* item : itemsConnectable )
1152 {
1153 if( item->Type() == SCH_JUNCTION_T && item->IsConnected( aPoint ) && !item->IsSelected() )
1154 {
1155 ptHasUnselectedJunction = true;
1156 break;
1157 }
1158 }
1159
1160 SCH_LINE* newWire = nullptr;
1161
1162 for( SCH_ITEM* test : itemsConnectable )
1163 {
1164 KICAD_T testType = test->Type();
1165
1166 switch( testType )
1167 {
1168 case SCH_LINE_T:
1169 {
1170 // Select the connected end of wires/bus connections that don't have an unselected
1171 // junction isolating them from the drag
1172 if( ptHasUnselectedJunction )
1173 break;
1174
1175 SCH_LINE* line = static_cast<SCH_LINE*>( test );
1176
1177 if( line->GetStartPoint() == aPoint )
1178 {
1179 // It's possible to manually select one end of a line and get a drag
1180 // connected other end, so we set the flag and then early exit the loop
1181 // later if the other drag items like labels attached to the line have
1182 // already been grabbed during the partial selection process.
1183 line->SetFlags( STARTPOINT );
1184
1185 if( line->HasFlag( SELECTED ) || line->HasFlag( SELECTED_BY_DRAG ) )
1186 continue;
1187 else
1188 {
1189 line->SetFlags( SELECTED_BY_DRAG );
1190 aList.push_back( line );
1191 }
1192 }
1193 else if( line->GetEndPoint() == aPoint )
1194 {
1195 line->SetFlags( ENDPOINT );
1196
1197 if( line->HasFlag( SELECTED ) || line->HasFlag( SELECTED_BY_DRAG ) )
1198 continue;
1199 else
1200 {
1201 line->SetFlags( SELECTED_BY_DRAG );
1202 aList.push_back( line );
1203 }
1204 }
1205 else
1206 {
1207 switch( aSelectedItem->Type() )
1208 {
1209 // These items can connect anywhere along a line
1212 case SCH_LABEL_T:
1213 case SCH_HIER_LABEL_T:
1214 case SCH_GLOBAL_LABEL_T:
1216 // Only add a line if this line is unselected; if the label and line are both
1217 // selected they'll move together
1218 if( line->HitTest( aPoint, 1 ) && !line->HasFlag( SELECTED ) )
1219 {
1220 newWire = makeNewWire( line, aSelectedItem, aPoint, aPoint );
1221 newWire->SetFlags( SELECTED_BY_DRAG | STARTPOINT );
1222 newWire->StoreAngle( ( line->Angle() + ANGLE_90 ).Normalize() );
1223 aList.push_back( newWire );
1224
1225 if( aPoint != line->GetStartPoint() && aPoint != line->GetEndPoint() )
1226 {
1227 // Split line in half
1228 if( !line->IsNew() )
1229 {
1230 saveCopyInUndoList( line, UNDO_REDO::CHANGED, aAppendUndo );
1231 aAppendUndo = true;
1232 }
1233
1234 VECTOR2I oldEnd = line->GetEndPoint();
1235 line->SetEndPoint( aPoint );
1236
1237 SCH_LINE* secondHalf = makeNewWire( line, line, aPoint, oldEnd );
1238 SCH_JUNCTION* junction = makeNewJunction( line, aPoint );
1239
1240 saveCopyInUndoList( secondHalf, UNDO_REDO::NEWITEM, aAppendUndo );
1241 aAppendUndo = true;
1242
1243 saveCopyInUndoList( junction, UNDO_REDO::NEWITEM, aAppendUndo );
1244 aAppendUndo = true;
1245
1246 m_frame->AddToScreen( secondHalf, m_frame->GetScreen() );
1247 m_frame->AddToScreen( junction, m_frame->GetScreen() );
1248 }
1249 else
1250 {
1251 m_lineConnectionCache[ newWire ] = { line };
1252 m_lineConnectionCache[ line ] = { newWire };
1253 }
1254 }
1255 break;
1256
1257 default:
1258 break;
1259 }
1260
1261 break;
1262 }
1263
1264 // Since only one end is going to move, the movement vector of any labels attached to
1265 // it is scaled by the proportion of the line length the label is from the moving end.
1266 for( SCH_ITEM* item : items.Overlapping( line->GetBoundingBox() ) )
1267 {
1268 SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( item );
1269
1270 if( !label || label->IsSelected() )
1271 continue; // These will be moved on their own because they're selected
1272
1273 if( label->HasFlag( SELECTED_BY_DRAG ) )
1274 continue;
1275
1276 if( label->CanConnect( line ) && line->HitTest( label->GetPosition(), 1 ) )
1277 {
1278 label->SetFlags( SELECTED_BY_DRAG );
1279 aList.push_back( label );
1280
1282 info.attachedLine = line;
1283 info.originalLabelPos = label->GetPosition();
1284 m_specialCaseLabels[label] = info;
1285 }
1286 }
1287
1288 break;
1289 }
1290
1291 case SCH_SHEET_T:
1292 for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( test )->GetPins() )
1293 {
1294 if( pin->IsConnected( aPoint ) )
1295 {
1296 if( pin->IsSelected() && aSelectedItem->Type() == SCH_LINE_T )
1297 {
1298 SCH_LINE* line = static_cast<SCH_LINE*>( aSelectedItem );
1299 m_specialCaseSheetPins[ pin ] = { line, line->GetStartPoint() == aPoint };
1300 }
1301 else if( !newWire )
1302 {
1303 // Add a new wire between the sheetpin and the selected item so the
1304 // selected item can be dragged.
1305 newWire = makeNewWire( pin, aSelectedItem, aPoint, aPoint );
1306 newWire->SetFlags( SELECTED_BY_DRAG | STARTPOINT );
1307 aList.push_back( newWire );
1308 }
1309 }
1310 }
1311
1312 break;
1313
1314 case SCH_SYMBOL_T:
1315 case SCH_JUNCTION_T:
1316 if( test->IsConnected( aPoint ) && !newWire )
1317 {
1318 // Add a new wire between the symbol or junction and the selected item so
1319 // the selected item can be dragged.
1320 newWire = makeNewWire( test, aSelectedItem, aPoint, aPoint );
1321 newWire->SetFlags( SELECTED_BY_DRAG | STARTPOINT );
1322 aList.push_back( newWire );
1323 }
1324
1325 break;
1326
1327 case SCH_NO_CONNECT_T:
1328 // Select no-connects that are connected to items being moved.
1329 if( !test->HasFlag( SELECTED_BY_DRAG ) && test->IsConnected( aPoint ) )
1330 {
1331 aList.push_back( test );
1332 test->SetFlags( SELECTED_BY_DRAG );
1333 }
1334
1335 break;
1336
1337 case SCH_LABEL_T:
1338 case SCH_GLOBAL_LABEL_T:
1339 case SCH_HIER_LABEL_T:
1341 // Performance optimization:
1342 if( test->HasFlag( SELECTED_BY_DRAG ) )
1343 break;
1344
1345 // Select labels that are connected to a wire (or bus) being moved.
1346 if( aSelectedItem->Type() == SCH_LINE_T && test->CanConnect( aSelectedItem ) )
1347 {
1348 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( test );
1349 SCH_LINE* line = static_cast<SCH_LINE*>( aSelectedItem );
1350
1351 bool oneEndFixed = !line->HasFlag( STARTPOINT ) || !line->HasFlag( ENDPOINT );
1352
1353 if( line->HitTest( label->GetTextPos(), 1 ) )
1354 {
1355 if( ( !line->HasFlag( STARTPOINT )
1356 && label->GetPosition() == line->GetStartPoint() )
1357 || ( !line->HasFlag( ENDPOINT )
1358 && label->GetPosition() == line->GetEndPoint() ) )
1359 {
1360 //If we have a line selected at only one end, don't grab labels
1361 //connected directly to the unselected endpoint
1362 break;
1363 }
1364 else
1365 {
1366 label->SetFlags( SELECTED_BY_DRAG );
1367 aList.push_back( label );
1368
1369 if( oneEndFixed )
1370 {
1372 info.attachedLine = line;
1373 info.originalLabelPos = label->GetPosition();
1374 m_specialCaseLabels[label] = info;
1375 }
1376 }
1377 }
1378 }
1379 else if( test->IsConnected( aPoint ) && !newWire )
1380 {
1381 // Add a new wire between the label and the selected item so the selected item
1382 // can be dragged.
1383 newWire = makeNewWire( test, aSelectedItem, aPoint, aPoint );
1384 newWire->SetFlags( SELECTED_BY_DRAG | STARTPOINT );
1385 aList.push_back( newWire );
1386 }
1387
1388 break;
1389
1392 // Performance optimization:
1393 if( test->HasFlag( SELECTED_BY_DRAG ) )
1394 break;
1395
1396 // Select bus entries that are connected to a bus being moved.
1397 if( aSelectedItem->Type() == SCH_LINE_T && test->CanConnect( aSelectedItem ) )
1398 {
1399 SCH_LINE* line = static_cast<SCH_LINE*>( aSelectedItem );
1400
1401 if( ( !line->HasFlag( STARTPOINT ) && test->IsConnected( line->GetStartPoint() ) )
1402 || ( !line->HasFlag( ENDPOINT ) && test->IsConnected( line->GetEndPoint() ) ) )
1403 {
1404 // If we have a line selected at only one end, don't grab bus entries
1405 // connected directly to the unselected endpoint
1406 continue;
1407 }
1408
1409 for( VECTOR2I& point : test->GetConnectionPoints() )
1410 {
1411 if( line->HitTest( point, 1 ) )
1412 {
1413 test->SetFlags( SELECTED_BY_DRAG );
1414 aList.push_back( test );
1415
1416 // A bus entry needs its wire & label as well
1417 std::vector<VECTOR2I> ends = test->GetConnectionPoints();
1418 VECTOR2I otherEnd;
1419
1420 if( ends[0] == point )
1421 otherEnd = ends[1];
1422 else
1423 otherEnd = ends[0];
1424
1425 getConnectedDragItems( test, otherEnd, aList, aAppendUndo );
1426
1427 // No need to test the other end of the bus entry
1428 break;
1429 }
1430 }
1431 }
1432
1433 break;
1434
1435 default:
1436 break;
1437 }
1438 }
1439}
1440
1441
1442void SCH_MOVE_TOOL::moveItem( EDA_ITEM* aItem, const VECTOR2I& aDelta )
1443{
1444 switch( aItem->Type() )
1445 {
1446 case SCH_LINE_T:
1447 {
1448 SCH_LINE* line = static_cast<SCH_LINE*>( aItem );
1449
1450 if( aItem->HasFlag( STARTPOINT ) )
1451 line->MoveStart( aDelta );
1452
1453 if( aItem->HasFlag( ENDPOINT ) )
1454 line->MoveEnd( aDelta );
1455
1456 break;
1457 }
1458
1459 case SCH_PIN_T:
1460 case SCH_FIELD_T:
1461 {
1462 SCH_ITEM* parent = (SCH_ITEM*) aItem->GetParent();
1463 VECTOR2I delta( aDelta );
1464
1465 if( parent && parent->Type() == SCH_SYMBOL_T )
1466 {
1467 SCH_SYMBOL* symbol = (SCH_SYMBOL*) aItem->GetParent();
1468 TRANSFORM transform = symbol->GetTransform().InverseTransform();
1469
1470 delta = transform.TransformCoordinate( delta );
1471 }
1472
1473 static_cast<SCH_ITEM*>( aItem )->Move( delta );
1474
1475 // If we're moving a field with respect to its parent then it's no longer auto-placed
1476 if( aItem->Type() == SCH_FIELD_T && parent && !parent->IsSelected() )
1477 parent->ClearFieldsAutoplaced();
1478
1479 break;
1480 }
1481
1482 case SCH_SHEET_PIN_T:
1483 {
1484 SCH_SHEET_PIN* pin = (SCH_SHEET_PIN*) aItem;
1485
1486 pin->SetStoredPos( pin->GetStoredPos() + aDelta );
1487 pin->ConstrainOnEdge( pin->GetStoredPos() );
1488 break;
1489 }
1490
1491 case SCH_LABEL_T:
1493 case SCH_GLOBAL_LABEL_T:
1494 case SCH_HIER_LABEL_T:
1495 {
1496 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( aItem );
1497
1498 if( m_specialCaseLabels.count( label ) )
1499 {
1501 SEG currentLine( info.attachedLine->GetStartPoint(), info.attachedLine->GetEndPoint() );
1502 label->SetPosition( currentLine.NearestPoint( info.originalLabelPos ) );
1503 }
1504 else
1505 {
1506 label->Move( aDelta );
1507 }
1508
1509 break;
1510 }
1511
1512 default:
1513 static_cast<SCH_ITEM*>( aItem )->Move( aDelta );
1514 break;
1515 }
1516
1517 getView()->Hide( aItem, false );
1518 aItem->SetFlags( IS_MOVING );
1519}
1520
1521
1523{
1526 bool appendUndo = false;
1527
1528 auto doMoveItem =
1529 [&]( EDA_ITEM* item, const VECTOR2I& delta )
1530 {
1531 saveCopyInUndoList( item, UNDO_REDO::CHANGED, appendUndo );
1532 appendUndo = true;
1533
1534 moveItem( item, delta );
1535 item->ClearFlags( IS_MOVING );
1536 updateItem( item, true );
1537 };
1538
1539 for( SCH_ITEM* it : m_frame->GetScreen()->Items() )
1540 {
1541 if( !it->IsSelected() )
1542 it->ClearFlags( STARTPOINT | ENDPOINT );
1543
1544 if( !selection.IsHover() && it->IsSelected() )
1545 it->SetFlags( STARTPOINT | ENDPOINT );
1546
1547 it->SetStoredPos( it->GetPosition() );
1548
1549 if( it->Type() == SCH_SHEET_T )
1550 {
1551 for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( it )->GetPins() )
1552 pin->SetStoredPos( pin->GetPosition() );
1553 }
1554 }
1555
1556 for( EDA_ITEM* item : selection )
1557 {
1558 if( item->Type() == SCH_LINE_T )
1559 {
1560 SCH_LINE* line = static_cast<SCH_LINE*>( item );
1561 std::vector<int> flags{ STARTPOINT, ENDPOINT };
1562 std::vector<VECTOR2I> pts{ line->GetStartPoint(), line->GetEndPoint() };
1563
1564 for( int ii = 0; ii < 2; ++ii )
1565 {
1566 EDA_ITEMS drag_items{ item };
1567 line->ClearFlags();
1568 line->SetFlags( SELECTED );
1569 line->SetFlags( flags[ii] );
1570 getConnectedDragItems( line, pts[ii], drag_items, appendUndo );
1571 std::set<EDA_ITEM*> unique_items( drag_items.begin(), drag_items.end() );
1572
1573 VECTOR2I gridpt = grid.AlignGrid( pts[ii] ) - pts[ii];
1574
1575 if( gridpt != VECTOR2I( 0, 0 ) )
1576 {
1577 for( EDA_ITEM* dragItem : unique_items )
1578 {
1579 if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
1580 continue;
1581
1582 doMoveItem( dragItem, gridpt );
1583 }
1584 }
1585 }
1586 }
1587 else if( item->Type() == SCH_FIELD_T )
1588 {
1589 VECTOR2I gridpt = grid.AlignGrid( item->GetPosition() ) - item->GetPosition();
1590
1591 if( gridpt != VECTOR2I( 0, 0 ) )
1592 doMoveItem( item, gridpt );
1593 }
1594 else if( item->Type() == SCH_SHEET_T )
1595 {
1596 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
1597 VECTOR2I topLeft = sheet->GetPosition();
1598 VECTOR2I bottomRight = topLeft + sheet->GetSize();
1599 VECTOR2I tl_gridpt = grid.AlignGrid( topLeft ) - topLeft;
1600 VECTOR2I br_gridpt = grid.AlignGrid( bottomRight ) - bottomRight;
1601
1602 if( tl_gridpt != VECTOR2I( 0, 0 ) || br_gridpt != VECTOR2I( 0, 0 ) )
1603 {
1604 doMoveItem( sheet, tl_gridpt );
1605
1606 VECTOR2I newSize = (VECTOR2I) sheet->GetSize() - tl_gridpt + br_gridpt;
1607 sheet->SetSize( wxSize( newSize.x, newSize.y ) );
1608 updateItem( sheet, true );
1609
1610 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
1611 {
1612 VECTOR2I gridpt;
1613
1614 if( pin->GetSide() == SHEET_SIDE::TOP || pin->GetSide() == SHEET_SIDE::LEFT )
1615 gridpt = tl_gridpt;
1616 else
1617 gridpt = br_gridpt;
1618
1619 if( gridpt != VECTOR2I( 0, 0 ) )
1620 {
1621 EDA_ITEMS drag_items;
1622 getConnectedDragItems( pin, pin->GetConnectionPoints()[0], drag_items,
1623 appendUndo );
1624
1625 doMoveItem( pin, gridpt );
1626
1627 for( EDA_ITEM* dragItem : drag_items )
1628 {
1629 if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
1630 continue;
1631
1632 doMoveItem( dragItem, gridpt );
1633 }
1634 }
1635 }
1636 }
1637 }
1638 else
1639 {
1640 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
1641 std::vector<VECTOR2I> connections = schItem->GetConnectionPoints();
1642 EDA_ITEMS drag_items{ item };
1643
1644 for( const VECTOR2I& point : connections )
1645 getConnectedDragItems( schItem, point, drag_items, appendUndo );
1646
1647 std::map<VECTOR2I, int> shifts;
1648 VECTOR2I most_common( 0, 0 );
1649 int max_count = 0;
1650
1651 for( const VECTOR2I& conn : connections )
1652 {
1653 VECTOR2I gridpt = grid.AlignGrid( conn ) - conn;
1654
1655 shifts[gridpt]++;
1656
1657 if( shifts[gridpt] > max_count )
1658 {
1659 most_common = gridpt;
1660 max_count = shifts[most_common];
1661 }
1662 }
1663
1664 if( most_common != VECTOR2I( 0, 0 ) )
1665 {
1666 for( EDA_ITEM* dragItem : drag_items )
1667 {
1668 if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
1669 continue;
1670
1671 doMoveItem( dragItem, most_common );
1672 }
1673 }
1674 }
1675 }
1676
1680
1683
1684 m_frame->OnModify();
1685 return 0;
1686}
1687
1688
1690{
1691 for( SCH_LINE* newLine : m_newDragLines )
1692 {
1693 newLine->ClearEditFlags();
1694 saveCopyInUndoList( newLine, UNDO_REDO::NEWITEM, true );
1695 }
1696
1697 // These lines have been changed, but aren't selected. We need
1698 // to manually clear these edit flags or they'll stick around.
1699 for( SCH_LINE* oldLine : m_changedDragLines )
1700 {
1701 oldLine->ClearEditFlags();
1702 }
1703
1704 m_newDragLines.clear();
1705 m_changedDragLines.clear();
1706}
1707
1708
1710{
1711 // Remove new bend lines added during the drag
1712 for( SCH_LINE* newLine : m_newDragLines )
1713 {
1714 m_frame->RemoveFromScreen( newLine, m_frame->GetScreen() );
1715 delete newLine;
1716 }
1717
1718 m_newDragLines.clear();
1719}
1720
1721
1723{
1725 Go( &SCH_MOVE_TOOL::Main, EE_ACTIONS::move.MakeEvent() );
1726 Go( &SCH_MOVE_TOOL::Main, EE_ACTIONS::drag.MakeEvent() );
1728}
1729
1730
static TOOL_ACTION duplicate
Definition: actions.h:72
static TOOL_ACTION doDelete
Definition: actions.h:73
static TOOL_ACTION cursorClick
Definition: actions.h:126
static TOOL_ACTION refreshPreview
Definition: actions.h:109
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:251
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:142
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:143
bool IsSelected() const
Definition: eda_item.h:107
EDA_ITEM * GetParent() const
Definition: eda_item.h:99
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition: eda_item.h:145
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:120
static TOOL_ACTION highlightNet
Definition: ee_actions.h:252
static TOOL_ACTION move
Definition: ee_actions.h:123
static TOOL_ACTION clearSelection
Clears the current selection.
Definition: ee_actions.h:56
static TOOL_ACTION drag
Definition: ee_actions.h:124
static TOOL_ACTION moveActivate
Definition: ee_actions.h:122
static TOOL_ACTION rotateCCW
Definition: ee_actions.h:127
static TOOL_ACTION restartMove
Definition: ee_actions.h:222
static TOOL_ACTION rotateCW
Definition: ee_actions.h:126
static TOOL_ACTION selectOnPCB
Definition: ee_actions.h:223
static TOOL_ACTION trimOverlappingWires
Definition: ee_actions.h:79
static TOOL_ACTION addNeededJunctions
Definition: ee_actions.h:78
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:184
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:213
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:216
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:1586
void Hide(VIEW_ITEM *aItem, bool aHide=true)
Temporarily hide the item in the view (e.g.
Definition: view.cpp:1545
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 RecalculateConnections(SCH_CLEANUP_FLAGS aCleanupFlags)
Generate the connection data for the entire schematic hierarchy.
void SelectUnit(SCH_SYMBOL *aSymbol, int aUnit)
Definition: picksymbol.cpp:239
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.
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
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
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:764
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:664
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: sch_line.cpp:191
void GetSelectedPoints(std::vector< VECTOR2I > &aPoints) const
Definition: sch_line.cpp:683
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
void MoveEnd(const VECTOR2I &aMoveVector)
Definition: sch_line.cpp:155
void SetLastResolvedState(const SCH_ITEM *aItem) override
Definition: sch_line.h:146
void MoveStart(const VECTOR2I &aMoveVector)
Definition: sch_line.cpp:145
double GetLength() const
Definition: sch_line.cpp:207
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
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:443
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:55
void SetSize(const wxSize &aSize)
Definition: sch_sheet.h:107
VECTOR2I GetPosition() const override
Definition: sch_sheet.h:366
wxSize GetSize() const
Definition: sch_sheet.h:106
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition: sch_sheet.h:173
Schematic symbol object.
Definition: sch_symbol.h:80
TRANSFORM & GetTransform()
Definition: sch_symbol.h:282
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:261
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:32
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:133
VECTOR2I GetReferencePoint() const
Definition: selection.h:250
bool IsHover() const
Definition: selection.h:81
EDA_ITEM * Front() const
Definition: selection.h:206
void SetReferencePoint(const VECTOR2I &aP)
Definition: selection.h:258
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:107
bool HasReferencePoint() const
Definition: selection.h:245
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:214
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:212
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:200
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:88
bool IsDblClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:206
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:56
VECTOR2I TransformCoordinate(const VECTOR2I &aPoint) const
Calculate a new coordinate according to the mirror/rotation transform.
Definition: transform.cpp:41
void Dismiss() override
Dismisses the infobar and updates the containing layout and AUI manager (if one is provided).
Definition: infobar.cpp:175
#define _(s)
static constexpr EDA_ANGLE & ANGLE_90
Definition: eda_angle.h:414
std::vector< EDA_ITEM * > EDA_ITEMS
Define list of drawing items for screens.
Definition: eda_item.h:526
#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 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:401
see class PGM_BASE
@ LOCAL_CLEANUP
#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:618