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