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 m_frame->PushTool( aEvent );
347
348 if( aEvent.HasPosition() )
349 {
351 GRID_HELPER_GRIDS gridType = ( params->layer == LAYER_NOTES ) ? GRID_GRAPHICS : GRID_WIRES;
352
353 grid.SetSnap( !aEvent.Modifier( MD_SHIFT ) );
354 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !aEvent.DisableGridSnapping() );
355
356 VECTOR2D cursorPos = grid.BestSnapAnchor( aEvent.Position(), gridType, nullptr );
357 startSegments( commit, params->layer, cursorPos, params->sourceSegment );
358 }
359
360 return doDrawSegments( aEvent, commit, params->layer, params->quitOnDraw );
361}
362
363
365{
366 if( m_inDrawingTool )
367 return 0;
368
370
371 SCH_COMMIT commit( m_toolMgr );
372 wxString* netPtr = aEvent.Parameter<wxString*>();
373 wxString net;
374 SCH_LINE* segment = nullptr;
375
376 m_frame->PushTool( aEvent );
377 Activate();
378
379 if( netPtr )
380 {
381 net = *netPtr;
382 delete netPtr;
383 }
384 else
385 {
386 const auto busGetter = [this]()
387 {
388 return getBusForUnfolding();
389 };
390 BUS_UNFOLD_MENU unfoldMenu( busGetter );
391 unfoldMenu.SetTool( this );
392 unfoldMenu.SetShowTitle();
393
394 SetContextMenu( &unfoldMenu, CMENU_NOW );
395
396 while( TOOL_EVENT* evt = Wait() )
397 {
398 if( evt->Action() == TA_CHOICE_MENU_CHOICE )
399 {
400 std::optional<int> id = evt->GetCommandId();
401
402 if( id && ( *id > 0 ) )
403 net = *evt->Parameter<wxString*>();
404
405 break;
406 }
407 else if( evt->Action() == TA_CHOICE_MENU_CLOSED )
408 {
409 break;
410 }
411 else
412 {
413 evt->SetPassEvent();
414 }
415 }
416 }
417
418 // Break a wire for the given net out of the bus
419 if( !net.IsEmpty() )
420 segment = doUnfoldBus( commit, net );
421
422 // If we have an unfolded wire to draw, then draw it
423 if( segment )
424 {
425 return doDrawSegments( aEvent, commit, LAYER_WIRE, false );
426 }
427 else
428 {
429 m_frame->PopTool( aEvent );
430 return 0;
431 }
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.BestSnapAnchor( eventPosition, gridType, segment );
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 {
795 m_frame->PopTool( aTool );
796 break;
797 }
798 }
799 else
800 {
801 m_frame->PopTool( aTool );
802 break;
803 }
804 }
805 else if( evt->IsActivate() && !isSyntheticClick )
806 {
807 if( segment || m_busUnfold.in_progress )
808 {
809 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel drawing." ) );
810 evt->SetPassEvent( false );
811 continue;
812 }
813
814 if( evt->IsMoveTool() )
815 {
816 // leave ourselves on the stack so we come back after the move
817 break;
818 }
819 else
820 {
821 m_frame->PopTool( aTool );
822 break;
823 }
824 }
825 //------------------------------------------------------------------------
826 // Handle finish:
827 //
828 else if( evt->IsAction( &ACTIONS::finishInteractive ) )
829 {
830 if( segment || m_busUnfold.in_progress )
831 {
832 finishSegments( aCommit );
833 segment = nullptr;
834
835 aCommit.Push( _( "Draw Wires" ) );
836
837 if( aQuitOnDraw )
838 {
839 m_frame->PopTool( aTool );
840 break;
841 }
842 }
843 }
844 //------------------------------------------------------------------------
845 // Handle click:
846 //
847 else if( evt->IsClick( BUT_LEFT )
848 || ( segment && evt->IsDblClick( BUT_LEFT ) )
849 || isSyntheticClick )
850 {
851 // First click when unfolding places the label and wire-to-bus entry
852 if( m_busUnfold.in_progress && !m_busUnfold.label_placed )
853 {
854 wxASSERT( aType == LAYER_WIRE );
855
856 m_frame->AddToScreen( m_busUnfold.label, screen );
857 m_selectionTool->RemoveItemFromSel( m_busUnfold.label, true );
858 m_busUnfold.label_placed = true;
859 }
860
861 if( !segment )
862 {
863 segment = startSegments( aCommit, aType, VECTOR2D( cursorPos ) );
864 }
865 // Create a new segment if we're out of previously-created ones
866 else if( !segment->IsNull()
867 || ( twoSegments && !m_wires[m_wires.size() - 2]->IsNull() ) )
868 {
869 // Terminate the command if the end point is on a pin, junction, label, or another
870 // wire or bus.
871 if( screen->IsTerminalPoint( cursorPos, segment->GetLayer() ) )
872 {
873 finishSegments( aCommit );
874 segment = nullptr;
875
876 aCommit.Push( _( "Draw Wires" ) );
877
878 if( aQuitOnDraw )
879 {
880 m_frame->PopTool( aTool );
881 break;
882 }
883 }
884 else
885 {
886 int placedSegments = 1;
887
888 // When placing lines with the forty-five degree end, the user is
889 // targetting the endpoint with the angled portion, so it's more
890 // intuitive to place both segments at the same time.
891 if( currentMode == LINE_MODE::LINE_MODE_45 )
892 placedSegments++;
893
894 segment->SetEndPoint( cursorPos );
895
896 for( int i = 0; i < placedSegments; i++ )
897 {
898 // Create a new segment, and chain it after the current segment.
899 segment = static_cast<SCH_LINE*>( segment->Duplicate( true, &aCommit ) );
900 segment->SetFlags( IS_NEW | IS_MOVING );
901 segment->SetStartPoint( cursorPos );
902 m_wires.push_back( segment );
903
904 m_selectionTool->AddItemToSel( segment, true /*quiet mode*/ );
905 }
906 }
907 }
908
909 if( evt->IsDblClick( BUT_LEFT ) && segment )
910 {
911 if( twoSegments && m_wires.size() >= 2 )
912 {
913 computeBreakPoint( { m_wires[m_wires.size() - 2], segment }, cursorPos,
914 currentMode, posture );
915 }
916
917 finishSegments( aCommit );
918 segment = nullptr;
919
920 aCommit.Push( _( "Draw Wires" ) );
921
922 if( aQuitOnDraw )
923 {
924 m_frame->PopTool( aTool );
925 break;
926 }
927 }
928 }
929 //------------------------------------------------------------------------
930 // Handle motion:
931 //
932 else if( evt->IsMotion() || evt->IsAction( &ACTIONS::refreshPreview ) )
933 {
934 m_view->ClearPreview();
935
936 // Update the bus unfold posture based on the mouse movement
937 if( m_busUnfold.in_progress && !m_busUnfold.label_placed )
938 {
939 VECTOR2I cursor_delta = cursorPos - m_busUnfold.origin;
940 SCH_BUS_WIRE_ENTRY* entry = m_busUnfold.entry;
941
942 bool flipX = ( cursor_delta.x < 0 );
943 bool flipY = ( cursor_delta.y < 0 );
944
945 // Erase and redraw if necessary
946 if( flipX != m_busUnfold.flipX || flipY != m_busUnfold.flipY )
947 {
948 VECTOR2I size = entry->GetSize();
949 int ySign = flipY ? -1 : 1;
950 int xSign = flipX ? -1 : 1;
951
952 size.x = std::abs( size.x ) * xSign;
953 size.y = std::abs( size.y ) * ySign;
954 entry->SetSize( size );
955
956 m_busUnfold.flipY = flipY;
957 m_busUnfold.flipX = flipX;
958
959 m_frame->UpdateItem( entry, false, true );
960 m_wires.front()->SetStartPoint( entry->GetEnd() );
961 }
962
963 // Update the label "ghost" position
964 m_busUnfold.label->SetPosition( cursorPos );
965 m_view->AddToPreview( m_busUnfold.label->Clone() );
966
967 // Ensure segment is non-null at the start of bus unfold
968 if( !segment )
969 segment = m_wires.back();
970 }
971
972 if( segment )
973 {
974 // Coerce the line to vertical/horizontal/45 as necessary
975 if( twoSegments && m_wires.size() >= 2 )
976 {
977 computeBreakPoint( { m_wires[m_wires.size() - 2], segment }, cursorPos,
978 currentMode, posture );
979 }
980 else
981 {
982 segment->SetEndPoint( cursorPos );
983 }
984 }
985
986 for( SCH_LINE* wire : m_wires )
987 {
988 if( !wire->IsNull() )
989 m_view->AddToPreview( wire->Clone() );
990 }
991
992 std::vector<SCH_ITEM*> previewItems;
993
994 for( SCH_LINE* wire : m_wires )
995 {
996 if( !wire->IsNull() )
997 previewItems.push_back( wire );
998 }
999
1000 if( m_busUnfold.entry )
1001 previewItems.push_back( m_busUnfold.entry );
1002
1004 previewItems ) )
1005 {
1006 m_view->AddToPreview( jct, true );
1007 }
1008 }
1009 else if( evt->IsAction( &SCH_ACTIONS::undoLastSegment )
1010 || evt->IsAction( &ACTIONS::doDelete )
1011 || evt->IsAction( &ACTIONS::undo ) )
1012 {
1013 if( ( currentMode == LINE_MODE::LINE_MODE_FREE && m_wires.size() > 1 )
1014 || ( LINE_MODE::LINE_MODE_90 && m_wires.size() > 2 ) )
1015 {
1016 m_view->ClearPreview();
1017
1018 m_wires.pop_back();
1019 m_selectionTool->RemoveItemFromSel( segment );
1020 delete segment;
1021
1022 segment = m_wires.back();
1023 cursorPos = segment->GetEndPoint();
1024 getViewControls()->WarpMouseCursor( cursorPos, true );
1025
1026 // Find new bend point for current mode
1027 if( twoSegments && m_wires.size() >= 2 )
1028 {
1029 computeBreakPoint( { m_wires[m_wires.size() - 2], segment }, cursorPos,
1030 currentMode, posture );
1031 }
1032 else
1033 {
1034 segment->SetEndPoint( cursorPos );
1035 }
1036
1037 for( SCH_LINE* wire : m_wires )
1038 {
1039 if( !wire->IsNull() )
1040 m_view->AddToPreview( wire->Clone() );
1041 }
1042 }
1043 else if( evt->IsAction( &ACTIONS::undo ) )
1044 {
1045 // Dispatch as normal undo event
1046 evt->SetPassEvent();
1047 }
1048 else
1049 {
1050 wxBell();
1051 }
1052 }
1053 else if( evt->IsAction( &SCH_ACTIONS::switchSegmentPosture ) && m_wires.size() >= 2 )
1054 {
1055 posture = !posture;
1056
1057 // The 90 degree mode doesn't have a forced posture like
1058 // the 45 degree mode and computeBreakPoint maintains existing 90s' postures.
1059 // Instead, just swap the 90 angle here.
1060 if( currentMode == LINE_MODE::LINE_MODE_90 )
1061 {
1062 m_view->ClearPreview();
1063
1064 SCH_LINE* line2 = m_wires[m_wires.size() - 1];
1065 SCH_LINE* line1 = m_wires[m_wires.size() - 2];
1066
1067 VECTOR2I delta2 = line2->GetEndPoint() - line2->GetStartPoint();
1068 VECTOR2I delta1 = line1->GetEndPoint() - line1->GetStartPoint();
1069
1070 line2->SetStartPoint(line2->GetEndPoint() - delta1);
1071 line1->SetEndPoint(line1->GetStartPoint() + delta2);
1072
1073 for( SCH_LINE* wire : m_wires )
1074 {
1075 if( !wire->IsNull() )
1076 m_view->AddToPreview( wire->Clone() );
1077 }
1078 }
1079 else
1080 {
1081 computeBreakPoint( { m_wires[m_wires.size() - 2], segment }, cursorPos, currentMode,
1082 posture );
1083
1084 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1085 }
1086 }
1087 //------------------------------------------------------------------------
1088 // Handle context menu:
1089 //
1090 else if( evt->IsClick( BUT_RIGHT ) )
1091 {
1092 // Warp after context menu only if dragging...
1093 if( !segment )
1094 m_toolMgr->VetoContextMenuMouseWarp();
1095
1096 contextMenuPos = cursorPos;
1097 m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
1098 }
1099 else if( evt->Category() == TC_COMMAND && evt->Action() == TA_CHOICE_MENU_CHOICE )
1100 {
1101 if( *evt->GetCommandId() >= ID_POPUP_SCH_UNFOLD_BUS
1102 && *evt->GetCommandId() <= ID_POPUP_SCH_UNFOLD_BUS_END )
1103 {
1104 wxASSERT_MSG( !segment, "Bus unfold event received when already drawing!" );
1105
1106 aType = LAYER_WIRE;
1107 wxString net = *evt->Parameter<wxString*>();
1108 segment = doUnfoldBus( aCommit, net, contextMenuPos );
1109 }
1110 }
1111 //------------------------------------------------------------------------
1112 // Handle TOOL_ACTION special cases
1113 //
1114 else if( evt->IsAction( &SCH_ACTIONS::rotateCW ) || evt->IsAction( &SCH_ACTIONS::rotateCCW ) )
1115 {
1116 if( m_busUnfold.in_progress )
1117 {
1118 m_busUnfold.label->Rotate90( evt->IsAction( &SCH_ACTIONS::rotateCW ) );
1119 busUnfoldPersistentSettings.label_spin_style = m_busUnfold.label->GetSpinStyle();
1120
1121 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1122 }
1123 else
1124 {
1125 wxBell();
1126 }
1127 }
1128 else if( evt->IsAction( &ACTIONS::redo ) )
1129 {
1130 wxBell();
1131 }
1132 else
1133 {
1134 evt->SetPassEvent();
1135 }
1136
1137 // Enable autopanning and cursor capture only when there is a segment to be placed
1138 controls->SetAutoPan( segment != nullptr );
1139 controls->CaptureCursor( segment != nullptr );
1140 }
1141
1142 controls->SetAutoPan( false );
1143 controls->CaptureCursor( false );
1144 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1145 controls->ForceCursorPosition( false );
1146 return 0;
1147}
1148
1149
1151 SCH_LINE* aSegment )
1152{
1153 if( !aSegment )
1154 aSegment = m_frame->GetScreen()->GetLine( aPos, 0, aType );
1155
1156 if( !aSegment )
1157 {
1158 switch( aType )
1159 {
1160 default: aSegment = new SCH_LINE( aPos, LAYER_NOTES ); break;
1161 case LAYER_WIRE: aSegment = new SCH_LINE( aPos, LAYER_WIRE ); break;
1162 case LAYER_BUS: aSegment = new SCH_LINE( aPos, LAYER_BUS ); break;
1163 }
1164
1165 // Give segments a parent so they find the default line/wire/bus widths
1166 aSegment->SetParent( &m_frame->Schematic() );
1167 }
1168 else
1169 {
1170 aSegment = static_cast<SCH_LINE*>( aSegment->Duplicate( true, &aCommit ) );
1171 aSegment->SetStartPoint( aPos );
1172 }
1173
1174
1175 aSegment->SetFlags( IS_NEW | IS_MOVING );
1176 m_wires.push_back( aSegment );
1177
1178 m_selectionTool->AddItemToSel( aSegment, true /*quiet mode*/ );
1179
1180 // We need 2 segments to go from a given start pin to an end point when the
1181 // horizontal and vertical lines only switch is on.
1182 if( m_frame->eeconfig()->m_Drawing.line_mode )
1183 {
1184 aSegment = static_cast<SCH_LINE*>( aSegment->Duplicate( true, &aCommit ) );
1185 aSegment->SetFlags( IS_NEW | IS_MOVING );
1186 m_wires.push_back( aSegment );
1187
1188 m_selectionTool->AddItemToSel( aSegment, true /*quiet mode*/ );
1189 }
1190
1191 return aSegment;
1192}
1193
1194
1209{
1210 for( auto it = m_wires.begin(); it != m_wires.end(); )
1211 {
1212 SCH_LINE* line = *it;
1213
1214 if( line->IsNull() )
1215 {
1216 delete line;
1217 it = m_wires.erase( it );
1218 continue;
1219 }
1220
1221 auto next_it = it;
1222 ++next_it;
1223
1224 if( next_it == m_wires.end() )
1225 break;
1226
1227 SCH_LINE* next_line = *next_it;
1228
1229 if( SCH_LINE* merged = line->MergeOverlap( m_frame->GetScreen(), next_line, false ) )
1230 {
1231 delete line;
1232 delete next_line;
1233 it = m_wires.erase( it );
1234 *it = merged;
1235 }
1236
1237 ++it;
1238 }
1239}
1240
1241
1243{
1244 // Clear selection when done so that a new wire can be started.
1245 // NOTE: this must be done before simplifyWireList is called or we might end up with
1246 // freed selected items.
1247 m_toolMgr->RunAction( ACTIONS::selectionClear );
1248
1249 SCH_SCREEN* screen = m_frame->GetScreen();
1250
1251 // Remove segments backtracking over others
1253
1254 // Collect the possible connection points for the new lines
1255 std::vector<VECTOR2I> connections = screen->GetConnections();
1256 std::vector<VECTOR2I> new_ends;
1257
1258 // Check each new segment for possible junctions and add/split if needed
1259 for( SCH_LINE* wire : m_wires )
1260 {
1261 if( wire->HasFlag( SKIP_STRUCT ) )
1262 continue;
1263
1264 std::vector<VECTOR2I> tmpends = wire->GetConnectionPoints();
1265
1266 new_ends.insert( new_ends.end(), tmpends.begin(), tmpends.end() );
1267
1268 for( const VECTOR2I& pt : connections )
1269 {
1270 if( IsPointOnSegment( wire->GetStartPoint(), wire->GetEndPoint(), pt ) )
1271 new_ends.push_back( pt );
1272 }
1273
1274 aCommit.Added( wire, screen );
1275 }
1276
1277 if( m_busUnfold.in_progress && m_busUnfold.label_placed )
1278 {
1279 wxASSERT( m_busUnfold.entry && m_busUnfold.label );
1280
1281 aCommit.Added( m_busUnfold.entry, screen );
1282 m_frame->SaveCopyForRepeatItem( m_busUnfold.entry );
1283
1284 aCommit.Added( m_busUnfold.label, screen );
1285 m_frame->AddCopyForRepeatItem( m_busUnfold.label );
1286 m_busUnfold.label->ClearEditFlags();
1287
1288 if( !m_wires.empty() )
1289 m_frame->AddCopyForRepeatItem( m_wires[0] );
1290 }
1291 else if( !m_wires.empty() )
1292 {
1293 m_frame->SaveCopyForRepeatItem( m_wires[0] );
1294 }
1295
1296 for( size_t ii = 1; ii < m_wires.size(); ++ii )
1297 m_frame->AddCopyForRepeatItem( m_wires[ii] );
1298
1299 // Add the new wires
1300 for( SCH_LINE* wire : m_wires )
1301 {
1302 wire->ClearFlags( IS_NEW | IS_MOVING );
1303 m_frame->AddToScreen( wire, screen );
1304 }
1305
1306 m_wires.clear();
1307 m_view->ClearPreview();
1308 m_view->ShowPreview( false );
1309
1310 getViewControls()->CaptureCursor( false );
1311 getViewControls()->SetAutoPan( false );
1312
1313 // Correct and remove segments that need to be merged.
1314 m_frame->Schematic().CleanUp( &aCommit );
1315
1316 std::vector<SCH_ITEM*> symbols;
1317
1318 for( SCH_ITEM* symbol : m_frame->GetScreen()->Items().OfType( SCH_SYMBOL_T ) )
1319 symbols.push_back( symbol );
1320
1321 for( SCH_ITEM* symbol : symbols )
1322 {
1323 std::vector<VECTOR2I> pts = symbol->GetConnectionPoints();
1324
1325 if( pts.size() > 2 )
1326 continue;
1327
1328 for( auto pt = pts.begin(); pt != pts.end(); pt++ )
1329 {
1330 for( auto secondPt = pt + 1; secondPt != pts.end(); secondPt++ )
1331 m_frame->TrimWire( &aCommit, *pt, *secondPt );
1332 }
1333 }
1334
1335 for( const VECTOR2I& pt : new_ends )
1336 {
1337 if( m_frame->GetScreen()->IsExplicitJunctionNeeded( pt ) )
1338 AddJunction( &aCommit, m_frame->GetScreen(), pt );
1339 }
1340
1341 if( m_busUnfold.in_progress )
1342 m_busUnfold = {};
1343
1344 for( SCH_ITEM* item : m_frame->GetScreen()->Items() )
1345 item->ClearEditFlags();
1346}
1347
1348
1350{
1352 SCH_SCREEN* screen = sch->CurrentSheet().LastScreen();
1353
1354 std::set<SCH_LINE*> lines;
1355 BOX2I bb = aSelection->GetBoundingBox();
1356
1357 for( EDA_ITEM* item : screen->Items().Overlapping( SCH_LINE_T, bb ) )
1358 lines.insert( static_cast<SCH_LINE*>( item ) );
1359
1360 for( unsigned ii = 0; ii < aSelection->GetSize(); ii++ )
1361 {
1362 SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( aSelection->GetItem( ii ) );
1363
1364 if( !item || !item->IsConnectable() || ( item->Type() == SCH_LINE_T ) )
1365 continue;
1366
1367 std::vector<VECTOR2I> pts = item->GetConnectionPoints();
1368
1371 for( SCH_LINE* line : lines )
1372 {
1373 std::vector<VECTOR2I> conn_pts;
1374
1375 for( const VECTOR2I& pt : pts )
1376 {
1377 if( IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), pt ) )
1378 conn_pts.push_back( pt );
1379
1380 if( conn_pts.size() > 2 )
1381 break;
1382 }
1383
1384 if( conn_pts.size() == 2 )
1385 m_frame->TrimWire( aCommit, conn_pts[0], conn_pts[1] );
1386 }
1387 }
1388
1389 return 0;
1390}
1391
1392
1394{
1395 SCH_SCREEN* screen = m_frame->GetScreen();
1396 std::deque<EDA_ITEM*> allItems;
1397
1398 for( EDA_ITEM* item : aSelection->Items() )
1399 {
1400 allItems.push_back( item );
1401
1402 if( item->Type() == SCH_GROUP_T )
1403 {
1404 static_cast<SCH_GROUP*>( item )->RunOnChildren(
1405 [&]( SCH_ITEM* child )
1406 {
1407 allItems.push_back( child );
1409 }
1410 }
1411
1412 for( const VECTOR2I& point : screen->GetNeededJunctions( allItems ) )
1413 {
1414 wxLogTrace( "KICAD_SCH_MOVE", "AddJunctionsIfNeeded: adding junction at %s", point.Format().c_str() );
1415 AddJunction( aCommit, m_frame->GetScreen(), point );
1416 }
1417
1418 return 0;
1419}
1420
1421
1422void SCH_LINE_WIRE_BUS_TOOL::BreakSegment( SCH_COMMIT* aCommit, SCH_LINE* aSegment, const VECTOR2I& aPoint,
1423 SCH_LINE** aNewSegment, SCH_SCREEN* aScreen )
1424{
1425 // Save the copy of aSegment before breaking it
1426 aCommit->Modify( aSegment, aScreen );
1427
1428 SCH_LINE* newSegment = aSegment->BreakAt( aCommit, aPoint );
1429
1430 aSegment->SetFlags( IS_CHANGED | IS_BROKEN );
1431 newSegment->SetFlags( IS_NEW | IS_BROKEN );
1432 m_frame->AddToScreen( newSegment, aScreen );
1433
1434 aCommit->Added( newSegment, aScreen );
1435
1436 *aNewSegment = newSegment;
1437}
1438
1439
1441{
1442 bool brokenSegments = false;
1443 SCH_LINE* new_line;
1444
1445 for( SCH_LINE* wire : aScreen->GetBusesAndWires( aPos, true ) )
1446 {
1447 BreakSegment( aCommit, wire, aPos, &new_line, aScreen );
1448 brokenSegments = true;
1449 }
1450
1451 return brokenSegments;
1452}
1453
1454
1456{
1457 bool brokenSegments = false;
1458
1459 std::set<VECTOR2I> point_set;
1460
1461 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
1462 point_set.insert( item->GetPosition() );
1463
1464 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_BUS_WIRE_ENTRY_T ) )
1465 {
1466 SCH_BUS_WIRE_ENTRY* entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
1467 point_set.insert( entry->GetPosition() );
1468 point_set.insert( entry->GetEnd() );
1469 }
1470
1471 for( const VECTOR2I& pt : point_set )
1472 {
1473 BreakSegments( aCommit, pt, aScreen );
1474 brokenSegments = true;
1475 }
1476
1477 return brokenSegments;
1478}
1479
1480
1482 const VECTOR2I& aPos )
1483{
1484 SCH_JUNCTION* junction = new SCH_JUNCTION( aPos );
1485
1486 if( aScreen->GetBus( aPos ) )
1487 junction->SetLayer( LAYER_BUS_JUNCTION );
1488
1489 m_frame->AddToScreen( junction, aScreen );
1490 aCommit->Added( junction, aScreen );
1491
1492 BreakSegments( aCommit, aPos, aScreen );
1493
1494 return junction;
1495}
1496
1497
const char * name
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
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.
Definition sch_item.cpp:980
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:152
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:89
bool IsNew() const
Definition eda_item.h:129
EE_TYPE Overlapping(const BOX2I &aRect) const
Definition sch_rtree.h:226
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:221
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:90
SCH_SHEET_PATH & CurrentSheet() const
Definition schematic.h:189
static TOOL_ACTION rotateCCW
static TOOL_ACTION placeClassLabel
Definition sch_actions.h:75
static TOOL_ACTION clearHighlight
static TOOL_ACTION placeGlobalLabel
Definition sch_actions.h:76
static TOOL_ACTION leaveSheet
static TOOL_ACTION breakWire
static TOOL_ACTION drawLines
Definition sch_actions.h:97
static TOOL_ACTION placeHierLabel
Definition sch_actions.h:77
static TOOL_ACTION selectConnection
If current selection is a wire or bus, expand to entire connection.
Definition sch_actions.h:47
static TOOL_ACTION placeLabel
Definition sch_actions.h:74
static TOOL_ACTION drawWire
Definition sch_actions.h:68
static TOOL_ACTION rotateCW
static TOOL_ACTION placeJunction
Definition sch_actions.h:72
static TOOL_ACTION selectNode
Select the junction, wire or bus segment under the cursor.
Definition sch_actions.h:43
static TOOL_ACTION unfoldBus
Definition sch_actions.h:70
static TOOL_ACTION drawBus
Definition sch_actions.h:69
static TOOL_ACTION slice
static TOOL_ACTION undoLastSegment
Definition sch_actions.h:99
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:162
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:160
virtual bool IsConnectable() const
Definition sch_item.h:524
void SetLayer(SCH_LAYER_ID aLayer)
Definition sch_item.h:339
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition sch_item.h:338
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:487
virtual std::vector< VECTOR2I > GetConnectionPoints() const
Add all the connection points for this item to aPoints.
Definition sch_item.h:539
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:38
void SetStartPoint(const VECTOR2I &aPosition)
Definition sch_line.h:136
VECTOR2I GetEndPoint() const
Definition sch_line.h:144
VECTOR2I GetStartPoint() const
Definition sch_line.h:135
SEG GetSeg() const
Get the geometric aspect of the wire as a SEG.
Definition sch_line.h:154
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:495
bool IsNull() const
Definition sch_line.h:133
SCH_LINE * BreakAt(SCH_COMMIT *aCommit, const VECTOR2I &aPoint)
Break this segment into two at the specified point.
Definition sch_line.cpp:616
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:115
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:448
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:44
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition sch_sheet.h:227
VECTOR2I GetPosition() const override
Definition sch_text.h:146
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:629
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:71
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition selection.h:101
EDA_ITEM * Front() const
Definition selection.h:173
std::deque< EDA_ITEM * > & Items()
Definition selection.h:178
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:49
#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:40
@ GRID_GRAPHICS
Definition grid_helper.h:48
@ GRID_WIRES
Definition grid_helper.h:45
@ LAYER_WIRE
Definition layer_ids.h:450
@ LAYER_NOTES
Definition layer_ids.h:465
@ LAYER_BUS
Definition layer_ids.h:451
@ LAYER_BUS_JUNCTION
Definition layer_ids.h:496
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: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.
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:170
@ SCH_LINE_T
Definition typeinfo.h:160
@ SCH_SYMBOL_T
Definition typeinfo.h:169
@ SCH_SHEET_T
Definition typeinfo.h:172
@ SCH_ITEM_LOCATE_BUS_T
Definition typeinfo.h:184
@ SCH_BUS_WIRE_ENTRY_T
Definition typeinfo.h:158
@ SCH_JUNCTION_T
Definition typeinfo.h:156
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682