KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_line_wire_bus_tool.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2019 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
26
27#include <wx/debug.h>
28#include <wx/gdicmn.h>
29#include <wx/string.h>
30#include <wx/translation.h>
31#include <algorithm>
32#include <cstdlib>
33#include <iterator>
34#include <memory>
35#include <utility>
36#include <vector>
37
38#include <layer_ids.h>
39#include <math/vector2d.h>
40#include <advanced_config.h>
42#include <view/view_controls.h>
43#include <tool/actions.h>
45#include <tool/selection.h>
47#include <tool/tool_event.h>
48#include <trigo.h>
49#include <eeschema_id.h>
50#include <sch_bus_entry.h>
51#include <sch_connection.h>
52#include <sch_edit_frame.h>
53#include <sch_junction.h>
54#include <sch_line.h>
55#include <sch_screen.h>
56#include <sch_sheet.h>
57#include <sch_group.h>
58#include <sch_sheet_pin.h>
59#include <schematic.h>
60#include <sch_commit.h>
61#include <sch_actions.h>
62#include <junction_helpers.h>
63#include <ee_grid_helper.h>
64#include <sch_selection.h>
65#include <sch_selection_tool.h>
66
67
68using BUS_GETTER = std::function<SCH_LINE*()>;
69
71{
72public:
78 ACTION_MENU( true ),
79 m_showTitle( false ),
80 m_busGetter( aBusGetter )
81 {
83 SetTitle( _( "Unfold from Bus" ) );
84 }
85
87 {
88 m_showTitle = true;
89 }
90
91 bool PassHelpTextToHandler() override { return true; }
92
93protected:
94 ACTION_MENU* create() const override
95 {
96 return new BUS_UNFOLD_MENU( m_busGetter );
97 }
98
99private:
100 void update() override
101 {
102 SCH_LINE* bus = m_busGetter();
103 Clear();
104 // Pick up the pointer again because it may have been changed by SchematicCleanUp
105 bus = m_busGetter();
106
107 if( !bus )
108 {
109 Append( ID_POPUP_SCH_UNFOLD_BUS, _( "No bus selected" ), wxEmptyString );
110 Enable( ID_POPUP_SCH_UNFOLD_BUS, false );
111 return;
112 }
113
114 SCH_CONNECTION* connection = bus->Connection();
115
116 if( !connection || !connection->IsBus() || connection->Members().empty() )
117 {
118 Append( ID_POPUP_SCH_UNFOLD_BUS, _( "Bus has no members" ), wxEmptyString );
119 Enable( ID_POPUP_SCH_UNFOLD_BUS, false );
120 return;
121 }
122
123 int idx = 0;
124
125 if( m_showTitle )
126 {
127 Append( ID_POPUP_SCH_UNFOLD_BUS, _( "Unfold from Bus" ), wxEmptyString );
128 Enable( ID_POPUP_SCH_UNFOLD_BUS, false );
129 }
130
131 for( const std::shared_ptr<SCH_CONNECTION>& member : connection->Members() )
132 {
133 int id = ID_POPUP_SCH_UNFOLD_BUS + ( idx++ );
134 wxString name = member->FullLocalName();
135
136 if( member->Type() == CONNECTION_TYPE::BUS )
137 {
138 ACTION_MENU* submenu = new ACTION_MENU( true, m_tool );
139 AppendSubMenu( submenu, SCH_CONNECTION::PrintBusForUI( name ), name );
140
141 for( const std::shared_ptr<SCH_CONNECTION>& sub_member : member->Members() )
142 {
143 id = ID_POPUP_SCH_UNFOLD_BUS + ( idx++ );
144 name = sub_member->FullLocalName();
145 submenu->Append( id, SCH_CONNECTION::PrintBusForUI( name ), name );
146 }
147 }
148 else
149 {
150 Append( id, name, wxEmptyString );
151 }
152 }
153 }
154
157};
158
159
167
168
172
173
175 SCH_TOOL_BASE<SCH_EDIT_FRAME>( "eeschema.InteractiveDrawingLineWireBus" ),
176 m_inDrawingTool( false )
177{
178 m_busUnfold = {};
179 m_wires.reserve( 16 );
180}
181
182
186
187
189{
191
192 const auto busGetter =
193 [this]()
194 {
195 return getBusForUnfolding();
196 };
197
198 std::shared_ptr<BUS_UNFOLD_MENU>
199 busUnfoldMenu = std::make_shared<BUS_UNFOLD_MENU>( busGetter );
200 busUnfoldMenu->SetTool( this );
201 m_menu->RegisterSubMenu( busUnfoldMenu );
202
203 std::shared_ptr<BUS_UNFOLD_MENU> selBusUnfoldMenu = std::make_shared<BUS_UNFOLD_MENU>( busGetter );
204 selBusUnfoldMenu->SetTool( m_selectionTool );
205 m_selectionTool->GetToolMenu().RegisterSubMenu( selBusUnfoldMenu );
206
207 auto wireOrBusTool =
208 [this]( const SELECTION& aSel )
209 {
210 return ( m_frame->IsCurrentTool( SCH_ACTIONS::drawWire )
211 || m_frame->IsCurrentTool( SCH_ACTIONS::drawBus ) );
212 };
213
214 auto lineTool =
215 [this]( const SELECTION& aSel )
216 {
217 return m_frame->IsCurrentTool( SCH_ACTIONS::drawLines );
218 };
219
220 auto belowRootSheetCondition =
221 [this]( const SELECTION& aSel )
222 {
223 return m_frame->GetCurrentSheet().Last() != &m_frame->Schematic().Root();
224 };
225
226 auto busSelection = SCH_CONDITIONS::MoreThan( 0 )
228
229 auto haveHighlight =
230 [this]( const SELECTION& sel )
231 {
232 SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
233
234 return editFrame && !editFrame->GetHighlightedConnection().IsEmpty();
235 };
236
237 auto& ctxMenu = m_menu->GetMenu();
238
239 // Build the tool menu
240 //
241 ctxMenu.AddItem( SCH_ACTIONS::clearHighlight, haveHighlight && SCH_CONDITIONS::Idle, 1 );
242 ctxMenu.AddSeparator( haveHighlight && SCH_CONDITIONS::Idle, 1 );
243
244 ctxMenu.AddSeparator( 10 );
245 ctxMenu.AddItem( SCH_ACTIONS::drawWire, wireOrBusTool && SCH_CONDITIONS::Idle, 10 );
246 ctxMenu.AddItem( SCH_ACTIONS::drawBus, wireOrBusTool && SCH_CONDITIONS::Idle, 10 );
247 ctxMenu.AddItem( SCH_ACTIONS::drawLines, lineTool && SCH_CONDITIONS::Idle, 10 );
248
252
253 ctxMenu.AddMenu( busUnfoldMenu.get(), SCH_CONDITIONS::Idle, 10 );
254
255 ctxMenu.AddSeparator( 100 );
256 ctxMenu.AddItem( SCH_ACTIONS::placeJunction, wireOrBusTool && SCH_CONDITIONS::Idle, 100 );
257 ctxMenu.AddItem( SCH_ACTIONS::placeLabel, wireOrBusTool && SCH_CONDITIONS::Idle, 100 );
258 ctxMenu.AddItem( SCH_ACTIONS::placeClassLabel, wireOrBusTool && SCH_CONDITIONS::Idle, 100 );
259 ctxMenu.AddItem( SCH_ACTIONS::placeGlobalLabel, wireOrBusTool && SCH_CONDITIONS::Idle, 100 );
260 ctxMenu.AddItem( SCH_ACTIONS::placeHierLabel, wireOrBusTool && SCH_CONDITIONS::Idle, 100 );
261 ctxMenu.AddItem( SCH_ACTIONS::breakWire, wireOrBusTool && SCH_CONDITIONS::Idle, 100 );
262 ctxMenu.AddItem( SCH_ACTIONS::slice, ( wireOrBusTool || lineTool )
263 && SCH_CONDITIONS::Idle, 100 );
264 ctxMenu.AddItem( SCH_ACTIONS::leaveSheet, belowRootSheetCondition, 150 );
265
266 ctxMenu.AddSeparator( 200 );
267 ctxMenu.AddItem( SCH_ACTIONS::selectNode, wireOrBusTool && SCH_CONDITIONS::Idle, 200 );
268 ctxMenu.AddItem( SCH_ACTIONS::selectConnection, wireOrBusTool && SCH_CONDITIONS::Idle, 200 );
269
270 // Add bus unfolding to the selection tool
271 //
272 CONDITIONAL_MENU& selToolMenu = m_selectionTool->GetToolMenu().GetMenu();
273
274 selToolMenu.AddMenu( selBusUnfoldMenu.get(), busSelection && SCH_CONDITIONS::Idle, 100 );
275
276 return true;
277}
278
279
281{
282 // NOTE: for immediate hotkeys, it is NOT required that the line, wire or bus tool
283 // be selected
284 SCH_ITEM* item = (SCH_ITEM*) aSelection.Front();
285 return item && item->IsNew() && item->Type() == SCH_LINE_T;
286}
287
288
290{
291 if( m_inDrawingTool )
292 return 0;
293
295
296 const DRAW_SEGMENT_EVENT_PARAMS* params = aEvent.Parameter<const DRAW_SEGMENT_EVENT_PARAMS*>();
297 SCH_COMMIT commit( m_toolMgr );
298
299 m_frame->PushTool( aEvent );
301
302 if( aEvent.HasPosition() )
303 {
305 GRID_HELPER_GRIDS gridType = ( params->layer == LAYER_NOTES ) ? GRID_GRAPHICS : GRID_WIRES;
306
307 grid.SetSnap( !aEvent.Modifier( MD_SHIFT ) );
308 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !aEvent.DisableGridSnapping() );
309
310 VECTOR2D cursorPos = grid.BestSnapAnchor( aEvent.Position(), gridType, nullptr );
311 startSegments( commit, params->layer, cursorPos, params->sourceSegment );
312 }
313
314 return doDrawSegments( aEvent, commit, params->layer, params->quitOnDraw );
315}
316
317
319{
320 if( m_inDrawingTool )
321 return 0;
322
324
325 SCH_COMMIT commit( m_toolMgr );
326 wxString* netPtr = aEvent.Parameter<wxString*>();
327 wxString net;
328 SCH_LINE* segment = nullptr;
329
330 m_frame->PushTool( aEvent );
331 Activate();
332
333 if( netPtr )
334 {
335 net = *netPtr;
336 delete netPtr;
337 }
338 else
339 {
340 const auto busGetter = [this]()
341 {
342 return getBusForUnfolding();
343 };
344 BUS_UNFOLD_MENU unfoldMenu( busGetter );
345 unfoldMenu.SetTool( this );
346 unfoldMenu.SetShowTitle();
347
348 SetContextMenu( &unfoldMenu, CMENU_NOW );
349
350 while( TOOL_EVENT* evt = Wait() )
351 {
352 if( evt->Action() == TA_CHOICE_MENU_CHOICE )
353 {
354 std::optional<int> id = evt->GetCommandId();
355
356 if( id && ( *id > 0 ) )
357 net = *evt->Parameter<wxString*>();
358
359 break;
360 }
361 else if( evt->Action() == TA_CHOICE_MENU_CLOSED )
362 {
363 break;
364 }
365 else
366 {
367 evt->SetPassEvent();
368 }
369 }
370 }
371
372 // Break a wire for the given net out of the bus
373 if( !net.IsEmpty() )
374 segment = doUnfoldBus( commit, net );
375
376 // If we have an unfolded wire to draw, then draw it
377 if( segment )
378 {
379 return doDrawSegments( aEvent, commit, LAYER_WIRE, false );
380 }
381 else
382 {
383 m_frame->PopTool( aEvent );
384 return 0;
385 }
386}
387
388
390{
391 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_ITEM_LOCATE_BUS_T } );
392 return static_cast<SCH_LINE*>( selection.Front() );
393}
394
395
397 const std::optional<VECTOR2I>& aPos )
398{
399 SCHEMATIC_SETTINGS& cfg = getModel<SCHEMATIC>()->Settings();
400 SCH_SCREEN* screen = m_frame->GetScreen();
401 // use the same function as the menu selector, so we choose the same bus segment
402 SCH_LINE* const bus = getBusForUnfolding();
403
404 if ( bus == nullptr )
405 {
406 wxASSERT_MSG( false, wxString::Format( "Couldn't find the originating bus line (but had a net: %s )",
407 aNet ) );
408 return nullptr;
409 }
410
411 VECTOR2I pos = aPos.value_or( static_cast<VECTOR2I>( getViewControls()->GetCursorPosition() ) );
412
413 // It is possible for the position to be near the bus, but not exactly on it, but
414 // we need the bus entry to be on the bus exactly to connect.
415 // If the bus segment is H or V, this will be on the selection grid, if it's not,
416 // it might not be, but it won't be a broken connection (and the user asked for it!)
417 pos = bus->GetSeg().NearestPoint( pos );
418
420
421 m_busUnfold.entry = new SCH_BUS_WIRE_ENTRY( pos );
422 m_busUnfold.entry->SetParent( screen );
423 m_frame->AddToScreen( m_busUnfold.entry, m_frame->GetScreen() );
424
425 m_busUnfold.label = new SCH_LABEL( m_busUnfold.entry->GetEnd(), aNet );
426 m_busUnfold.label->SetTextSize( VECTOR2I( cfg.m_DefaultTextSize, cfg.m_DefaultTextSize ) );
427 m_busUnfold.label->SetSpinStyle( busUnfoldPersistentSettings.label_spin_style );
428 m_busUnfold.label->SetParent( m_frame->GetScreen() );
429 m_busUnfold.label->SetFlags( IS_NEW | IS_MOVING );
430
431 m_busUnfold.in_progress = true;
432 m_busUnfold.origin = pos;
433 m_busUnfold.net_name = aNet;
434
435 getViewControls()->SetCrossHairCursorPosition( m_busUnfold.entry->GetEnd(), false );
436
437 std::vector<DANGLING_END_ITEM> endPointsByType;
438
439 for( SCH_ITEM* item : screen->Items().Overlapping( m_busUnfold.entry->GetBoundingBox() ) )
440 item->GetEndPoints( endPointsByType );
441
442 std::vector<DANGLING_END_ITEM> endPointsByPos = endPointsByType;
443 DANGLING_END_ITEM_HELPER::sort_dangling_end_items( endPointsByType, endPointsByPos );
444 m_busUnfold.entry->UpdateDanglingState( endPointsByType, endPointsByPos );
445 m_busUnfold.entry->SetEndDangling( false );
446 m_busUnfold.label->SetIsDangling( false );
447
448 return startSegments( aCommit, LAYER_WIRE, m_busUnfold.entry->GetEnd() );
449}
450
451
453{
454 SCH_SCREEN* screen = m_frame->GetScreen();
455
456 for( SCH_ITEM* item : screen->Items().Overlapping( SCH_SHEET_T, aPosition ) )
457 {
458 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
459
460 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
461 {
462 if( pin->GetPosition() == aPosition )
463 return pin;
464 }
465 }
466
467 return nullptr;
468}
469
470
471void SCH_LINE_WIRE_BUS_TOOL::computeBreakPoint( const std::pair<SCH_LINE*, SCH_LINE*>& aSegments,
472 VECTOR2I& aPosition,
473 LINE_MODE mode,
474 bool posture )
475{
476 wxCHECK_RET( aSegments.first && aSegments.second,
477 wxT( "Cannot compute break point of NULL line segment." ) );
478
479 VECTOR2I midPoint;
480 SCH_LINE* segment = aSegments.first;
481 SCH_LINE* nextSegment = aSegments.second;
482
483 VECTOR2I delta = aPosition - segment->GetStartPoint();
484 int xDir = delta.x > 0 ? 1 : -1;
485 int yDir = delta.y > 0 ? 1 : -1;
486
487
488 bool preferHorizontal;
489 bool preferVertical;
490
491 if( ( mode == LINE_MODE_45 ) && posture )
492 {
493 preferHorizontal = ( nextSegment->GetEndPoint().x - nextSegment->GetStartPoint().x ) != 0;
494 preferVertical = ( nextSegment->GetEndPoint().y - nextSegment->GetStartPoint().y ) != 0;
495 }
496 else
497 {
498 preferHorizontal = ( segment->GetEndPoint().x - segment->GetStartPoint().x ) != 0;
499 preferVertical = ( segment->GetEndPoint().y - segment->GetStartPoint().y ) != 0;
500 }
501
502 // Check for times we need to force horizontal sheet pin connections
503 const SCH_SHEET_PIN* connectedPin = getSheetPin( segment->GetStartPoint() );
504 SHEET_SIDE force = connectedPin ? connectedPin->GetSide() : SHEET_SIDE::UNDEFINED;
505
506 if( force == SHEET_SIDE::LEFT || force == SHEET_SIDE::RIGHT )
507 {
508 if( aPosition.x == connectedPin->GetPosition().x ) // push outside sheet boundary
509 {
510 int direction = ( force == SHEET_SIDE::LEFT ) ? -1 : 1;
511 aPosition.x += KiROUND( getView()->GetGAL()->GetGridSize().x * direction );
512 }
513
514 preferHorizontal = true;
515 preferVertical = false;
516 }
517
518
519 auto breakVertical = [&]() mutable
520 {
521 switch( mode )
522 {
523 case LINE_MODE_45:
524 if( !posture )
525 {
526 midPoint.x = segment->GetStartPoint().x;
527 midPoint.y = aPosition.y - yDir * abs( delta.x );
528 }
529 else
530 {
531 midPoint.x = aPosition.x;
532 midPoint.y = segment->GetStartPoint().y + yDir * abs( delta.x );
533 }
534 break;
535 default:
536 midPoint.x = segment->GetStartPoint().x;
537 midPoint.y = aPosition.y;
538 }
539 };
540
541
542 auto breakHorizontal = [&]() mutable
543 {
544 switch( mode )
545 {
546 case LINE_MODE_45:
547 if( !posture )
548 {
549 midPoint.x = aPosition.x - xDir * abs( delta.y );
550 midPoint.y = segment->GetStartPoint().y;
551 }
552 else
553 {
554 midPoint.x = segment->GetStartPoint().x + xDir * abs( delta.y );
555 midPoint.y = aPosition.y;
556 }
557 break;
558 default:
559 midPoint.x = aPosition.x;
560 midPoint.y = segment->GetStartPoint().y;
561 }
562 };
563
564
565 // Maintain current line shape if we can, e.g. if we were originally moving
566 // vertically keep the first segment vertical
567 if( preferVertical )
568 breakVertical();
569 else if( preferHorizontal )
570 breakHorizontal();
571
572 // Check if our 45 degree angle is one of these shapes
573 // /
574 // /
575 // /
576 // /__________
577 VECTOR2I deltaMidpoint = midPoint - segment->GetStartPoint();
578
579 if( mode == LINE_MODE::LINE_MODE_45 && !posture
580 && ( ( alg::signbit( deltaMidpoint.x ) != alg::signbit( delta.x ) )
581 || ( alg::signbit( deltaMidpoint.y ) != alg::signbit( delta.y ) ) ) )
582 {
583 preferVertical = false;
584 preferHorizontal = false;
585 }
586 else if( mode == LINE_MODE::LINE_MODE_45 && posture
587 && ( ( abs( deltaMidpoint.x ) > abs( delta.x ) )
588 || ( abs( deltaMidpoint.y ) > abs( delta.y ) ) ) )
589 {
590 preferVertical = false;
591 preferHorizontal = false;
592 }
593
594 if( !preferHorizontal && !preferVertical )
595 {
596 if( std::abs( delta.x ) < std::abs( delta.y ) )
597 breakVertical();
598 else
599 breakHorizontal();
600 }
601
602 segment->SetEndPoint( midPoint );
603 nextSegment->SetStartPoint( midPoint );
604 nextSegment->SetEndPoint( aPosition );
605}
606
607
609 int aType, bool aQuitOnDraw )
610{
611 SCH_SCREEN* screen = m_frame->GetScreen();
612 SCH_LINE* segment = nullptr;
614 GRID_HELPER_GRIDS gridType = ( aType == LAYER_NOTES ) ? GRID_GRAPHICS : GRID_WIRES;
616 int lastMode = m_frame->eeconfig()->m_Drawing.line_mode;
617 static bool posture = false;
618
619 auto setCursor =
620 [&]()
621 {
622 if( aType == LAYER_WIRE )
623 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::LINE_WIRE );
624 else if( aType == LAYER_BUS )
625 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::LINE_BUS );
626 else if( aType == LAYER_NOTES )
627 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::LINE_GRAPHIC );
628 else
629 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::LINE_WIRE );
630 };
631
632 auto cleanup =
633 [&] ()
634 {
636
637 for( SCH_LINE* wire : m_wires )
638 delete wire;
639
640 m_wires.clear();
641 segment = nullptr;
642
643 if( m_busUnfold.entry )
644 m_frame->RemoveFromScreen( m_busUnfold.entry, screen );
645
646 if( m_busUnfold.label && !m_busUnfold.label_placed )
647 m_selectionTool->RemoveItemFromSel( m_busUnfold.label, true );
648
649 if( m_busUnfold.label && m_busUnfold.label_placed )
650 m_frame->RemoveFromScreen( m_busUnfold.label, screen );
651
652 delete m_busUnfold.entry;
653 delete m_busUnfold.label;
654 m_busUnfold = {};
655
656 m_view->ClearPreview();
657 m_view->ShowPreview( false );
658 };
659
660 Activate();
661 // Must be done after Activate() so that it gets set into the correct context
662 controls->ShowCursor( true );
663 // Set initial cursor
664 setCursor();
665
666 // Add the new label to the selection so the rotate command operates on it
667 if( m_busUnfold.label )
668 m_selectionTool->AddItemToSel( m_busUnfold.label, true );
669
670 // Continue the existing wires if we've started (usually by immediate action preference)
671 if( !m_wires.empty() )
672 segment = m_wires.back();
673
674 VECTOR2I contextMenuPos;
675
676 // Main loop: keep receiving events
677 while( TOOL_EVENT* evt = Wait() )
678 {
679 LINE_MODE currentMode = (LINE_MODE) m_frame->eeconfig()->m_Drawing.line_mode;
680 bool twoSegments = currentMode != LINE_MODE::LINE_MODE_FREE;
681
682 // The tool hotkey is interpreted as a click when drawing
683 bool isSyntheticClick = ( segment || m_busUnfold.in_progress ) && evt->IsActivate()
684 && evt->HasPosition() && evt->Matches( aTool );
685
686 setCursor();
687 grid.SetMask( GRID_HELPER::ALL );
688 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
689 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
690
691 if( segment )
692 {
693 if( segment->GetStartPoint().x == segment->GetEndPoint().x )
694 grid.ClearMaskFlag( GRID_HELPER::VERTICAL );
695
696 if( segment->GetStartPoint().y == segment->GetEndPoint().y )
697 grid.ClearMaskFlag( GRID_HELPER::HORIZONTAL );
698 }
699
700 VECTOR2D eventPosition = evt->HasPosition() ? evt->Position()
701 : controls->GetMousePosition();
702
703 VECTOR2I cursorPos = grid.BestSnapAnchor( eventPosition, gridType, segment );
704 controls->ForceCursorPosition( true, cursorPos );
705
706 // Need to handle change in H/V mode while drawing
707 if( currentMode != lastMode )
708 {
709 // Need to delete extra segment if we have one
710 if( segment && currentMode == LINE_MODE::LINE_MODE_FREE && m_wires.size() >= 2 )
711 {
712 m_wires.pop_back();
713 m_selectionTool->RemoveItemFromSel( segment );
714 delete segment;
715
716 segment = m_wires.back();
717 segment->SetEndPoint( cursorPos );
718 }
719 // Add a segment so we can move orthogonally/45
720 else if( segment && lastMode == LINE_MODE::LINE_MODE_FREE )
721 {
722 segment->SetEndPoint( cursorPos );
723
724 // Create a new segment, and chain it after the current segment.
725 segment = static_cast<SCH_LINE*>( segment->Duplicate( true, &aCommit ) );
726 segment->SetFlags( IS_NEW | IS_MOVING );
727 segment->SetStartPoint( cursorPos );
728 m_wires.push_back( segment );
729
730 m_selectionTool->AddItemToSel( segment, true /*quiet mode*/ );
731 }
732
733 lastMode = currentMode;
734 }
735
736 //------------------------------------------------------------------------
737 // Handle cancel:
738 //
739 if( evt->IsCancelInteractive() )
740 {
741 m_frame->GetInfoBar()->Dismiss();
742
743 if( segment || m_busUnfold.in_progress )
744 {
745 cleanup();
746
747 if( aQuitOnDraw )
748 {
749 m_frame->PopTool( aTool );
750 break;
751 }
752 }
753 else
754 {
755 m_frame->PopTool( aTool );
756 break;
757 }
758 }
759 else if( evt->IsActivate() && !isSyntheticClick )
760 {
761 if( segment || m_busUnfold.in_progress )
762 {
763 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel drawing." ) );
764 evt->SetPassEvent( false );
765 continue;
766 }
767
768 if( evt->IsMoveTool() )
769 {
770 // leave ourselves on the stack so we come back after the move
771 break;
772 }
773 else
774 {
775 m_frame->PopTool( aTool );
776 break;
777 }
778 }
779 //------------------------------------------------------------------------
780 // Handle finish:
781 //
782 else if( evt->IsAction( &ACTIONS::finishInteractive ) )
783 {
784 if( segment || m_busUnfold.in_progress )
785 {
786 finishSegments( aCommit );
787 segment = nullptr;
788
789 aCommit.Push( _( "Draw Wires" ) );
790
791 if( aQuitOnDraw )
792 {
793 m_frame->PopTool( aTool );
794 break;
795 }
796 }
797 }
798 //------------------------------------------------------------------------
799 // Handle click:
800 //
801 else if( evt->IsClick( BUT_LEFT )
802 || ( segment && evt->IsDblClick( BUT_LEFT ) )
803 || isSyntheticClick )
804 {
805 // First click when unfolding places the label and wire-to-bus entry
806 if( m_busUnfold.in_progress && !m_busUnfold.label_placed )
807 {
808 wxASSERT( aType == LAYER_WIRE );
809
810 m_frame->AddToScreen( m_busUnfold.label, screen );
811 m_selectionTool->RemoveItemFromSel( m_busUnfold.label, true );
812 m_busUnfold.label_placed = true;
813 }
814
815 if( !segment )
816 {
817 segment = startSegments( aCommit, aType, VECTOR2D( cursorPos ) );
818 }
819 // Create a new segment if we're out of previously-created ones
820 else if( !segment->IsNull()
821 || ( twoSegments && !m_wires[m_wires.size() - 2]->IsNull() ) )
822 {
823 // Terminate the command if the end point is on a pin, junction, label, or another
824 // wire or bus.
825 if( screen->IsTerminalPoint( cursorPos, segment->GetLayer() ) )
826 {
827 finishSegments( aCommit );
828 segment = nullptr;
829
830 aCommit.Push( _( "Draw Wires" ) );
831
832 if( aQuitOnDraw )
833 {
834 m_frame->PopTool( aTool );
835 break;
836 }
837 }
838 else
839 {
840 int placedSegments = 1;
841
842 // When placing lines with the forty-five degree end, the user is
843 // targetting the endpoint with the angled portion, so it's more
844 // intuitive to place both segments at the same time.
845 if( currentMode == LINE_MODE::LINE_MODE_45 )
846 placedSegments++;
847
848 segment->SetEndPoint( cursorPos );
849
850 for( int i = 0; i < placedSegments; i++ )
851 {
852 // Create a new segment, and chain it after the current segment.
853 segment = static_cast<SCH_LINE*>( segment->Duplicate( true, &aCommit ) );
854 segment->SetFlags( IS_NEW | IS_MOVING );
855 segment->SetStartPoint( cursorPos );
856 m_wires.push_back( segment );
857
858 m_selectionTool->AddItemToSel( segment, true /*quiet mode*/ );
859 }
860 }
861 }
862
863 if( evt->IsDblClick( BUT_LEFT ) && segment )
864 {
865 if( twoSegments && m_wires.size() >= 2 )
866 {
867 computeBreakPoint( { m_wires[m_wires.size() - 2], segment }, cursorPos,
868 currentMode, posture );
869 }
870
871 finishSegments( aCommit );
872 segment = nullptr;
873
874 aCommit.Push( _( "Draw Wires" ) );
875
876 if( aQuitOnDraw )
877 {
878 m_frame->PopTool( aTool );
879 break;
880 }
881 }
882 }
883 //------------------------------------------------------------------------
884 // Handle motion:
885 //
886 else if( evt->IsMotion() || evt->IsAction( &ACTIONS::refreshPreview ) )
887 {
888 m_view->ClearPreview();
889
890 // Update the bus unfold posture based on the mouse movement
891 if( m_busUnfold.in_progress && !m_busUnfold.label_placed )
892 {
893 VECTOR2I cursor_delta = cursorPos - m_busUnfold.origin;
894 SCH_BUS_WIRE_ENTRY* entry = m_busUnfold.entry;
895
896 bool flipX = ( cursor_delta.x < 0 );
897 bool flipY = ( cursor_delta.y < 0 );
898
899 // Erase and redraw if necessary
900 if( flipX != m_busUnfold.flipX || flipY != m_busUnfold.flipY )
901 {
902 VECTOR2I size = entry->GetSize();
903 int ySign = flipY ? -1 : 1;
904 int xSign = flipX ? -1 : 1;
905
906 size.x = std::abs( size.x ) * xSign;
907 size.y = std::abs( size.y ) * ySign;
908 entry->SetSize( size );
909
910 m_busUnfold.flipY = flipY;
911 m_busUnfold.flipX = flipX;
912
913 m_frame->UpdateItem( entry, false, true );
914 m_wires.front()->SetStartPoint( entry->GetEnd() );
915 }
916
917 // Update the label "ghost" position
918 m_busUnfold.label->SetPosition( cursorPos );
919 m_view->AddToPreview( m_busUnfold.label->Clone() );
920
921 // Ensure segment is non-null at the start of bus unfold
922 if( !segment )
923 segment = m_wires.back();
924 }
925
926 if( segment )
927 {
928 // Coerce the line to vertical/horizontal/45 as necessary
929 if( twoSegments && m_wires.size() >= 2 )
930 {
931 computeBreakPoint( { m_wires[m_wires.size() - 2], segment }, cursorPos,
932 currentMode, posture );
933 }
934 else
935 {
936 segment->SetEndPoint( cursorPos );
937 }
938 }
939
940 for( SCH_LINE* wire : m_wires )
941 {
942 if( !wire->IsNull() )
943 m_view->AddToPreview( wire->Clone() );
944 }
945
946 std::vector<SCH_ITEM*> previewItems;
947
948 for( SCH_LINE* wire : m_wires )
949 {
950 if( !wire->IsNull() )
951 previewItems.push_back( wire );
952 }
953
954 if( m_busUnfold.entry )
955 previewItems.push_back( m_busUnfold.entry );
956
958 previewItems ) )
959 {
960 m_view->AddToPreview( jct, true );
961 }
962 }
963 else if( evt->IsAction( &SCH_ACTIONS::undoLastSegment )
964 || evt->IsAction( &ACTIONS::doDelete )
965 || evt->IsAction( &ACTIONS::undo ) )
966 {
967 if( ( currentMode == LINE_MODE::LINE_MODE_FREE && m_wires.size() > 1 )
968 || ( LINE_MODE::LINE_MODE_90 && m_wires.size() > 2 ) )
969 {
970 m_view->ClearPreview();
971
972 m_wires.pop_back();
973 m_selectionTool->RemoveItemFromSel( segment );
974 delete segment;
975
976 segment = m_wires.back();
977 cursorPos = segment->GetEndPoint();
978 getViewControls()->WarpMouseCursor( cursorPos, true );
979
980 // Find new bend point for current mode
981 if( twoSegments && m_wires.size() >= 2 )
982 {
983 computeBreakPoint( { m_wires[m_wires.size() - 2], segment }, cursorPos,
984 currentMode, posture );
985 }
986 else
987 {
988 segment->SetEndPoint( cursorPos );
989 }
990
991 for( SCH_LINE* wire : m_wires )
992 {
993 if( !wire->IsNull() )
994 m_view->AddToPreview( wire->Clone() );
995 }
996 }
997 else if( evt->IsAction( &ACTIONS::undo ) )
998 {
999 // Dispatch as normal undo event
1000 evt->SetPassEvent();
1001 }
1002 else
1003 {
1004 wxBell();
1005 }
1006 }
1007 else if( evt->IsAction( &SCH_ACTIONS::switchSegmentPosture ) && m_wires.size() >= 2 )
1008 {
1009 posture = !posture;
1010
1011 // The 90 degree mode doesn't have a forced posture like
1012 // the 45 degree mode and computeBreakPoint maintains existing 90s' postures.
1013 // Instead, just swap the 90 angle here.
1014 if( currentMode == LINE_MODE::LINE_MODE_90 )
1015 {
1016 m_view->ClearPreview();
1017
1018 SCH_LINE* line2 = m_wires[m_wires.size() - 1];
1019 SCH_LINE* line1 = m_wires[m_wires.size() - 2];
1020
1021 VECTOR2I delta2 = line2->GetEndPoint() - line2->GetStartPoint();
1022 VECTOR2I delta1 = line1->GetEndPoint() - line1->GetStartPoint();
1023
1024 line2->SetStartPoint(line2->GetEndPoint() - delta1);
1025 line1->SetEndPoint(line1->GetStartPoint() + delta2);
1026
1027 for( SCH_LINE* wire : m_wires )
1028 {
1029 if( !wire->IsNull() )
1030 m_view->AddToPreview( wire->Clone() );
1031 }
1032 }
1033 else
1034 {
1035 computeBreakPoint( { m_wires[m_wires.size() - 2], segment }, cursorPos, currentMode,
1036 posture );
1037
1038 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1039 }
1040 }
1041 //------------------------------------------------------------------------
1042 // Handle context menu:
1043 //
1044 else if( evt->IsClick( BUT_RIGHT ) )
1045 {
1046 // Warp after context menu only if dragging...
1047 if( !segment )
1048 m_toolMgr->VetoContextMenuMouseWarp();
1049
1050 contextMenuPos = cursorPos;
1051 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
1052 }
1053 else if( evt->Category() == TC_COMMAND && evt->Action() == TA_CHOICE_MENU_CHOICE )
1054 {
1055 if( *evt->GetCommandId() >= ID_POPUP_SCH_UNFOLD_BUS
1056 && *evt->GetCommandId() <= ID_POPUP_SCH_UNFOLD_BUS_END )
1057 {
1058 wxASSERT_MSG( !segment, "Bus unfold event received when already drawing!" );
1059
1060 aType = LAYER_WIRE;
1061 wxString net = *evt->Parameter<wxString*>();
1062 segment = doUnfoldBus( aCommit, net, contextMenuPos );
1063 }
1064 }
1065 //------------------------------------------------------------------------
1066 // Handle TOOL_ACTION special cases
1067 //
1068 else if( evt->IsAction( &SCH_ACTIONS::rotateCW ) || evt->IsAction( &SCH_ACTIONS::rotateCCW ) )
1069 {
1070 if( m_busUnfold.in_progress )
1071 {
1072 m_busUnfold.label->Rotate90( evt->IsAction( &SCH_ACTIONS::rotateCW ) );
1073 busUnfoldPersistentSettings.label_spin_style = m_busUnfold.label->GetSpinStyle();
1074
1075 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1076 }
1077 else
1078 {
1079 wxBell();
1080 }
1081 }
1082 else if( evt->IsAction( &ACTIONS::redo ) )
1083 {
1084 wxBell();
1085 }
1086 else
1087 {
1088 evt->SetPassEvent();
1089 }
1090
1091 // Enable autopanning and cursor capture only when there is a segment to be placed
1092 controls->SetAutoPan( segment != nullptr );
1093 controls->CaptureCursor( segment != nullptr );
1094 }
1095
1096 controls->SetAutoPan( false );
1097 controls->CaptureCursor( false );
1098 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1099 controls->ForceCursorPosition( false );
1100 return 0;
1101}
1102
1103
1105 SCH_LINE* aSegment )
1106{
1107 if( !aSegment )
1108 aSegment = m_frame->GetScreen()->GetLine( aPos, 0, aType );
1109
1110 if( !aSegment )
1111 {
1112 switch( aType )
1113 {
1114 default: aSegment = new SCH_LINE( aPos, LAYER_NOTES ); break;
1115 case LAYER_WIRE: aSegment = new SCH_LINE( aPos, LAYER_WIRE ); break;
1116 case LAYER_BUS: aSegment = new SCH_LINE( aPos, LAYER_BUS ); break;
1117 }
1118
1119 // Give segments a parent so they find the default line/wire/bus widths
1120 aSegment->SetParent( &m_frame->Schematic() );
1121 }
1122 else
1123 {
1124 aSegment = static_cast<SCH_LINE*>( aSegment->Duplicate( true, &aCommit ) );
1125 aSegment->SetStartPoint( aPos );
1126 }
1127
1128
1129 aSegment->SetFlags( IS_NEW | IS_MOVING );
1130 m_wires.push_back( aSegment );
1131
1132 m_selectionTool->AddItemToSel( aSegment, true /*quiet mode*/ );
1133
1134 // We need 2 segments to go from a given start pin to an end point when the
1135 // horizontal and vertical lines only switch is on.
1136 if( m_frame->eeconfig()->m_Drawing.line_mode )
1137 {
1138 aSegment = static_cast<SCH_LINE*>( aSegment->Duplicate( true, &aCommit ) );
1139 aSegment->SetFlags( IS_NEW | IS_MOVING );
1140 m_wires.push_back( aSegment );
1141
1142 m_selectionTool->AddItemToSel( aSegment, true /*quiet mode*/ );
1143 }
1144
1145 return aSegment;
1146}
1147
1148
1163{
1164 for( auto it = m_wires.begin(); it != m_wires.end(); )
1165 {
1166 SCH_LINE* line = *it;
1167
1168 if( line->IsNull() )
1169 {
1170 delete line;
1171 it = m_wires.erase( it );
1172 continue;
1173 }
1174
1175 auto next_it = it;
1176 ++next_it;
1177
1178 if( next_it == m_wires.end() )
1179 break;
1180
1181 SCH_LINE* next_line = *next_it;
1182
1183 if( SCH_LINE* merged = line->MergeOverlap( m_frame->GetScreen(), next_line, false ) )
1184 {
1185 delete line;
1186 delete next_line;
1187 it = m_wires.erase( it );
1188 *it = merged;
1189 }
1190
1191 ++it;
1192 }
1193}
1194
1195
1197{
1198 // Clear selection when done so that a new wire can be started.
1199 // NOTE: this must be done before simplifyWireList is called or we might end up with
1200 // freed selected items.
1201 m_toolMgr->RunAction( ACTIONS::selectionClear );
1202
1203 SCH_SCREEN* screen = m_frame->GetScreen();
1204
1205 // Remove segments backtracking over others
1207
1208 // Collect the possible connection points for the new lines
1209 std::vector<VECTOR2I> connections = screen->GetConnections();
1210 std::vector<VECTOR2I> new_ends;
1211
1212 // Check each new segment for possible junctions and add/split if needed
1213 for( SCH_LINE* wire : m_wires )
1214 {
1215 if( wire->HasFlag( SKIP_STRUCT ) )
1216 continue;
1217
1218 std::vector<VECTOR2I> tmpends = wire->GetConnectionPoints();
1219
1220 new_ends.insert( new_ends.end(), tmpends.begin(), tmpends.end() );
1221
1222 for( const VECTOR2I& pt : connections )
1223 {
1224 if( IsPointOnSegment( wire->GetStartPoint(), wire->GetEndPoint(), pt ) )
1225 new_ends.push_back( pt );
1226 }
1227
1228 aCommit.Added( wire, screen );
1229 }
1230
1231 if( m_busUnfold.in_progress && m_busUnfold.label_placed )
1232 {
1233 wxASSERT( m_busUnfold.entry && m_busUnfold.label );
1234
1235 aCommit.Added( m_busUnfold.entry, screen );
1236 m_frame->SaveCopyForRepeatItem( m_busUnfold.entry );
1237
1238 aCommit.Added( m_busUnfold.label, screen );
1239 m_frame->AddCopyForRepeatItem( m_busUnfold.label );
1240 m_busUnfold.label->ClearEditFlags();
1241
1242 if( !m_wires.empty() )
1243 m_frame->AddCopyForRepeatItem( m_wires[0] );
1244 }
1245 else if( !m_wires.empty() )
1246 {
1247 m_frame->SaveCopyForRepeatItem( m_wires[0] );
1248 }
1249
1250 for( size_t ii = 1; ii < m_wires.size(); ++ii )
1251 m_frame->AddCopyForRepeatItem( m_wires[ii] );
1252
1253 // Add the new wires
1254 for( SCH_LINE* wire : m_wires )
1255 {
1256 wire->ClearFlags( IS_NEW | IS_MOVING );
1257 m_frame->AddToScreen( wire, screen );
1258 }
1259
1260 m_wires.clear();
1261 m_view->ClearPreview();
1262 m_view->ShowPreview( false );
1263
1264 getViewControls()->CaptureCursor( false );
1265 getViewControls()->SetAutoPan( false );
1266
1267 // Correct and remove segments that need to be merged.
1268 m_frame->Schematic().CleanUp( &aCommit );
1269
1270 std::vector<SCH_ITEM*> symbols;
1271
1272 for( SCH_ITEM* symbol : m_frame->GetScreen()->Items().OfType( SCH_SYMBOL_T ) )
1273 symbols.push_back( symbol );
1274
1275 for( SCH_ITEM* symbol : symbols )
1276 {
1277 std::vector<VECTOR2I> pts = symbol->GetConnectionPoints();
1278
1279 if( pts.size() > 2 )
1280 continue;
1281
1282 for( auto pt = pts.begin(); pt != pts.end(); pt++ )
1283 {
1284 for( auto secondPt = pt + 1; secondPt != pts.end(); secondPt++ )
1285 m_frame->TrimWire( &aCommit, *pt, *secondPt );
1286 }
1287 }
1288
1289 for( const VECTOR2I& pt : new_ends )
1290 {
1291 if( m_frame->GetScreen()->IsExplicitJunctionNeeded( pt ) )
1292 AddJunction( &aCommit, m_frame->GetScreen(), pt );
1293 }
1294
1295 if( m_busUnfold.in_progress )
1296 m_busUnfold = {};
1297
1298 for( SCH_ITEM* item : m_frame->GetScreen()->Items() )
1299 item->ClearEditFlags();
1300}
1301
1302
1304{
1306 SCH_SCREEN* screen = sch->CurrentSheet().LastScreen();
1307
1308 std::set<SCH_LINE*> lines;
1309 BOX2I bb = aSelection->GetBoundingBox();
1310
1311 for( EDA_ITEM* item : screen->Items().Overlapping( SCH_LINE_T, bb ) )
1312 lines.insert( static_cast<SCH_LINE*>( item ) );
1313
1314 for( unsigned ii = 0; ii < aSelection->GetSize(); ii++ )
1315 {
1316 SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( aSelection->GetItem( ii ) );
1317
1318 if( !item || !item->IsConnectable() || ( item->Type() == SCH_LINE_T ) )
1319 continue;
1320
1321 std::vector<VECTOR2I> pts = item->GetConnectionPoints();
1322
1325 for( SCH_LINE* line : lines )
1326 {
1327 std::vector<VECTOR2I> conn_pts;
1328
1329 for( const VECTOR2I& pt : pts )
1330 {
1331 if( IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), pt ) )
1332 conn_pts.push_back( pt );
1333
1334 if( conn_pts.size() > 2 )
1335 break;
1336 }
1337
1338 if( conn_pts.size() == 2 )
1339 m_frame->TrimWire( aCommit, conn_pts[0], conn_pts[1] );
1340 }
1341 }
1342
1343 return 0;
1344}
1345
1346
1348{
1349 SCH_SCREEN* screen = m_frame->GetScreen();
1350 std::deque<EDA_ITEM*> allItems;
1351
1352 for( EDA_ITEM* item : aSelection->Items() )
1353 {
1354 allItems.push_back( item );
1355
1356 if( item->Type() == SCH_GROUP_T )
1357 {
1358 static_cast<SCH_GROUP*>( item )->RunOnChildren(
1359 [&]( SCH_ITEM* child )
1360 {
1361 allItems.push_back( child );
1363 }
1364 }
1365
1366 for( const VECTOR2I& point : screen->GetNeededJunctions( allItems ) )
1367 AddJunction( aCommit, m_frame->GetScreen(), point );
1368
1369 return 0;
1370}
1371
1372
1373void SCH_LINE_WIRE_BUS_TOOL::BreakSegment( SCH_COMMIT* aCommit, SCH_LINE* aSegment, const VECTOR2I& aPoint,
1374 SCH_LINE** aNewSegment, SCH_SCREEN* aScreen )
1375{
1376 // Save the copy of aSegment before breaking it
1377 aCommit->Modify( aSegment, aScreen );
1378
1379 SCH_LINE* newSegment = aSegment->BreakAt( aCommit, aPoint );
1380
1381 aSegment->SetFlags( IS_CHANGED | IS_BROKEN );
1382 newSegment->SetFlags( IS_NEW | IS_BROKEN );
1383 m_frame->AddToScreen( newSegment, aScreen );
1384
1385 aCommit->Added( newSegment, aScreen );
1386
1387 *aNewSegment = newSegment;
1388}
1389
1390
1392{
1393 bool brokenSegments = false;
1394 SCH_LINE* new_line;
1395
1396 for( SCH_LINE* wire : aScreen->GetBusesAndWires( aPos, true ) )
1397 {
1398 BreakSegment( aCommit, wire, aPos, &new_line, aScreen );
1399 brokenSegments = true;
1400 }
1401
1402 return brokenSegments;
1403}
1404
1405
1407{
1408 bool brokenSegments = false;
1409
1410 std::set<VECTOR2I> point_set;
1411
1412 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
1413 point_set.insert( item->GetPosition() );
1414
1415 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_BUS_WIRE_ENTRY_T ) )
1416 {
1417 SCH_BUS_WIRE_ENTRY* entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
1418 point_set.insert( entry->GetPosition() );
1419 point_set.insert( entry->GetEnd() );
1420 }
1421
1422 for( const VECTOR2I& pt : point_set )
1423 {
1424 BreakSegments( aCommit, pt, aScreen );
1425 brokenSegments = true;
1426 }
1427
1428 return brokenSegments;
1429}
1430
1431
1433 const VECTOR2I& aPos )
1434{
1435 SCH_JUNCTION* junction = new SCH_JUNCTION( aPos );
1436
1437 m_frame->AddToScreen( junction, aScreen );
1438 aCommit->Added( junction, aScreen );
1439
1440 BreakSegments( aCommit, aPos, aScreen );
1441
1442 return junction;
1443}
1444
1445
const char * name
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
static TOOL_ACTION undo
Definition actions.h:75
static TOOL_ACTION doDelete
Definition actions.h:85
static TOOL_ACTION redo
Definition actions.h:76
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:223
static TOOL_ACTION refreshPreview
Definition actions.h:158
static TOOL_ACTION finishInteractive
Definition actions.h:73
ACTION_MENU(bool isContextMenu, TOOL_INTERACTIVE *aTool=nullptr)
Default constructor.
void Clear()
Remove all the entries from the menu (as well as its title).
void SetTitle(const wxString &aTitle) override
Set title for the menu.
void SetTool(TOOL_INTERACTIVE *aTool)
Set a tool that is the creator of the menu.
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
TOOL_INTERACTIVE * m_tool
Creator of the menu.
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
bool PassHelpTextToHandler() override
BUS_UNFOLD_MENU(BUS_GETTER aBusGetter)
void update() override
Update menu state stub.
COMMIT & Added(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
Definition commit.h:84
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:106
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
static void sort_dangling_end_items(std::vector< DANGLING_END_ITEM > &aItemListByType, std::vector< DANGLING_END_ITEM > &aItemListByPos)
Both contain the same information.
Definition sch_item.cpp:796
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:142
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.h:113
bool IsNew() const
Definition eda_item.h:124
EE_TYPE Overlapping(const BOX2I &aRect) const
Definition sch_rtree.h:246
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:241
An interface for classes handling user events controlling the view behavior such as zooming,...
virtual void CaptureCursor(bool aEnabled)
Force the cursor to stay within the drawing panel area.
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
virtual void ShowCursor(bool aEnabled)
Enable or disables display of cursor.
virtual void WarpMouseCursor(const VECTOR2D &aPosition, bool aWorldCoordinates=false, bool aWarpView=false)=0
If enabled (.
virtual void SetCrossHairCursorPosition(const VECTOR2D &aPosition, bool aWarpView=true)=0
Move the graphic crosshair cursor to the requested position expressed in world coordinates.
virtual VECTOR2D GetMousePosition(bool aWorldCoordinates=true) const =0
Return the current mouse pointer position.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
These are loaded from Eeschema settings but then overwritten by the project settings.
Holds all the data relating to one schematic.
Definition schematic.h:88
SCH_SHEET_PATH & CurrentSheet() const
Definition schematic.h:171
static TOOL_ACTION rotateCCW
static TOOL_ACTION placeClassLabel
Definition sch_actions.h:79
static TOOL_ACTION clearHighlight
static TOOL_ACTION placeGlobalLabel
Definition sch_actions.h:80
static TOOL_ACTION leaveSheet
static TOOL_ACTION breakWire
static TOOL_ACTION drawLines
Definition sch_actions.h:99
static TOOL_ACTION placeHierLabel
Definition sch_actions.h:81
static TOOL_ACTION selectConnection
If current selection is a wire or bus, expand to entire connection.
Definition sch_actions.h:51
static TOOL_ACTION placeLabel
Definition sch_actions.h:78
static TOOL_ACTION drawWire
Definition sch_actions.h:72
static TOOL_ACTION rotateCW
static TOOL_ACTION placeJunction
Definition sch_actions.h:76
static TOOL_ACTION selectNode
Select the junction, wire or bus segment under the cursor.
Definition sch_actions.h:47
static TOOL_ACTION unfoldBus
Definition sch_actions.h:74
static TOOL_ACTION drawBus
Definition sch_actions.h:73
static TOOL_ACTION slice
static TOOL_ACTION undoLastSegment
static TOOL_ACTION switchSegmentPosture
VECTOR2I GetSize() const
void SetSize(const VECTOR2I &aSize)
VECTOR2I GetPosition() const override
VECTOR2I GetEnd() const
Class for a wire to bus entry.
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Execute the changes.
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
bool IsBus() const
const std::vector< std::shared_ptr< SCH_CONNECTION > > & Members() const
static wxString PrintBusForUI(const wxString &aString)
Schematic editor (Eeschema) main window.
const wxString & GetHighlightedConnection() const
A set of SCH_ITEMs (i.e., without duplicates).
Definition sch_group.h:52
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:167
SCH_ITEM * Duplicate(bool addToParentGroup, SCH_COMMIT *aCommit=nullptr, bool doClone=false) const
Routine to create a new copy of given item.
Definition sch_item.cpp:137
virtual bool IsConnectable() const
Definition sch_item.h:493
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition sch_item.h:309
SCH_CONNECTION * Connection(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieve the connection associated with this object in the given sheet.
Definition sch_item.cpp:323
virtual std::vector< VECTOR2I > GetConnectionPoints() const
Add all the connection points for this item to aPoints.
Definition sch_item.h:508
void computeBreakPoint(const std::pair< SCH_LINE *, SCH_LINE * > &aSegments, VECTOR2I &aPosition, LINE_MODE mode, bool posture)
Compute the middle coordinate for 2 segments from the start point to aPosition with the segments kept...
int DrawSegments(const TOOL_EVENT &aEvent)
bool BreakSegmentsOnJunctions(SCH_COMMIT *aCommit, SCH_SCREEN *aScreen)
Test all junctions and bus entries in the schematic for intersections with wires and buses and breaks...
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
int AddJunctionsIfNeeded(SCH_COMMIT *aCommit, SCH_SELECTION *aSelection)
Handle the addition of junctions to a selection of objects.
SCH_JUNCTION * AddJunction(SCH_COMMIT *aCommit, SCH_SCREEN *aScreen, const VECTOR2I &aPos)
int UnfoldBus(const TOOL_EVENT &aEvent)
SCH_LINE * startSegments(SCH_COMMIT &aCommit, int aType, const VECTOR2D &aPos, SCH_LINE *aSegment=nullptr)
bool Init() override
Init() is called once upon a registration of the tool.
int TrimOverLappingWires(SCH_COMMIT *aCommit, SCH_SELECTION *aSelection)
Logic to remove wires when overlapping correct items.
const SCH_SHEET_PIN * getSheetPin(const VECTOR2I &aPosition)
Search for a sheet pin at a location.
SCH_LINE * doUnfoldBus(SCH_COMMIT &aCommit, const wxString &aNet, const std::optional< VECTOR2I > &aPos=std::nullopt)
Unfold the given bus from the given position.
SCH_LINE * getBusForUnfolding()
Choose a bus to unfold based on the current tool selection.
bool BreakSegments(SCH_COMMIT *aCommit, const VECTOR2I &aPoint, SCH_SCREEN *aScreen)
Check every wire and bus for a intersection at aPoint and break into two segments at aPoint if an int...
std::vector< SCH_LINE * > m_wires
static bool IsDrawingLineWireOrBus(const SELECTION &aSelection)
void BreakSegment(SCH_COMMIT *aCommit, SCH_LINE *aSegment, const VECTOR2I &aPoint, SCH_LINE **aNewSegment, SCH_SCREEN *aScreen)
Break a single segment into two at the specified point.
void simplifyWireList()
Iterate over the wire list and removes the null segments and overlapping segments to create a simplif...
int doDrawSegments(const TOOL_EVENT &aTool, SCH_COMMIT &aCommit, int aType, bool aQuitOnDraw)
void finishSegments(SCH_COMMIT &aCommit)
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:42
void SetStartPoint(const VECTOR2I &aPosition)
Definition sch_line.h:140
VECTOR2I GetEndPoint() const
Definition sch_line.h:144
VECTOR2I GetStartPoint() const
Definition sch_line.h:139
SEG GetSeg() const
Get the geometric aspect of the wire as a SEG.
Definition sch_line.h:150
SCH_LINE * MergeOverlap(SCH_SCREEN *aScreen, SCH_LINE *aLine, bool aCheckJunctions)
Check line against aLine to see if it overlaps and merge if it does.
Definition sch_line.cpp:429
bool IsNull() const
Definition sch_line.h:137
SCH_LINE * BreakAt(SCH_COMMIT *aCommit, const VECTOR2I &aPoint)
Break this segment into two at the specified point.
Definition sch_line.cpp:550
void SetEndPoint(const VECTOR2I &aPosition)
Definition sch_line.h:145
std::vector< SCH_LINE * > GetBusesAndWires(const VECTOR2I &aPosition, bool aIgnoreEndpoints=false) const
Return buses and wires passing through aPosition.
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:117
bool IsTerminalPoint(const VECTOR2I &aPosition, int aLayer) const
Test if aPosition is a connection point on aLayer.
std::vector< VECTOR2I > GetNeededJunctions(const std::deque< EDA_ITEM * > &aItems) const
Return the unique set of points belonging to aItems where a junction is needed.
std::vector< VECTOR2I > GetConnections() const
Collect a unique list of all possible connection points in the schematic.
BOX2I GetBoundingBox() const override
SCH_SCREEN * LastScreen()
Define a sheet pin (label) used in sheets to create hierarchical schematics.
SHEET_SIDE GetSide() const
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:47
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition sch_sheet.h:187
VECTOR2I GetPosition() const override
Definition sch_text.h:141
bool Init() override
Init() is called once upon a registration of the tool.
SCH_TOOL_BASE(const std::string &aName)
SCH_SELECTION_TOOL * m_selectionTool
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition seg.cpp:604
static SELECTION_CONDITION MoreThan(int aNumber)
Create a functor that tests if the number of selected items is greater than the value given as parame...
static bool Idle(const SELECTION &aSelection)
Test if there no items selected or being edited.
static bool ShowAlways(const SELECTION &aSelection)
The default condition function (always returns true).
static SELECTION_CONDITION OnlyTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if the selected items are only of given types.
virtual KIGFX::VIEW_ITEM * GetItem(unsigned int aIdx) const override
Definition selection.cpp:75
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition selection.h:105
EDA_ITEM * Front() const
Definition selection.h:177
std::deque< EDA_ITEM * > & Items()
Definition selection.h:182
SCH_EDIT_FRAME * getModel() const
Definition tool_base.h:198
KIGFX::VIEW_CONTROLS * getViewControls() const
Definition tool_base.cpp:44
KIGFX::VIEW * getView() const
Definition tool_base.cpp:38
Generic, UI-independent tool event.
Definition tool_event.h:171
bool HasPosition() const
Returns if it this event has a valid position (true for mouse events and context-menu or hotkey-based...
Definition tool_event.h:260
bool DisableGridSnapping() const
Definition tool_event.h:371
const VECTOR2D Position() const
Return mouse cursor position in world coordinates.
Definition tool_event.h:293
int Modifier(int aMask=MD_MODIFIER_MASK) const
Return information about key modifiers state (Ctrl, Alt, etc.).
Definition tool_event.h:366
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:473
void SetContextMenu(ACTION_MENU *aMenu, CONTEXT_MENU_TRIGGER aTrigger=CMENU_BUTTON)
void Go(int(SCH_EDIT_FRAME::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
std::unique_ptr< TOOL_MENU > m_menu
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
@ LINE_BUS
Definition cursors.h:88
@ LINE_WIRE
Definition cursors.h:92
@ ARROW
Definition cursors.h:46
@ LINE_GRAPHIC
Definition cursors.h:90
#define _(s)
@ RECURSE
Definition eda_item.h:51
#define IS_CHANGED
Item was edited, and modified.
#define IS_NEW
New item, just created.
#define IS_BROKEN
Is a segment just broken by BreakSegment.
#define SKIP_STRUCT
flag indicating that the structure should be ignored
#define IS_MOVING
Item being moved.
@ ID_POPUP_SCH_UNFOLD_BUS_END
Definition eeschema_id.h:86
@ ID_POPUP_SCH_UNFOLD_BUS
Definition eeschema_id.h:85
@ LINE_MODE_90
@ LINE_MODE_45
@ LINE_MODE_FREE
GRID_HELPER_GRIDS
Definition grid_helper.h:43
@ GRID_GRAPHICS
Definition grid_helper.h:51
@ GRID_WIRES
Definition grid_helper.h:48
@ LAYER_WIRE
Definition layer_ids.h:451
@ LAYER_NOTES
Definition layer_ids.h:466
@ LAYER_BUS
Definition layer_ids.h:452
std::vector< SCH_JUNCTION * > PreviewJunctions(const class SCH_SCREEN *aScreen, const std::vector< class SCH_ITEM * > &aItems)
Determine the points where explicit junctions would be required if the given temporary items were com...
bool signbit(T v)
Integral version of std::signbit that works all compilers.
Definition kicad_algo.h:176
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
@ BUS
This item represents a bus vector.
Class to handle a set of SCH_ITEMs.
std::function< SCH_LINE *()> BUS_GETTER
static BUS_UNFOLD_PERSISTENT_SETTINGS busUnfoldPersistentSettings
SHEET_SIDE
Define the edge of the sheet that the sheet pin is positioned.
Settings for bus unfolding that are persistent across invocations of the tool.
int delta
@ CMENU_NOW
Right now (after TOOL_INTERACTIVE::SetContextMenu).
Definition tool_event.h:156
@ TA_CHOICE_MENU_CHOICE
Context menu choice.
Definition tool_event.h:98
@ TA_CHOICE_MENU_CLOSED
Context menu is closed, no matter whether anything has been chosen or not.
Definition tool_event.h:101
@ TC_COMMAND
Definition tool_event.h:57
@ MD_SHIFT
Definition tool_event.h:143
@ BUT_LEFT
Definition tool_event.h:132
@ BUT_RIGHT
Definition tool_event.h:133
bool IsPointOnSegment(const VECTOR2I &aSegStart, const VECTOR2I &aSegEnd, const VECTOR2I &aTestPoint)
Test if aTestPoint is on line defined by aSegStart and aSegEnd.
Definition trigo.cpp:89
@ SCH_GROUP_T
Definition typeinfo.h:175
@ SCH_LINE_T
Definition typeinfo.h:165
@ SCH_SYMBOL_T
Definition typeinfo.h:174
@ SCH_SHEET_T
Definition typeinfo.h:177
@ SCH_ITEM_LOCATE_BUS_T
Definition typeinfo.h:189
@ SCH_BUS_WIRE_ENTRY_T
Definition typeinfo.h:163
@ SCH_JUNCTION_T
Definition typeinfo.h:161
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
VECTOR2< double > VECTOR2D
Definition vector2d.h:694