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