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