Schematic Drag: fixes and improvements

Fixes:
* Various special cases around junctions on pins and dragging.

* Some rotations of endpoints resulting in a 45 degree rotate.

* Some cases where it was possible to get a line with neither
  endpoint selected, and also substractive unselecting only one of two
  selected endpoints unselecting both.

* Use line midpoint for sorting.  Start and endpoints aren't
  consistent in the order they appear on the X or Y axis. So,
  we need to use the midpoint for our position for consistent
  sorting when dragging groups of parallel lines where some
  have the start and end reversed.

Other:
* Rename TEMP_SELECTED TO SELECTED_BY_DRAG. This is the actual meaning
  of the flag, and should reduce confusion as
  to when it should be used.

* Move usage of TEMP_SELECTED as an algorithmic mark to CANDIDATE
  instead.

* Fix mistaken clearing of START_POINT and ENDPOINT.

* Move endpoint setting and clearing out of narrowSelection, and into
  selectPoint and selectMultiple.

* Don't show dangling end warnings on new lines
This commit is contained in:
Mike Williams 2022-03-03 09:35:57 -05:00
parent 4c225cff5b
commit e51ab86225
12 changed files with 180 additions and 116 deletions

View File

@ -256,6 +256,7 @@ public:
VECTOR2I GetPosition() const override { return m_start; } VECTOR2I GetPosition() const override { return m_start; }
void SetPosition( const VECTOR2I& aPosition ) override; void SetPosition( const VECTOR2I& aPosition ) override;
VECTOR2I GetSortPosition() const override { return GetMidPoint(); }
bool IsPointClickableAnchor( const VECTOR2I& aPos ) const override bool IsPointClickableAnchor( const VECTOR2I& aPos ) const override
{ {

View File

@ -1602,6 +1602,11 @@ void SCH_PAINTER::draw( const SCH_LINE *aLine, int aLayer )
if( drawingShadows && !( aLine->IsBrightened() || aLine->IsSelected() ) ) if( drawingShadows && !( aLine->IsBrightened() || aLine->IsSelected() ) )
return; return;
// Line end dangling status isn't updated until the line is finished drawing,
// so don't warn them about ends that are probably connected
if( aLine->IsNew() && drawingDangling )
return;
COLOR4D color = getRenderColor( aLine, aLine->GetLayer(), drawingShadows ); COLOR4D color = getRenderColor( aLine, aLine->GetLayer(), drawingShadows );
float width = getLineWidth( aLine, drawingShadows ); float width = getLineWidth( aLine, drawingShadows );
PLOT_DASH_TYPE lineStyle = aLine->GetEffectiveLineStyle(); PLOT_DASH_TYPE lineStyle = aLine->GetEffectiveLineStyle();

View File

@ -1010,9 +1010,9 @@ void EE_POINT_EDITOR::updateParentItem() const
if( connected.first ) if( connected.first )
{ {
if( connected.second == STARTPOINT ) if( connected.second == STARTPOINT )
static_cast<SCH_LINE*>( connected.first )->SetStartPoint( line->GetPosition() ); static_cast<SCH_LINE*>( connected.first )->SetStartPoint( line->GetStartPoint() );
else if( connected.second == ENDPOINT ) else if( connected.second == ENDPOINT )
static_cast<SCH_LINE*>( connected.first )->SetEndPoint( line->GetPosition() ); static_cast<SCH_LINE*>( connected.first )->SetEndPoint( line->GetStartPoint() );
updateItem( connected.first, true ); updateItem( connected.first, true );
} }

View File

@ -377,7 +377,7 @@ int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
// Collect items at the clicked location (doesn't select them yet) // Collect items at the clicked location (doesn't select them yet)
EE_COLLECTOR collector; EE_COLLECTOR collector;
CollectHits( collector, evt->Position() ); CollectHits( collector, evt->Position() );
narrowSelection( collector, evt->Position(), false, false ); narrowSelection( collector, evt->Position(), false );
if( collector.GetCount() == 1 && !m_isSymbolEditor && !modifier_enabled ) if( collector.GetCount() == 1 && !m_isSymbolEditor && !modifier_enabled )
{ {
@ -405,7 +405,8 @@ int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
if( !selCancelled ) if( !selCancelled )
{ {
selectPoint( collector, nullptr, nullptr, m_additive, m_subtractive, m_exclusive_or ); selectPoint( collector, evt->Position(), nullptr, nullptr, m_additive,
m_subtractive, m_exclusive_or );
m_selection.SetIsHover( false ); m_selection.SetIsHover( false );
} }
} }
@ -580,7 +581,7 @@ int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
if( CollectHits( collector, evt->Position() ) ) if( CollectHits( collector, evt->Position() ) )
{ {
narrowSelection( collector, evt->Position(), false, false ); narrowSelection( collector, evt->Position(), false );
if( collector.GetCount() == 1 && !modifier_enabled ) if( collector.GetCount() == 1 && !modifier_enabled )
{ {
@ -797,7 +798,7 @@ bool EE_SELECTION_TOOL::CollectHits( EE_COLLECTOR& aCollector, const VECTOR2I& a
void EE_SELECTION_TOOL::narrowSelection( EE_COLLECTOR& collector, const VECTOR2I& aWhere, void EE_SELECTION_TOOL::narrowSelection( EE_COLLECTOR& collector, const VECTOR2I& aWhere,
bool aCheckLocked, bool aSelectPoints ) bool aCheckLocked )
{ {
for( int i = collector.GetCount() - 1; i >= 0; --i ) for( int i = collector.GetCount() - 1; i >= 0; --i )
{ {
@ -812,20 +813,6 @@ void EE_SELECTION_TOOL::narrowSelection( EE_COLLECTOR& collector, const VECTOR2I
collector.Remove( i ); collector.Remove( i );
continue; continue;
} }
// SelectPoint, unlike other selection routines, can select line ends
if( aSelectPoints && collector[i]->Type() == SCH_LINE_T )
{
SCH_LINE* line = (SCH_LINE*) collector[i];
line->ClearFlags( STARTPOINT | ENDPOINT );
if( HitTestPoints( line->GetStartPoint(), aWhere, collector.m_Threshold ) )
line->SetFlags( STARTPOINT );
else if( HitTestPoints( line->GetEndPoint(), aWhere, collector.m_Threshold ) )
line->SetFlags( ENDPOINT );
else
line->SetFlags( STARTPOINT | ENDPOINT );
}
} }
// Apply some ugly heuristics to avoid disambiguation menus whenever possible // Apply some ugly heuristics to avoid disambiguation menus whenever possible
@ -836,9 +823,9 @@ void EE_SELECTION_TOOL::narrowSelection( EE_COLLECTOR& collector, const VECTOR2I
} }
bool EE_SELECTION_TOOL::selectPoint( EE_COLLECTOR& aCollector, EDA_ITEM** aItem, bool EE_SELECTION_TOOL::selectPoint( EE_COLLECTOR& aCollector, const VECTOR2I& aWhere,
bool* aSelectionCancelledFlag, bool aAdd, bool aSubtract, EDA_ITEM** aItem, bool* aSelectionCancelledFlag, bool aAdd,
bool aExclusiveOr ) bool aSubtract, bool aExclusiveOr )
{ {
m_selection.ClearReferencePoint(); m_selection.ClearReferencePoint();
@ -873,13 +860,33 @@ bool EE_SELECTION_TOOL::selectPoint( EE_COLLECTOR& aCollector, EDA_ITEM** aItem,
{ {
for( int i = 0; i < aCollector.GetCount(); ++i ) for( int i = 0; i < aCollector.GetCount(); ++i )
{ {
EDA_ITEM_FLAGS flags = 0;
// Handle line ends specially
if( aCollector[i]->Type() == SCH_LINE_T )
{
SCH_LINE* line = (SCH_LINE*) aCollector[i];
if( HitTestPoints( line->GetStartPoint(), aWhere, aCollector.m_Threshold ) )
flags = STARTPOINT;
else if( HitTestPoints( line->GetEndPoint(), aWhere, aCollector.m_Threshold ) )
flags = ENDPOINT;
else
flags = STARTPOINT | ENDPOINT;
}
if( aSubtract || ( aExclusiveOr && aCollector[i]->IsSelected() ) ) if( aSubtract || ( aExclusiveOr && aCollector[i]->IsSelected() ) )
{
aCollector[i]->ClearFlags( flags );
if( !aCollector[i]->HasFlag( STARTPOINT ) && !aCollector[i]->HasFlag( ENDPOINT ) )
{ {
unselect( aCollector[i] ); unselect( aCollector[i] );
anySubtracted = true; anySubtracted = true;
} }
}
else else
{ {
aCollector[i]->SetFlags( flags );
select( aCollector[i] ); select( aCollector[i] );
anyAdded = true; anyAdded = true;
} }
@ -915,9 +922,10 @@ bool EE_SELECTION_TOOL::SelectPoint( const VECTOR2I& aWhere, const KICAD_T* aFil
if( !CollectHits( collector, aWhere, aFilterList ) ) if( !CollectHits( collector, aWhere, aFilterList ) )
return false; return false;
narrowSelection( collector, aWhere, aCheckLocked, true ); narrowSelection( collector, aWhere, aCheckLocked );
return selectPoint( collector, aItem, aSelectionCancelledFlag, aAdd, aSubtract, aExclusiveOr ); return selectPoint( collector, aWhere, aItem, aSelectionCancelledFlag, aAdd, aSubtract,
aExclusiveOr );
} }
@ -1234,7 +1242,7 @@ bool EE_SELECTION_TOOL::selectMultiple()
if( item ) if( item )
{ {
item->ClearFlags( TEMP_SELECTED | STARTPOINT | ENDPOINT ); item->ClearFlags( CANDIDATE );
nearbyItems.push_back( item ); nearbyItems.push_back( item );
} }
@ -1253,40 +1261,44 @@ bool EE_SELECTION_TOOL::selectMultiple()
bool anyAdded = false; bool anyAdded = false;
bool anySubtracted = false; bool anySubtracted = false;
auto selectItem = auto selectItem = [&]( EDA_ITEM* aItem )
[&]( EDA_ITEM* aItem )
{ {
EDA_ITEM_FLAGS flags = 0;
// Handle line ends specially
if( aItem->Type() == SCH_LINE_T )
{
SCH_LINE* line = (SCH_LINE*) aItem;
if( selectionRect.Contains( line->GetStartPoint() ) )
flags |= STARTPOINT;
if( selectionRect.Contains( line->GetEndPoint() ) )
flags |= ENDPOINT;
// If no ends were selected, select whole line (both ends)
// Also select both ends if the selection overlaps the midpoint
if( ( !( flags & STARTPOINT ) && !( flags & ENDPOINT ) )
|| selectionRect.Contains( line->GetMidPoint() ) )
{
flags = STARTPOINT | ENDPOINT;
}
}
if( m_subtractive || ( m_exclusive_or && aItem->IsSelected() ) ) if( m_subtractive || ( m_exclusive_or && aItem->IsSelected() ) )
{
aItem->ClearFlags( flags );
if( !aItem->HasFlag( STARTPOINT ) && !aItem->HasFlag( ENDPOINT ) )
{ {
unselect( aItem ); unselect( aItem );
anySubtracted = true; anySubtracted = true;
} }
}
else else
{ {
aItem->SetFlags( flags );
select( aItem ); select( aItem );
// Lines can have just one end selected
if( aItem->Type() == SCH_LINE_T )
{
SCH_LINE* line = static_cast<SCH_LINE*>( aItem );
line->ClearFlags( STARTPOINT | ENDPOINT );
if( selectionRect.Contains( line->GetStartPoint() ) )
line->SetFlags( STARTPOINT );
if( selectionRect.Contains( line->GetEndPoint() ) )
line->SetFlags( ENDPOINT );
// If no ends were selected, select whole line (both ends)
// Also select both ends if the selection overlaps the midpoint
if( ( !line->HasFlag( STARTPOINT ) && !line->HasFlag( ENDPOINT ) )
|| selectionRect.Contains( line->GetMidPoint() ) )
{
line->SetFlags( STARTPOINT | ENDPOINT );
}
}
anyAdded = true; anyAdded = true;
} }
}; };
@ -1295,7 +1307,7 @@ bool EE_SELECTION_TOOL::selectMultiple()
{ {
if( Selectable( item ) && item->HitTest( selectionRect, isWindowSelection ) ) if( Selectable( item ) && item->HitTest( selectionRect, isWindowSelection ) )
{ {
item->SetFlags( TEMP_SELECTED ); item->SetFlags( CANDIDATE );
selectItem( item ); selectItem( item );
} }
} }
@ -1303,7 +1315,7 @@ bool EE_SELECTION_TOOL::selectMultiple()
for( EDA_ITEM* item : nearbyChildren ) for( EDA_ITEM* item : nearbyChildren )
{ {
if( Selectable( item ) if( Selectable( item )
&& !item->GetParent()->HasFlag( TEMP_SELECTED ) && !item->GetParent()->HasFlag( CANDIDATE )
&& item->HitTest( selectionRect, isWindowSelection ) ) && item->HitTest( selectionRect, isWindowSelection ) )
{ {
selectItem( item ); selectItem( item );
@ -1886,7 +1898,12 @@ void EE_SELECTION_TOOL::unhighlight( EDA_ITEM* aItem, int aMode, EE_SELECTION* a
KICAD_T itemType = aItem->Type(); KICAD_T itemType = aItem->Type();
if( aMode == SELECTED ) if( aMode == SELECTED )
{
aItem->ClearSelected(); aItem->ClearSelected();
// Lines need endpoints cleared here
if( aItem->Type() == SCH_LINE_T )
aItem->ClearFlags( STARTPOINT | ENDPOINT );
}
else if( aMode == BRIGHTENED ) else if( aMode == BRIGHTENED )
aItem->ClearBrightened(); aItem->ClearBrightened();

View File

@ -197,10 +197,8 @@ private:
* @param collector EE_COLLECTOR with elements to filter * @param collector EE_COLLECTOR with elements to filter
* @param aWhere point where we should narrow (if relevant) * @param aWhere point where we should narrow (if relevant)
* @param aCheckLocked If false, remove locked elements from #collector * @param aCheckLocked If false, remove locked elements from #collector
* @param aSelectPoints If true, set STARTPOINT/ENDPOINT flags on individual wires
*/ */
void narrowSelection( EE_COLLECTOR& collector, const VECTOR2I& aWhere, bool aCheckLocked, void narrowSelection( EE_COLLECTOR& collector, const VECTOR2I& aWhere, bool aCheckLocked );
bool aSelectPoints );
/** /**
* This is the primary SelectPoint method that will prompt the user with a menu to disambiguate * This is the primary SelectPoint method that will prompt the user with a menu to disambiguate
@ -208,6 +206,7 @@ private:
* actual selection group. * actual selection group.
* *
* @param aCollector is an EE_COLLECTOR that already has collected items * @param aCollector is an EE_COLLECTOR that already has collected items
* @param aWhere position of the selected point
* @param aItem is set to the newly selected item if only one was selected, otherwise is * @param aItem is set to the newly selected item if only one was selected, otherwise is
* unchanged. * unchanged.
* @param aSelectionCancelledFlag allows the function to inform its caller that a selection * @param aSelectionCancelledFlag allows the function to inform its caller that a selection
@ -217,7 +216,7 @@ private:
* @param aSubtract indicates if found item(s) should be subtracted from the selection * @param aSubtract indicates if found item(s) should be subtracted from the selection
* @param aExclusiveOr indicates if found item(s) should be toggle in the selection * @param aExclusiveOr indicates if found item(s) should be toggle in the selection
*/ */
bool selectPoint( EE_COLLECTOR& aCollector, EDA_ITEM** aItem = nullptr, bool selectPoint( EE_COLLECTOR& aCollector, const VECTOR2I& aWhere, EDA_ITEM** aItem = nullptr,
bool* aSelectionCancelledFlag = nullptr, bool aAdd = false, bool* aSelectionCancelledFlag = nullptr, bool aAdd = false,
bool aSubtract = false, bool aExclusiveOr = false ); bool aSubtract = false, bool aExclusiveOr = false );

View File

@ -139,7 +139,7 @@ protected:
// IS_SELECTED flag should not be set on undo items which were added for // IS_SELECTED flag should not be set on undo items which were added for
// a drag operation. // a drag operation.
if( selected && aItem->HasFlag( TEMP_SELECTED ) ) if( selected && aItem->HasFlag( SELECTED_BY_DRAG ) )
aItem->ClearSelected(); aItem->ClearSelected();
if( m_isSymbolEditor ) if( m_isSymbolEditor )
@ -168,7 +168,7 @@ protected:
} }
} }
if( selected && aItem->HasFlag( TEMP_SELECTED ) ) if( selected && aItem->HasFlag( SELECTED_BY_DRAG ) )
aItem->SetSelected(); aItem->SetSelected();
} }

View File

@ -498,7 +498,7 @@ int SCH_EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
{ {
SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.GetItem( ii ) ); SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.GetItem( ii ) );
if( item->HasFlag( TEMP_SELECTED ) ) if( item->HasFlag( SELECTED_BY_DRAG ) )
continue; continue;
principalItemCount++; principalItemCount++;
@ -652,7 +652,7 @@ int SCH_EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
// We've already rotated the user selected item if there was only one. We're just // We've already rotated the user selected item if there was only one. We're just
// here to rotate the ends of wires that were attached to it. // here to rotate the ends of wires that were attached to it.
if( principalItemCount == 1 && !item->HasFlag( TEMP_SELECTED ) ) if( principalItemCount == 1 && !item->HasFlag( SELECTED_BY_DRAG ) )
continue; continue;
if( !moving ) if( !moving )

View File

@ -189,7 +189,7 @@ int SCH_MOVE_TOOL::Main( const TOOL_EVENT& aEvent )
for( SCH_ITEM* it : m_frame->GetScreen()->Items() ) for( SCH_ITEM* it : m_frame->GetScreen()->Items() )
{ {
it->ClearFlags( TEMP_SELECTED ); it->ClearFlags( SELECTED_BY_DRAG );
if( !it->IsSelected() ) if( !it->IsSelected() )
it->ClearFlags( STARTPOINT | ENDPOINT ); it->ClearFlags( STARTPOINT | ENDPOINT );
@ -268,7 +268,7 @@ int SCH_MOVE_TOOL::Main( const TOOL_EVENT& aEvent )
if( item->IsNew() ) if( item->IsNew() )
{ {
if( item->HasFlag( TEMP_SELECTED ) && m_isDrag ) if( item->HasFlag( SELECTED_BY_DRAG ) && m_isDrag )
{ {
// Item was added in getConnectedDragItems // Item was added in getConnectedDragItems
saveCopyInUndoList( (SCH_ITEM*) item, UNDO_REDO::NEWITEM, appendUndo ); saveCopyInUndoList( (SCH_ITEM*) item, UNDO_REDO::NEWITEM, appendUndo );
@ -448,6 +448,7 @@ int SCH_MOVE_TOOL::Main( const TOOL_EVENT& aEvent )
// Look for pre-existing lines we can drag with us instead of creating new ones // Look for pre-existing lines we can drag with us instead of creating new ones
bool foundAttachment = false; bool foundAttachment = false;
bool foundJunction = false; bool foundJunction = false;
bool foundPin = false;
SCH_LINE* foundLine = nullptr; SCH_LINE* foundLine = nullptr;
for( EDA_ITEM* cItem : m_lineConnectionCache[line] ) for( EDA_ITEM* cItem : m_lineConnectionCache[line] )
{ {
@ -455,7 +456,9 @@ int SCH_MOVE_TOOL::Main( const TOOL_EVENT& aEvent )
// If the move is the same angle as a connected line, // If the move is the same angle as a connected line,
// we can shrink/extend that line endpoint // we can shrink/extend that line endpoint
if( cItem->Type() == SCH_LINE_T ) switch( cItem->Type() )
{
case SCH_LINE_T:
{ {
SCH_LINE* cLine = static_cast<SCH_LINE*>( cItem ); SCH_LINE* cLine = static_cast<SCH_LINE*>( cItem );
@ -469,9 +472,19 @@ int SCH_MOVE_TOOL::Main( const TOOL_EVENT& aEvent )
// when we can (otherwise the zero length line will draw overlapping segments on them) // when we can (otherwise the zero length line will draw overlapping segments on them)
if( foundLine == nullptr && cLine->GetLength() == 0 ) if( foundLine == nullptr && cLine->GetLength() == 0 )
foundLine = cLine; foundLine = cLine;
break;
} }
else if( cItem->Type() == SCH_JUNCTION_T ) case SCH_JUNCTION_T:
foundJunction = true; foundJunction = true;
break;
case SCH_PIN_T:
foundPin = true;
break;
default: break;
}
} }
// Ok... what if our original line is length zero from moving in its direction, // Ok... what if our original line is length zero from moving in its direction,
@ -491,8 +504,9 @@ int SCH_MOVE_TOOL::Main( const TOOL_EVENT& aEvent )
} }
// If we have found an attachment, but not a line, we want to check if it's // If we have found an attachment, but not a line, we want to check if it's
// a junction. These are special-cased and get a single line added instead of a // a junction. These are special-cased and get a single line added instead of a
// 90-degree bend. // 90-degree bend. Except when we're on a pin, because pins always need bends,
else if( !foundLine && foundJunction ) // and junctions are just added to pins for visual clarity.
else if( !foundLine && foundJunction && !foundPin )
{ {
// Create a new wire ending at the unselected end // Create a new wire ending at the unselected end
foundLine = new SCH_LINE( unselectedEnd, line->GetLayer() ); foundLine = new SCH_LINE( unselectedEnd, line->GetLayer() );
@ -884,18 +898,38 @@ void SCH_MOVE_TOOL::getConnectedItems( SCH_ITEM* aOriginalItem, const VECTOR2I&
{ {
EE_RTREE& items = m_frame->GetScreen()->Items(); EE_RTREE& items = m_frame->GetScreen()->Items();
EE_RTREE::EE_TYPE itemsOverlapping = items.Overlapping( aOriginalItem->GetBoundingBox() ); EE_RTREE::EE_TYPE itemsOverlapping = items.Overlapping( aOriginalItem->GetBoundingBox() );
SCH_ITEM* foundJunction = nullptr;
SCH_ITEM* foundSymbol = nullptr;
// If you're connected to a junction, you're only connected to the junction // If you're connected to a junction, you're only connected to the junction.
// (unless you are the junction) //
// But, if you're connected to a junction on a pin, you're only connected to the pin. This
// is because junctions and pins have different logic for how bend lines are generated
// and we need to prioritize the pin version in some cases.
for( SCH_ITEM* item : itemsOverlapping ) for( SCH_ITEM* item : itemsOverlapping )
{ {
if( item != aOriginalItem && item->Type() == SCH_JUNCTION_T && item->IsConnected( aPoint ) ) if( item != aOriginalItem && item->IsConnected( aPoint ) )
{ {
aList.push_back( item ); if( item->Type() == SCH_JUNCTION_T )
foundJunction = item;
else if( item->Type() == SCH_SYMBOL_T )
foundSymbol = item;
}
}
if( foundSymbol && foundJunction )
{
aList.push_back( foundSymbol );
return; return;
} }
if( foundJunction )
{
aList.push_back( foundJunction );
return;
} }
for( SCH_ITEM* test : itemsOverlapping ) for( SCH_ITEM* test : itemsOverlapping )
{ {
if( test == aOriginalItem || !test->CanConnect( aOriginalItem ) ) if( test == aOriginalItem || !test->CanConnect( aOriginalItem ) )
@ -1028,11 +1062,11 @@ void SCH_MOVE_TOOL::getConnectedDragItems( SCH_ITEM* aOriginalItem, const VECTOR
// already been grabbed during the partial selection process. // already been grabbed during the partial selection process.
line->SetFlags( STARTPOINT ); line->SetFlags( STARTPOINT );
if( line->HasFlag( SELECTED ) || line->HasFlag( TEMP_SELECTED ) ) if( line->HasFlag( SELECTED ) || line->HasFlag( SELECTED_BY_DRAG ) )
continue; continue;
else else
{ {
line->SetFlags( TEMP_SELECTED ); line->SetFlags( SELECTED_BY_DRAG );
aList.push_back( line ); aList.push_back( line );
} }
} }
@ -1040,11 +1074,11 @@ void SCH_MOVE_TOOL::getConnectedDragItems( SCH_ITEM* aOriginalItem, const VECTOR
{ {
line->SetFlags( ENDPOINT ); line->SetFlags( ENDPOINT );
if( line->HasFlag( SELECTED ) || line->HasFlag( TEMP_SELECTED ) ) if( line->HasFlag( SELECTED ) || line->HasFlag( SELECTED_BY_DRAG ) )
continue; continue;
else else
{ {
line->SetFlags( TEMP_SELECTED ); line->SetFlags( SELECTED_BY_DRAG );
aList.push_back( line ); aList.push_back( line );
} }
} }
@ -1068,7 +1102,7 @@ void SCH_MOVE_TOOL::getConnectedDragItems( SCH_ITEM* aOriginalItem, const VECTOR
newWire->SetFlags( IS_NEW ); newWire->SetFlags( IS_NEW );
m_frame->AddToScreen( newWire, m_frame->GetScreen() ); m_frame->AddToScreen( newWire, m_frame->GetScreen() );
newWire->SetFlags( TEMP_SELECTED | STARTPOINT ); newWire->SetFlags( SELECTED_BY_DRAG | STARTPOINT );
aList.push_back( newWire ); aList.push_back( newWire );
//We need to add a connection reference here because the normal //We need to add a connection reference here because the normal
@ -1100,12 +1134,12 @@ void SCH_MOVE_TOOL::getConnectedDragItems( SCH_ITEM* aOriginalItem, const VECTOR
if( label->IsSelected() ) if( label->IsSelected() )
continue; // These will be moved on their own because they're selected continue; // These will be moved on their own because they're selected
if( label->HasFlag( TEMP_SELECTED ) ) if( label->HasFlag( SELECTED_BY_DRAG ) )
continue; continue;
if( label->CanConnect( line ) && line->HitTest( label->GetPosition(), 1 ) ) if( label->CanConnect( line ) && line->HitTest( label->GetPosition(), 1 ) )
{ {
label->SetFlags( TEMP_SELECTED ); label->SetFlags( SELECTED_BY_DRAG );
aList.push_back( label ); aList.push_back( label );
SPECIAL_CASE_LABEL_INFO info; SPECIAL_CASE_LABEL_INFO info;
@ -1141,17 +1175,17 @@ void SCH_MOVE_TOOL::getConnectedDragItems( SCH_ITEM* aOriginalItem, const VECTOR
newWire->SetFlags( IS_NEW ); newWire->SetFlags( IS_NEW );
m_frame->AddToScreen( newWire, m_frame->GetScreen() ); m_frame->AddToScreen( newWire, m_frame->GetScreen() );
newWire->SetFlags( TEMP_SELECTED | STARTPOINT ); newWire->SetFlags( SELECTED_BY_DRAG | STARTPOINT );
aList.push_back( newWire ); aList.push_back( newWire );
} }
break; break;
case SCH_NO_CONNECT_T: case SCH_NO_CONNECT_T:
// Select no-connects that are connected to items being moved. // Select no-connects that are connected to items being moved.
if( !test->HasFlag( TEMP_SELECTED ) && test->IsConnected( aPoint ) ) if( !test->HasFlag( SELECTED_BY_DRAG ) && test->IsConnected( aPoint ) )
{ {
aList.push_back( test ); aList.push_back( test );
test->SetFlags( TEMP_SELECTED ); test->SetFlags( SELECTED_BY_DRAG );
} }
break; break;
@ -1161,7 +1195,7 @@ void SCH_MOVE_TOOL::getConnectedDragItems( SCH_ITEM* aOriginalItem, const VECTOR
case SCH_HIER_LABEL_T: case SCH_HIER_LABEL_T:
case SCH_DIRECTIVE_LABEL_T: case SCH_DIRECTIVE_LABEL_T:
// Performance optimization: // Performance optimization:
if( test->HasFlag( TEMP_SELECTED ) ) if( test->HasFlag( SELECTED_BY_DRAG ) )
break; break;
// Select labels that are connected to a wire (or bus) being moved. // Select labels that are connected to a wire (or bus) being moved.
@ -1185,7 +1219,7 @@ void SCH_MOVE_TOOL::getConnectedDragItems( SCH_ITEM* aOriginalItem, const VECTOR
} }
else else
{ {
label->SetFlags( TEMP_SELECTED ); label->SetFlags( SELECTED_BY_DRAG );
aList.push_back( label ); aList.push_back( label );
if( oneEndFixed ) if( oneEndFixed )
@ -1204,7 +1238,7 @@ void SCH_MOVE_TOOL::getConnectedDragItems( SCH_ITEM* aOriginalItem, const VECTOR
case SCH_BUS_WIRE_ENTRY_T: case SCH_BUS_WIRE_ENTRY_T:
case SCH_BUS_BUS_ENTRY_T: case SCH_BUS_BUS_ENTRY_T:
// Performance optimization: // Performance optimization:
if( test->HasFlag( TEMP_SELECTED ) ) if( test->HasFlag( SELECTED_BY_DRAG ) )
break; break;
// Select bus entries that are connected to a bus being moved. // Select bus entries that are connected to a bus being moved.
@ -1224,7 +1258,7 @@ void SCH_MOVE_TOOL::getConnectedDragItems( SCH_ITEM* aOriginalItem, const VECTOR
{ {
if( line->HitTest( point, 1 ) ) if( line->HitTest( point, 1 ) )
{ {
test->SetFlags( TEMP_SELECTED ); test->SetFlags( SELECTED_BY_DRAG );
aList.push_back( test ); aList.push_back( test );
// A bus entry needs its wire & label as well // A bus entry needs its wire & label as well

View File

@ -164,7 +164,7 @@ public:
void ClearTempFlags() void ClearTempFlags()
{ {
ClearFlags( CANDIDATE | TEMP_SELECTED | IS_LINKED | SKIP_STRUCT | DO_NOT_DRAW ); ClearFlags( CANDIDATE | SELECTED_BY_DRAG | IS_LINKED | SKIP_STRUCT | DO_NOT_DRAW );
} }
void ClearEditFlags() void ClearEditFlags()
@ -256,6 +256,15 @@ public:
*/ */
virtual const VECTOR2I GetFocusPosition() const { return GetPosition(); } virtual const VECTOR2I GetFocusPosition() const { return GetPosition(); }
/**
* Return the coordinates that should be used for sorting this element
* visually compared to other elements. For instance, for lines the midpoint
* might be a better sorting point than either end.
*
* @return X,Y coordinate of the sort point
*/
virtual VECTOR2I GetSortPosition() const { return GetPosition(); }
/** /**
* Create a duplicate of this item with linked list members set to NULL. * Create a duplicate of this item with linked list members set to NULL.
* *

View File

@ -45,8 +45,8 @@
#define IS_WIRE_IMAGE (1 << 8) ///< Item to be drawn as wireframe while editing #define IS_WIRE_IMAGE (1 << 8) ///< Item to be drawn as wireframe while editing
#define STARTPOINT (1 << 9) ///< When a line is selected, these flags indicate which #define STARTPOINT (1 << 9) ///< When a line is selected, these flags indicate which
#define ENDPOINT (1 << 10) ///< ends. (Used to support dragging.) #define ENDPOINT (1 << 10) ///< ends. (Used to support dragging.)
#define SELECTED (1 << 11) #define SELECTED (1 << 11) ///< Item was manually selected by the user
#define TEMP_SELECTED (1 << 12) ///< flag indicating that the structure has already selected #define SELECTED_BY_DRAG (1 << 12) ///< Item was algorithmically selected as a dragged item
#define STRUCT_DELETED (1 << 13) ///< flag indication structures to be erased #define STRUCT_DELETED (1 << 13) ///< flag indication structures to be erased
#define CANDIDATE (1 << 14) ///< flag indicating that the structure is connected #define CANDIDATE (1 << 14) ///< flag indicating that the structure is connected
#define SKIP_STRUCT (1 << 15) ///< flag indicating that the structure should be ignored #define SKIP_STRUCT (1 << 15) ///< flag indicating that the structure should be ignored

View File

@ -34,7 +34,6 @@
#include <eda_item.h> #include <eda_item.h>
#include <view/view_group.h> #include <view/view_group.h>
class EDA_ITEM;
class SELECTION : public KIGFX::VIEW_GROUP class SELECTION : public KIGFX::VIEW_GROUP
{ {
@ -126,21 +125,21 @@ public:
std::sort( sorted_items.begin(), sorted_items.end(), [&]( EDA_ITEM* a, EDA_ITEM* b ) { std::sort( sorted_items.begin(), sorted_items.end(), [&]( EDA_ITEM* a, EDA_ITEM* b ) {
if( a->Type() == b->Type() ) if( a->Type() == b->Type() )
{ {
if( a->GetPosition().x == b->GetPosition().x ) if( a->GetSortPosition().x == b->GetSortPosition().x )
{ {
// Ensure deterministic sort // Ensure deterministic sort
if( a->GetPosition().y == b->GetPosition().y ) if( a->GetSortPosition().y == b->GetSortPosition().y )
return a->m_Uuid < b->m_Uuid; return a->m_Uuid < b->m_Uuid;
if( topBeforeBottom ) if( topBeforeBottom )
return a->GetPosition().y < b->GetPosition().y; return a->GetSortPosition().y < b->GetSortPosition().y;
else else
return a->GetPosition().y > b->GetPosition().y; return a->GetSortPosition().y > b->GetSortPosition().y;
} }
else if( leftBeforeRight ) else if( leftBeforeRight )
return a->GetPosition().x < b->GetPosition().x; return a->GetSortPosition().x < b->GetSortPosition().x;
else else
return a->GetPosition().x > b->GetPosition().x; return a->GetSortPosition().x > b->GetSortPosition().x;
} }
else else
return a->Type() < b->Type(); return a->Type() < b->Type();

View File

@ -3016,18 +3016,18 @@ void PCB_SELECTION_TOOL::FilterCollectorForHierarchy( GENERAL_COLLECTOR& aCollec
{ {
std::unordered_set<BOARD_ITEM*> toAdd; std::unordered_set<BOARD_ITEM*> toAdd;
// Set TEMP_SELECTED on all parents which are included in the GENERAL_COLLECTOR. This // Set CANDIDATE on all parents which are included in the GENERAL_COLLECTOR. This
// algorithm is O3n, whereas checking for the parent inclusion could potentially be On^2. // algorithm is O3n, whereas checking for the parent inclusion could potentially be On^2.
for( int j = 0; j < aCollector.GetCount(); j++ ) for( int j = 0; j < aCollector.GetCount(); j++ )
{ {
if( aCollector[j]->GetParent() ) if( aCollector[j]->GetParent() )
aCollector[j]->GetParent()->ClearFlags( TEMP_SELECTED ); aCollector[j]->GetParent()->ClearFlags( CANDIDATE );
} }
if( aMultiselect ) if( aMultiselect )
{ {
for( int j = 0; j < aCollector.GetCount(); j++ ) for( int j = 0; j < aCollector.GetCount(); j++ )
aCollector[j]->SetFlags( TEMP_SELECTED ); aCollector[j]->SetFlags( CANDIDATE );
} }
for( int j = 0; j < aCollector.GetCount(); ) for( int j = 0; j < aCollector.GetCount(); )
@ -3048,7 +3048,7 @@ void PCB_SELECTION_TOOL::FilterCollectorForHierarchy( GENERAL_COLLECTOR& aCollec
if( aTop != item ) if( aTop != item )
{ {
toAdd.insert( aTop ); toAdd.insert( aTop );
aTop->SetFlags( TEMP_SELECTED ); aTop->SetFlags( CANDIDATE );
aCollector.Remove( item ); aCollector.Remove( item );
continue; continue;
@ -3063,7 +3063,7 @@ void PCB_SELECTION_TOOL::FilterCollectorForHierarchy( GENERAL_COLLECTOR& aCollec
} }
// Footprints are a bit easier as they can't be nested. // Footprints are a bit easier as they can't be nested.
if( parent && ( parent->GetFlags() & TEMP_SELECTED ) ) if( parent && ( parent->GetFlags() & CANDIDATE ) )
{ {
// Remove children of selected items // Remove children of selected items
aCollector.Remove( item ); aCollector.Remove( item );