Cross-probing/selection for multiple items (PCB -> SCH)

Fixes https://gitlab.com/kicad/code/kicad/issues/10469
This commit is contained in:
Alex 2022-07-19 18:00:35 +03:00 committed by Seth Hillbrand
parent 8757b3f98a
commit 3a76d42630
35 changed files with 897 additions and 213 deletions

View File

@ -667,6 +667,7 @@ TOOL_ACTION ACTIONS::reportBug( "common.SuiteControl.reportBug",
// System-wide selection Events
const TOOL_EVENT EVENTS::PointSelectedEvent( TC_MESSAGE, TA_ACTION, "common.Interactive.pointSelected" );
const TOOL_EVENT EVENTS::SelectedEvent( TC_MESSAGE, TA_ACTION, "common.Interactive.selected" );
const TOOL_EVENT EVENTS::UnselectedEvent( TC_MESSAGE, TA_ACTION, "common.Interactive.unselected" );
const TOOL_EVENT EVENTS::ClearedEvent( TC_MESSAGE, TA_ACTION, "common.Interactive.cleared" );

View File

@ -37,7 +37,10 @@ void SELECTION::Add( EDA_ITEM* aItem )
ITER i = std::lower_bound( m_items.begin(), m_items.end(), aItem );
if( i == m_items.end() || *i > aItem )
{
m_items.insert( i, aItem );
m_lastAddedItem = aItem;
}
}
@ -46,7 +49,12 @@ void SELECTION::Remove( EDA_ITEM* aItem )
ITER i = std::lower_bound( m_items.begin(), m_items.end(), aItem );
if( !( i == m_items.end() || *i > aItem ) )
{
m_items.erase( i );
if( aItem == m_lastAddedItem )
m_lastAddedItem = nullptr;
}
}

View File

@ -224,7 +224,8 @@ bool TOOL_EVENT::IsSelectionEvent() const
{
return Matches( EVENTS::ClearedEvent )
|| Matches( EVENTS::UnselectedEvent )
|| Matches( EVENTS::SelectedEvent );
|| Matches( EVENTS::SelectedEvent )
|| Matches( EVENTS::PointSelectedEvent );
}

View File

@ -31,6 +31,7 @@
#include <kiface_base.h>
#include <kiplatform/app.h>
#include <kiway_express.h>
#include <string_utils.h>
#include <project/project_file.h>
#include <macros.h>
#include <netlist_reader/netlist_reader.h>
@ -417,8 +418,8 @@ void CVPCB_MAINFRAME::doCloseWindow()
m_modified = false;
// clear highlight symbol in schematic:
SendMessageToEESCHEMA( true );
// clear symbol selection in schematic:
SendComponentSelectionToSch( true );
}
@ -708,7 +709,7 @@ void CVPCB_MAINFRAME::refreshAfterSymbolSearch( COMPONENT* aSymbol )
}
}
SendMessageToEESCHEMA();
SendComponentSelectionToSch();
DisplayStatus();
}
@ -881,22 +882,23 @@ bool CVPCB_MAINFRAME::LoadFootprintFiles()
}
void CVPCB_MAINFRAME::SendMessageToEESCHEMA( bool aClearHighligntOnly )
void CVPCB_MAINFRAME::SendComponentSelectionToSch( bool aClearSelectionOnly )
{
if( m_netlist.IsEmpty() )
return;
// clear highlight of previously selected symbols (if any):
// Selecting a non existing symbol clears any previously highlighted symbols
std::string packet = "$CLEAR: \"HIGHLIGHTED\"";
std::string command = "$SELECT: ";
if( aClearSelectionOnly )
{
// Sending an empty list means clearing the selection.
if( Kiface().IsSingle() )
SendCommand( MSG_TO_SCH, packet );
SendCommand( MSG_TO_SCH, command );
else
Kiway().ExpressMail( FRAME_SCH, MAIL_CROSS_PROBE, packet, this );
Kiway().ExpressMail( FRAME_SCH, MAIL_SELECTION, command, this );
if( aClearHighligntOnly )
return;
}
int selection = m_symbolsListBox->GetSelection();
@ -906,15 +908,15 @@ void CVPCB_MAINFRAME::SendMessageToEESCHEMA( bool aClearHighligntOnly )
if( m_netlist.GetComponent( selection ) == nullptr )
return;
// Now highlight the selected symbol:
COMPONENT* symbol = m_netlist.GetComponent( selection );
// Now select the corresponding symbol on the schematic:
wxString ref = m_netlist.GetComponent( selection )->GetReference();
packet = std::string( "$PART: \"" ) + TO_UTF8( symbol->GetReference() ) + "\"";
command += wxT( "F" ) + EscapeString( ref, CTX_IPC );
if( Kiface().IsSingle() )
SendCommand( MSG_TO_SCH, packet );
SendCommand( MSG_TO_SCH, command );
else
Kiway().ExpressMail( FRAME_SCH, MAIL_CROSS_PROBE, packet, this );
Kiway().ExpressMail( FRAME_SCH, MAIL_SELECTION, command, this );
}
@ -1069,7 +1071,7 @@ void CVPCB_MAINFRAME::SetSelectedComponent( int aIndex, bool aSkipUpdate )
{
m_symbolsListBox->DeselectAll();
m_symbolsListBox->SetSelection( aIndex );
SendMessageToEESCHEMA();
SendComponentSelectionToSch();
}
m_skipComponentSelect = false;

View File

@ -250,10 +250,10 @@ public:
* Commands are:
* $PART: "reference" put cursor on component anchor
*
* @param aClearHighligntOnly use true if the message to send is only "clear highlight"
* @param aClearSelectionOnly use true if the message to send is only "clear clear selection"
* (used when exiting CvPcb)
*/
void SendMessageToEESCHEMA( bool aClearHighligntOnly = false );
void SendComponentSelectionToSch( bool aClearSelectionOnly = false );
/**
* Get the selected component from the component listbox.

View File

@ -27,7 +27,9 @@
#include <kiway_express.h>
#include <eda_dde.h>
#include <connection_graph.h>
#include <sch_sheet.h>
#include <sch_symbol.h>
#include <sch_reference_list.h>
#include <schematic.h>
#include <reporter.h>
#include <string_utils.h>
@ -134,111 +136,13 @@ SCH_ITEM* SCH_EDITOR_CONTROL::FindSymbolAndItem( const wxString* aPath, const wx
if( crossProbingSettings.zoom_to_fit )
{
EDA_RECT bbox = symbol->GetBoundingBox();
VECTOR2I bbSize = bbox.Inflate( bbox.GetWidth() * 0.2f ).GetSize();
VECTOR2D screenSize = getView()->GetViewport().GetSize();
// This code tries to come up with a zoom factor that doesn't simply zoom in
// to the cross probed symbol, but instead shows a reasonable amount of the
// circuit around it to provide context. This reduces or eliminates the need
// to manually change the zoom because it's too close.
// Using the default text height as a constant to compare against, use the
// height of the bounding box of visible items for a footprint to figure out
// if this is a big symbol (like a processor) or a small symbol (like a resistor).
// This ratio is not useful by itself as a scaling factor. It must be "bent" to
// provide good scaling at varying symbol sizes. Bigger symbols need less
// scaling than small ones.
double currTextHeight = Mils2iu( DEFAULT_TEXT_SIZE );
double compRatio = bbSize.y / currTextHeight; // Ratio of symbol to text height
double compRatioBent = 1.0;
// LUT to scale zoom ratio to provide reasonable schematic context. Must work
// with symbols of varying sizes (e.g. 0402 package and 200 pin BGA).
// "first" is used as the input and "second" as the output
//
// "first" = compRatio (symbol height / default text height)
// "second" = Amount to scale ratio by
std::vector<std::pair<double, double>> lut
{
{1.25, 16}, // 32
{2.5, 12}, //24
{5, 8}, // 16
{6, 6}, //
{10, 4}, //8
{20, 2}, //4
{40, 1.5}, // 2
{100, 1}
};
std::vector<std::pair<double, double>>::iterator it;
// Large symbol default is last LUT entry (1:1).
compRatioBent = lut.back().second;
// Use LUT to do linear interpolation of "compRatio" within "first", then
// use that result to linearly interpolate "second" which gives the scaling
// factor needed.
if( compRatio >= lut.front().first )
{
for( it = lut.begin(); it < lut.end() - 1; it++ )
{
if( it->first <= compRatio && next( it )->first >= compRatio )
{
double diffx = compRatio - it->first;
double diffn = next( it )->first - it->first;
compRatioBent = it->second
+ ( next( it )->second - it->second ) * diffx / diffn;
break; // We have our interpolated value
}
}
m_toolMgr->GetTool<EE_SELECTION_TOOL>()->ZoomFitCrossProbeBBox( bbox );
}
if( pin )
m_frame->FocusOnItem( pin );
else
{
compRatioBent = lut.front().second; // Small symbol default is first entry
}
// This is similar to the original KiCad code that scaled the zoom to make sure
// symbols were visible on screen. It's simply a ratio of screen size to
// symbol size, and its job is to zoom in to make the component fullscreen.
// Earlier in the code the symbol BBox is given a 20% margin to add some
// breathing room. We compare the height of this enlarged symbol bbox to the
// default text height. If a symbol will end up with the sides clipped, we
// adjust later to make sure it fits on screen.
screenSize.x = std::max( 10.0, screenSize.x );
screenSize.y = std::max( 10.0, screenSize.y );
double ratio = std::max( -1.0, fabs( bbSize.y / screenSize.y ) );
// Original KiCad code for how much to scale the zoom
double kicadRatio = std::max( fabs( bbSize.x / screenSize.x ),
fabs( bbSize.y / screenSize.y ) );
// If the width of the part we're probing is bigger than what the screen width
// will be after the zoom, then punt and use the KiCad zoom algorithm since it
// guarantees the part's width will be encompassed within the screen.
if( bbSize.x > screenSize.x * ratio * compRatioBent )
{
// Use standard KiCad zoom for parts too wide to fit on screen/
ratio = kicadRatio;
compRatioBent = 1.0; // Reset so we don't modify the "KiCad" ratio
wxLogTrace( "CROSS_PROBE_SCALE",
"Part TOO WIDE for screen. Using normal KiCad zoom ratio: %1.5f",
ratio );
}
// Now that "compRatioBent" holds our final scaling factor we apply it to the
// original fullscreen zoom ratio to arrive at the final ratio itself.
ratio *= compRatioBent;
bool alwaysZoom = false; // DEBUG - allows us to minimize zooming or not
// Try not to zoom on every cross-probe; it gets very noisy
if( ( ratio < 0.5 || ratio > 1.0 ) || alwaysZoom )
getView()->SetScale( getView()->GetScale() / ratio );
}
m_frame->FocusOnItem( symbol );
}
}
@ -273,18 +177,6 @@ SCH_ITEM* SCH_EDITOR_CONTROL::FindSymbolAndItem( const wxString* aPath, const wx
m_frame->SetStatusText( msg );
m_probingPcbToSch = true; // recursion guard
{
// Clear any existing highlighting
m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
if( foundItem )
m_toolMgr->RunAction( EE_ACTIONS::addItemToSel, true, foundItem );
}
m_probingPcbToSch = false;
m_frame->GetCanvas()->Refresh();
return foundItem;
@ -382,7 +274,7 @@ void SCH_EDIT_FRAME::ExecuteRemoteCommand( const char* cmdline )
}
void SCH_EDIT_FRAME::SendSelectItems( const std::deque<EDA_ITEM*>& aItems, bool aForce )
void SCH_EDIT_FRAME::SendSelectItemsToPcb( const std::deque<EDA_ITEM*>& aItems, bool aForce )
{
std::set<wxString> parts;
@ -552,6 +444,375 @@ void SCH_EDIT_FRAME::SendCrossProbeClearHighlight()
}
bool findSymbolsAndPins(
const SCHEMATIC& aSchematic, const SCH_SHEET_PATH& aSheetPath,
std::unordered_map<wxString, std::vector<SCH_REFERENCE>>& aSyncSymMap,
std::unordered_map<wxString, std::unordered_map<wxString, SCH_PIN*>>& aSyncPinMap,
bool aRecursive = false )
{
if( aRecursive )
{
// Iterate over children
for( const SCH_SHEET_PATH& candidate : aSchematic.GetSheets() )
{
if( candidate == aSheetPath || !candidate.IsContainedWithin( aSheetPath ) )
continue;
findSymbolsAndPins( aSchematic, candidate, aSyncSymMap, aSyncPinMap, aRecursive );
}
}
SCH_REFERENCE_LIST references;
aSheetPath.GetSymbols( references, false, true );
for( unsigned ii = 0; ii < references.GetCount(); ii++ )
{
SCH_REFERENCE& schRef = references[ii];
if( schRef.IsSplitNeeded() )
schRef.Split();
SCH_SYMBOL* symbol = schRef.GetSymbol();
wxString refNum = schRef.GetRefNumber();
wxString fullRef = schRef.GetRef() + refNum;
// Skip power symbols
if( fullRef.StartsWith( "#" ) )
continue;
// Unannotated symbols are not supported
if( refNum.compare( "?" ) == 0 )
continue;
// Look for whole footprint
auto symMatchIt = aSyncSymMap.find( fullRef );
if( symMatchIt != aSyncSymMap.end() )
{
symMatchIt->second.emplace_back( schRef );
// Whole footprint was selected, no need to select pins
continue;
}
// Look for pins
auto symPinMatchIt = aSyncPinMap.find( fullRef );
if( symPinMatchIt != aSyncPinMap.end() )
{
std::unordered_map<wxString, SCH_PIN*>& pinMap = symPinMatchIt->second;
std::vector<SCH_PIN*> pinsOnSheet = symbol->GetPins( &aSheetPath );
for( SCH_PIN* pin : pinsOnSheet )
{
int pinUnit = pin->GetLibPin()->GetUnit();
if( pinUnit > 0 && pinUnit != schRef.GetUnit() )
continue;
auto pinIt = pinMap.find( pin->GetNumber() );
if( pinIt != pinMap.end() )
pinIt->second = pin;
}
}
}
return false;
}
bool sheetContainsOnlyWantedItems(
const SCHEMATIC& aSchematic, const SCH_SHEET_PATH& aSheetPath,
std::unordered_map<wxString, std::vector<SCH_REFERENCE>>& aSyncSymMap,
std::unordered_map<wxString, std::unordered_map<wxString, SCH_PIN*>>& aSyncPinMap,
std::unordered_map<SCH_SHEET_PATH, bool>& aCache )
{
auto cacheIt = aCache.find( aSheetPath );
if( cacheIt != aCache.end() )
return cacheIt->second;
// Iterate over children
for( const SCH_SHEET_PATH& candidate : aSchematic.GetSheets() )
{
if( candidate == aSheetPath || !candidate.IsContainedWithin( aSheetPath ) )
continue;
bool childRet = sheetContainsOnlyWantedItems( aSchematic, candidate, aSyncSymMap,
aSyncPinMap, aCache );
if( !childRet )
{
aCache.emplace( aSheetPath, false );
return false;
}
}
SCH_REFERENCE_LIST references;
aSheetPath.GetSymbols( references, false, true );
for( unsigned ii = 0; ii < references.GetCount(); ii++ )
{
SCH_REFERENCE& schRef = references[ii];
if( schRef.IsSplitNeeded() )
schRef.Split();
wxString refNum = schRef.GetRefNumber();
wxString fullRef = schRef.GetRef() + refNum;
// Skip power symbols
if( fullRef.StartsWith( "#" ) )
continue;
// Unannotated symbols are not supported
if( refNum.compare( "?" ) == 0 )
continue;
if( aSyncSymMap.find( fullRef ) == aSyncSymMap.end() )
{
aCache.emplace( aSheetPath, false );
return false; // Some symbol is not wanted.
}
if( aSyncPinMap.find( fullRef ) != aSyncPinMap.end() )
{
aCache.emplace( aSheetPath, false );
return false; // Looking for specific pins, so can't be mapped
}
}
aCache.emplace( aSheetPath, true );
return true;
}
std::optional<std::tuple<SCH_SHEET_PATH, SCH_ITEM*, std::vector<SCH_ITEM*>>>
findItemsFromSyncSelection( const SCHEMATIC& aSchematic, const std::string aSyncStr,
bool aFocusOnFirst )
{
wxArrayString syncArray = wxStringTokenize( aSyncStr, "," );
std::unordered_map<wxString, std::vector<SCH_REFERENCE>> syncSymMap;
std::unordered_map<wxString, std::unordered_map<wxString, SCH_PIN*>> syncPinMap;
std::unordered_map<SCH_SHEET_PATH, double> symScores;
std::unordered_map<SCH_SHEET_PATH, bool> fullyWantedCache;
std::optional<wxString> focusSymbol;
std::optional<std::pair<wxString, wxString>> focusPin;
std::unordered_map<SCH_SHEET_PATH, std::vector<SCH_ITEM*>> focusItemResults;
const SCH_SHEET_LIST allSheetsList = aSchematic.GetSheets();
// In orderedSheets, the current sheet comes first.
SCH_SHEET_PATHS orderedSheets;
orderedSheets.reserve( allSheetsList.size() );
orderedSheets.push_back( aSchematic.CurrentSheet() );
for( const SCH_SHEET_PATH& sheetPath : allSheetsList )
{
if( sheetPath != aSchematic.CurrentSheet() )
orderedSheets.push_back( sheetPath );
}
// Init sync maps from the sync string
for( int i = 0; i < syncArray.size(); i++ )
{
wxString syncEntry = syncArray[i];
if( syncEntry.empty() )
continue;
wxString syncData = syncEntry.substr( 1 );
switch( syncEntry.GetChar( 0 ).GetValue() )
{
case 'F': // Select by footprint: F<Reference>
{
wxString symRef = UnescapeString( syncData );
if( aFocusOnFirst && ( i == 0 ) )
focusSymbol = symRef;
syncSymMap[symRef] = std::vector<SCH_REFERENCE>();
break;
}
case 'P': // Select by pad: P<Footprint reference>/<Pad number>
{
wxString symRef = UnescapeString( syncData.BeforeFirst( '/' ) );
wxString padNum = UnescapeString( syncData.AfterFirst( '/' ) );
if( aFocusOnFirst && ( i == 0 ) )
focusPin = std::make_pair( symRef, padNum );
syncPinMap[symRef][padNum] = nullptr;
break;
}
default: break;
}
}
// Lambda definitions
auto flattenSyncMaps = [&syncSymMap, &syncPinMap]() -> std::vector<SCH_ITEM*>
{
std::vector<SCH_ITEM*> allVec;
for( auto const& pairSym : syncSymMap )
{
for( const SCH_REFERENCE& ref : pairSym.second )
{
allVec.push_back( ref.GetSymbol() );
}
}
for( auto const& pairSym : syncPinMap )
{
for( auto const& pairPin : pairSym.second )
{
if( pairPin.second )
allVec.push_back( pairPin.second );
}
}
return allVec;
};
auto clearSyncMaps = [&syncSymMap, &syncPinMap]()
{
for( auto& pairSym : syncSymMap )
{
pairSym.second.clear();
}
for( auto& pairSym : syncPinMap )
{
for( auto& pairPin : pairSym.second )
{
pairPin.second = nullptr;
}
}
};
auto syncMapsValuesEmpty = [&syncSymMap, &syncPinMap]() -> bool
{
for( auto const& pairSym : syncSymMap )
{
if( pairSym.second.size() > 0 )
return false;
}
for( auto const& pairSym : syncPinMap )
{
for( auto const& pairPin : pairSym.second )
{
if( pairPin.second )
return false;
}
}
return true;
};
auto checkFocusItems = [&]( const SCH_SHEET_PATH& aSheetPath )
{
if( focusSymbol )
{
auto findIt = syncSymMap.find( *focusSymbol );
if( findIt != syncSymMap.end() )
{
if( findIt->second.size() > 0 )
{
focusItemResults[aSheetPath].push_back( findIt->second.front().GetSymbol() );
}
}
}
else if( focusPin )
{
auto findIt = syncPinMap.find( focusPin->first );
if( findIt != syncPinMap.end() )
{
if( findIt->second[focusPin->second] )
{
focusItemResults[aSheetPath].push_back( findIt->second[focusPin->second] );
}
}
}
};
auto makeRetForSheet = [&]( const SCH_SHEET_PATH& aSheet, SCH_ITEM* aFocusItem )
{
clearSyncMaps();
// Fill sync maps
findSymbolsAndPins( aSchematic, aSheet, syncSymMap, syncPinMap );
std::vector<SCH_ITEM*> itemsVector = flattenSyncMaps();
// Add fully wanted sheets to vector
for( SCH_ITEM* item : aSheet.LastScreen()->Items().OfType( SCH_SHEET_T ) )
{
KIID_PATH kiidPath = aSheet.Path();
kiidPath.push_back( item->m_Uuid );
std::optional<SCH_SHEET_PATH> subsheetPath =
allSheetsList.GetSheetPathByKIIDPath( kiidPath );
if( !subsheetPath )
continue;
if( sheetContainsOnlyWantedItems( aSchematic, *subsheetPath, syncSymMap, syncPinMap,
fullyWantedCache ) )
{
itemsVector.push_back( item );
}
}
return std::make_tuple( aSheet, aFocusItem, itemsVector );
};
if( aFocusOnFirst )
{
for( const SCH_SHEET_PATH& sheetPath : orderedSheets )
{
clearSyncMaps();
findSymbolsAndPins( aSchematic, sheetPath, syncSymMap, syncPinMap );
checkFocusItems( sheetPath );
}
if( focusItemResults.size() > 0 )
{
for( const SCH_SHEET_PATH& sheetPath : orderedSheets )
{
auto vec = focusItemResults[sheetPath];
if( !vec.empty() )
return makeRetForSheet( sheetPath, vec.front() );
}
}
}
else
{
for( const SCH_SHEET_PATH& sheetPath : orderedSheets )
{
clearSyncMaps();
findSymbolsAndPins( aSchematic, sheetPath, syncSymMap, syncPinMap );
if( !syncMapsValuesEmpty() )
{
// Something found on sheet
return makeRetForSheet( sheetPath, nullptr );
}
}
}
return std::nullopt;
}
void SCH_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
{
std::string& payload = mail.GetPayload();
@ -562,6 +823,45 @@ void SCH_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
ExecuteRemoteCommand( payload.c_str() );
break;
case MAIL_SELECTION:
if( !eeconfig()->m_CrossProbing.on_selection )
break;
KI_FALLTHROUGH;
case MAIL_SELECTION_FORCE:
{
// $SELECT: 0,<spec1>,<spec2>,<spec3>
// Try to select specified items.
// $SELECT: 1,<spec1>,<spec2>,<spec3>
// Select and focus on <spec1> item, select other specified items that are on the same sheet.
std::string prefix = "$SELECT: ";
std::string paramStr = payload.substr( prefix.size() );
std::string syncStr = paramStr.substr( 2 );
bool focusOnFirst = ( paramStr[0] == '1' );
std::optional<std::tuple<SCH_SHEET_PATH, SCH_ITEM*, std::vector<SCH_ITEM*>>> findRet =
findItemsFromSyncSelection( Schematic(), syncStr, focusOnFirst );
if( findRet )
{
auto& [sheetPath, focusItem, items] = *findRet;
m_syncingPcbToSchSelection = true; // recursion guard
GetToolManager()->GetTool<EE_SELECTION_TOOL>()->SyncSelection( sheetPath, focusItem,
items );
m_syncingPcbToSchSelection = false;
}
break;
}
case MAIL_SCH_GET_NETLIST:
{
if( !payload.empty() )

View File

@ -120,6 +120,7 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
m_showBorderAndTitleBlock = true; // true to show sheet references
m_supportsAutoSave = true;
m_syncingPcbToSchSelection = false;
m_aboutTitle = _( "KiCad Schematic Editor" );
m_findReplaceDialog = nullptr;
@ -1761,11 +1762,14 @@ void SCH_EDIT_FRAME::FocusOnItem( SCH_ITEM* aItem )
}
if( aItem )
{
if( !aItem->IsBrightened() )
{
aItem->SetBrightened();
UpdateItem( aItem );
lastBrightenedItemID = aItem->m_Uuid;
}
FocusOnLocation( aItem->GetFocusPosition() );
}

View File

@ -264,7 +264,7 @@ public:
* @param aForce select the element in pcbnew whether or not the user has the select option chosen
* This is used for when the eeschema user is using the cross-probe tool
*/
void SendSelectItems( const std::deque<EDA_ITEM*>& aElements, bool aForce );
void SendSelectItemsToPcb( const std::deque<EDA_ITEM*>& aElements, bool aForce );
/**
* Sends a net name to Pcbnew for highlighting
@ -772,6 +772,8 @@ public:
void FocusOnItem( SCH_ITEM* aItem );
bool IsSyncingSelection() { return m_syncingPcbToSchSelection; }
/**
* Update a schematic symbol from a LIB_SYMBOL.
*
@ -923,6 +925,8 @@ private:
DIALOG_SCH_FIND* m_findReplaceDialog;
HIERARCHY_NAVIG_PANEL* m_hierarchy;
bool m_syncingPcbToSchSelection; // Recursion guard when synchronizing selection from PCB
};

View File

@ -866,6 +866,18 @@ void SCH_SHEET_LIST::GetSheetsWithinPath( SCH_SHEET_PATHS& aSheets,
}
std::optional<SCH_SHEET_PATH> SCH_SHEET_LIST::GetSheetPathByKIIDPath( const KIID_PATH& aPath ) const
{
for( const SCH_SHEET_PATH& sheet : *this )
{
if( sheet.Path() == aPath )
return SCH_SHEET_PATH( sheet );
}
return std::nullopt;
}
void SCH_SHEET_LIST::GetMultiUnitSymbols( SCH_MULTI_UNIT_REFERENCE_MAP &aRefList,
bool aIncludePowerSymbols ) const
{

View File

@ -32,8 +32,10 @@
#define CLASS_DRAWSHEET_PATH_H
#include <map>
#include <optional>
#include <kiid.h>
#include <wx/string.h>
/**
* A simple container for schematic symbol instance information.
@ -506,6 +508,15 @@ public:
*/
void GetSheetsWithinPath( SCH_SHEET_PATHS& aSheets, const SCH_SHEET_PATH& aSheetPath ) const;
/**
* Finds a SCH_SHEET_PATH that matches the provided KIID_PATH.
*
* @param aPath The KIID_PATH to search for.
*/
std::optional<SCH_SHEET_PATH> GetSheetPathByKIIDPath( const KIID_PATH& aPath ) const;
/**
* Add a #SCH_REFERENCE_LIST object to \a aRefList for each same-reference set of
* multi-unit parts in the list of sheets. The map key for each element will be the

View File

@ -110,6 +110,9 @@ TOOL_ACTION EE_ACTIONS::removeItemsFromSel( "eeschema.InteractiveSelection.Remov
TOOL_ACTION EE_ACTIONS::clearSelection( "eeschema.InteractiveSelection.ClearSelection",
AS_GLOBAL );
TOOL_ACTION EE_ACTIONS::syncSelection( "eeschema.InteractiveSelection.SyncSelection",
AS_GLOBAL );
// SYMBOL_EDITOR_CONTROL
//

View File

@ -66,6 +66,9 @@ public:
/// Runs a selection menu to select from a list of items
static TOOL_ACTION selectionMenu;
/// Selection synchronization (PCB -> SCH)
static TOOL_ACTION syncSelection;
// Locking
static TOOL_ACTION toggleLock;
static TOOL_ACTION lock;

View File

@ -338,6 +338,7 @@ void EE_INSPECTION_TOOL::setTransitions()
Go( &EE_INSPECTION_TOOL::PrevMarker, EE_ACTIONS::prevMarker.MakeEvent() );
Go( &EE_INSPECTION_TOOL::NextMarker, EE_ACTIONS::nextMarker.MakeEvent() );
// See note 1:
Go( &EE_INSPECTION_TOOL::CrossProbe, EVENTS::PointSelectedEvent );
Go( &EE_INSPECTION_TOOL::CrossProbe, EVENTS::SelectedEvent );
Go( &EE_INSPECTION_TOOL::ExcludeMarker, EE_ACTIONS::excludeMarker.MakeEvent() );

View File

@ -1477,6 +1477,7 @@ void EE_POINT_EDITOR::rollbackFromUndo()
void EE_POINT_EDITOR::setTransitions()
{
Go( &EE_POINT_EDITOR::Main, EVENTS::PointSelectedEvent );
Go( &EE_POINT_EDITOR::Main, EVENTS::SelectedEvent );
Go( &EE_POINT_EDITOR::Main, ACTIONS::activatePointEditor.MakeEvent() );
Go( &EE_POINT_EDITOR::addCorner, EE_ACTIONS::pointEditorAddCorner.MakeEvent() );

View File

@ -872,7 +872,7 @@ bool EE_SELECTION_TOOL::selectPoint( EE_COLLECTOR& aCollector, const VECTOR2I& a
if( !aAdd && !aSubtract && !aExclusiveOr )
ClearSelection();
bool anyAdded = false;
int addedCount = 0;
bool anySubtracted = false;
if( aCollector.GetCount() > 0 )
@ -908,20 +908,25 @@ bool EE_SELECTION_TOOL::selectPoint( EE_COLLECTOR& aCollector, const VECTOR2I& a
{
aCollector[i]->SetFlags( flags );
select( aCollector[i] );
anyAdded = true;
addedCount++;
}
}
}
if( anyAdded )
if( addedCount == 1 )
{
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
m_toolMgr->ProcessEvent( EVENTS::PointSelectedEvent );
if( aItem && aCollector.GetCount() == 1 )
*aItem = aCollector[0];
return true;
}
else if( addedCount > 1 )
{
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
return true;
}
else if( anySubtracted )
{
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
@ -1491,6 +1496,156 @@ int EE_SELECTION_TOOL::ClearSelection( const TOOL_EVENT& aEvent )
}
void EE_SELECTION_TOOL::ZoomFitCrossProbeBBox( EDA_RECT bbox )
{
if( bbox.GetWidth() == 0 )
return;
bbox.Normalize();
VECTOR2I bbSize = bbox.Inflate( bbox.GetWidth() * 0.2f ).GetSize();
VECTOR2D screenSize = getView()->GetViewport().GetSize();
// This code tries to come up with a zoom factor that doesn't simply zoom in
// to the cross probed symbol, but instead shows a reasonable amount of the
// circuit around it to provide context. This reduces or eliminates the need
// to manually change the zoom because it's too close.
// Using the default text height as a constant to compare against, use the
// height of the bounding box of visible items for a footprint to figure out
// if this is a big symbol (like a processor) or a small symbol (like a resistor).
// This ratio is not useful by itself as a scaling factor. It must be "bent" to
// provide good scaling at varying symbol sizes. Bigger symbols need less
// scaling than small ones.
double currTextHeight = Mils2iu( DEFAULT_TEXT_SIZE );
double compRatio = bbSize.y / currTextHeight; // Ratio of symbol to text height
double compRatioBent = 1.0;
// LUT to scale zoom ratio to provide reasonable schematic context. Must work
// with symbols of varying sizes (e.g. 0402 package and 200 pin BGA).
// "first" is used as the input and "second" as the output
//
// "first" = compRatio (symbol height / default text height)
// "second" = Amount to scale ratio by
std::vector<std::pair<double, double>> lut{ { 1.25, 16 }, // 32
{ 2.5, 12 }, //24
{ 5, 8 }, // 16
{ 6, 6 }, //
{ 10, 4 }, //8
{ 20, 2 }, //4
{ 40, 1.5 }, // 2
{ 100, 1 } };
std::vector<std::pair<double, double>>::iterator it;
// Large symbol default is last LUT entry (1:1).
compRatioBent = lut.back().second;
// Use LUT to do linear interpolation of "compRatio" within "first", then
// use that result to linearly interpolate "second" which gives the scaling
// factor needed.
if( compRatio >= lut.front().first )
{
for( it = lut.begin(); it < lut.end() - 1; it++ )
{
if( it->first <= compRatio && next( it )->first >= compRatio )
{
double diffx = compRatio - it->first;
double diffn = next( it )->first - it->first;
compRatioBent = it->second + ( next( it )->second - it->second ) * diffx / diffn;
break; // We have our interpolated value
}
}
}
else
{
compRatioBent = lut.front().second; // Small symbol default is first entry
}
// This is similar to the original KiCad code that scaled the zoom to make sure
// symbols were visible on screen. It's simply a ratio of screen size to
// symbol size, and its job is to zoom in to make the component fullscreen.
// Earlier in the code the symbol BBox is given a 20% margin to add some
// breathing room. We compare the height of this enlarged symbol bbox to the
// default text height. If a symbol will end up with the sides clipped, we
// adjust later to make sure it fits on screen.
screenSize.x = std::max( 10.0, screenSize.x );
screenSize.y = std::max( 10.0, screenSize.y );
double ratio = std::max( -1.0, fabs( bbSize.y / screenSize.y ) );
// Original KiCad code for how much to scale the zoom
double kicadRatio =
std::max( fabs( bbSize.x / screenSize.x ), fabs( bbSize.y / screenSize.y ) );
// If the width of the part we're probing is bigger than what the screen width
// will be after the zoom, then punt and use the KiCad zoom algorithm since it
// guarantees the part's width will be encompassed within the screen.
if( bbSize.x > screenSize.x * ratio * compRatioBent )
{
// Use standard KiCad zoom for parts too wide to fit on screen/
ratio = kicadRatio;
compRatioBent = 1.0; // Reset so we don't modify the "KiCad" ratio
wxLogTrace( "CROSS_PROBE_SCALE",
"Part TOO WIDE for screen. Using normal KiCad zoom ratio: %1.5f", ratio );
}
// Now that "compRatioBent" holds our final scaling factor we apply it to the
// original fullscreen zoom ratio to arrive at the final ratio itself.
ratio *= compRatioBent;
bool alwaysZoom = false; // DEBUG - allows us to minimize zooming or not
// Try not to zoom on every cross-probe; it gets very noisy
if( ( ratio < 0.5 || ratio > 1.0 ) || alwaysZoom )
getView()->SetScale( getView()->GetScale() / ratio );
}
int EE_SELECTION_TOOL::SyncSelection( std::optional<SCH_SHEET_PATH> targetSheetPath,
SCH_ITEM* focusItem, std::vector<SCH_ITEM*> items )
{
SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
if( !editFrame || m_isSymbolEditor || m_isSymbolViewer )
return 0;
if( targetSheetPath && targetSheetPath != editFrame->Schematic().CurrentSheet() )
{
editFrame->Schematic().SetCurrentSheet( *targetSheetPath );
editFrame->DisplayCurrentSheet();
}
ClearSelection( items.size() > 0 ? true /*quiet mode*/ : false );
// Perform individual selection of each item before processing the event.
for( SCH_ITEM* item : items )
select( item );
EDA_RECT bbox = m_selection.GetBoundingBox();
if( bbox.GetWidth() != 0 && bbox.GetHeight() != 0 )
{
if( m_frame->eeconfig()->m_CrossProbing.center_on_items )
{
if( m_frame->eeconfig()->m_CrossProbing.zoom_to_fit )
ZoomFitCrossProbeBBox( bbox );
editFrame->FocusOnItem( focusItem );
if( !focusItem )
editFrame->FocusOnLocation( bbox.Centre() );
}
}
if( m_selection.Size() > 0 )
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
return 0;
}
void EE_SELECTION_TOOL::RebuildSelection()
{
m_selection.Clear();
@ -1604,7 +1759,7 @@ bool EE_SELECTION_TOOL::Selectable( const EDA_ITEM* aItem, const VECTOR2I* aPos,
}
void EE_SELECTION_TOOL::ClearSelection()
void EE_SELECTION_TOOL::ClearSelection( bool aQuietMode )
{
if( m_selection.Empty() )
return;
@ -1618,8 +1773,11 @@ void EE_SELECTION_TOOL::ClearSelection()
m_selection.ClearReferencePoint();
// Inform other potentially interested tools
if( !aQuietMode )
{
m_toolMgr->ProcessEvent( EVENTS::ClearedEvent );
}
}
void EE_SELECTION_TOOL::select( EDA_ITEM* aItem )

View File

@ -73,6 +73,9 @@ public:
void OnIdle( wxIdleEvent& aEvent );
///< Zoom the screen to fit the bounding box for cross probing/selection sync.
void ZoomFitCrossProbeBBox( EDA_RECT bbox );
/**
* @return the set of currently selected items.
*/
@ -135,7 +138,7 @@ public:
///< Select all visible items in sheet
int SelectAll( const TOOL_EVENT& aEvent );
void ClearSelection();
void ClearSelection( bool aQuietMode = false );
/**
* Check conditions for an item to be selected.
@ -170,6 +173,11 @@ public:
bool CollectHits( EE_COLLECTOR& aCollector, const VECTOR2I& aWhere,
const std::vector<KICAD_T>& aScanTypes = { SCH_LOCATE_ANY_T } );
///< Set selection to items passed by parameter.
///< Zooms to fit, if enabled.
int SyncSelection( std::optional<SCH_SHEET_PATH> targetSheetPath, SCH_ITEM* focusItem,
std::vector<SCH_ITEM*> items );
protected:
SELECTION& selection() override { return m_selection; }

View File

@ -716,14 +716,14 @@ int SCH_EDITOR_CONTROL::ExplicitCrossProbeToPcb( const TOOL_EVENT& aEvent )
void SCH_EDITOR_CONTROL::doCrossProbeSchToPcb( const TOOL_EVENT& aEvent, bool aForce )
{
// Don't get in an infinite loop SCH -> PCB -> SCH -> PCB -> SCH -> ...
if( m_probingPcbToSch )
if( m_probingPcbToSch || m_frame->IsSyncingSelection() )
return;
EE_SELECTION_TOOL* selTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
EE_SELECTION& selection = aForce ? selTool->RequestSelection() : selTool->GetSelection();
m_frame->SendSelectItems( selection.GetItems(), aForce );
m_frame->SendSelectItemsToPcb( selection.GetItems(), aForce );
}
@ -2337,6 +2337,7 @@ void SCH_EDITOR_CONTROL::setTransitions()
Go( &SCH_EDITOR_CONTROL::UpdateFind, ACTIONS::updateFind.MakeEvent() );
Go( &SCH_EDITOR_CONTROL::UpdateFind, EVENTS::SelectedItemsModified );
Go( &SCH_EDITOR_CONTROL::CrossProbeToPcb, EVENTS::PointSelectedEvent );
Go( &SCH_EDITOR_CONTROL::CrossProbeToPcb, EVENTS::SelectedEvent );
Go( &SCH_EDITOR_CONTROL::CrossProbeToPcb, EVENTS::UnselectedEvent );
Go( &SCH_EDITOR_CONTROL::CrossProbeToPcb, EVENTS::ClearedEvent );

View File

@ -149,7 +149,7 @@ public:
void AssignFootprints( const std::string& aChangedSetOfReferences );
/**
* Find a symbol in the schematic and an item in this symbol.
* Find a symbol in the schematic and an item in this symbol and select it.
*
* @param aPath The symbol path to find. Pass nullptr to search by aReference.
* @param aReference The symbol reference designator to find, or to display in
@ -222,7 +222,7 @@ private:
wxFindReplaceData& aData );
private:
bool m_probingPcbToSch; // Recursion guard when cross-probing to PcbNew
bool m_probingPcbToSch; // Recursion guard when cross-probing to schematic editor
EDA_ITEM* m_pickerItem; // Current item for picker highlighting.
// Temporary storage location for Duplicate action

View File

@ -37,7 +37,7 @@
enum MAIL_T
{
MAIL_CROSS_PROBE, // PCB<->SCH, CVPCB->SCH cross-probing.
MAIL_SELECTION, // SCH->PCB selection synchronization.
MAIL_SELECTION, // SCH<->PCB selection synchronization.
MAIL_SELECTION_FORCE, // Explicit selection of SCH->PCB selection synchronization.
MAIL_ASSIGN_FOOTPRINTS, // CVPCB->SCH footprint stuffing
MAIL_SCH_SAVE, // CVPCB->SCH save the schematic

View File

@ -198,6 +198,7 @@ public:
class EVENTS
{
public:
const static TOOL_EVENT PointSelectedEvent;
const static TOOL_EVENT SelectedEvent;
const static TOOL_EVENT UnselectedEvent;
const static TOOL_EVENT ClearedEvent;

View File

@ -42,6 +42,7 @@ public:
KIGFX::VIEW_GROUP::VIEW_GROUP()
{
m_isHover = false;
m_lastAddedItem = nullptr;
}
SELECTION( const SELECTION& aOther ) :
@ -49,12 +50,14 @@ public:
{
m_items = aOther.m_items;
m_isHover = aOther.m_isHover;
m_lastAddedItem = aOther.m_lastAddedItem;
}
SELECTION& operator= ( const SELECTION& aOther )
{
m_items = aOther.m_items;
m_isHover = aOther.m_isHover;
m_lastAddedItem = aOther.m_lastAddedItem;
return *this;
}
@ -111,6 +114,11 @@ public:
return m_items;
}
EDA_ITEM* GetLastAddedItem() const
{
return m_lastAddedItem;
}
/**
* Returns a copy of this selection of items sorted by their X then Y position.
*
@ -257,6 +265,7 @@ public:
protected:
OPT<VECTOR2I> m_referencePoint;
std::deque<EDA_ITEM*> m_items;
EDA_ITEM* m_lastAddedItem;
bool m_isHover;
// mute hidden overloaded virtual function warnings

View File

@ -36,6 +36,7 @@
#include <footprint.h>
#include <pad.h>
#include <pcb_track.h>
#include <pcb_group.h>
#include <zone.h>
#include <collectors.h>
#include <eda_dde.h>
@ -221,11 +222,11 @@ void PCB_EDIT_FRAME::ExecuteRemoteCommand( const char* cmdline )
renderSettings->SetHighlight( false );
}
if( crossProbingSettings.center_on_items && bbox.GetWidth() > 0 && bbox.GetHeight() > 0 )
if( crossProbingSettings.center_on_items && bbox.GetWidth() != 0 && bbox.GetHeight() != 0 )
{
if( crossProbingSettings.zoom_to_fit )
{
GetToolManager()->GetTool<PCB_SELECTION_TOOL>()->zoomFitCrossProbeBBox( bbox );
GetToolManager()->GetTool<PCB_SELECTION_TOOL>()->ZoomFitCrossProbeBBox( bbox );
}
FocusOnLocation( (wxPoint) bbox.Centre() );
@ -293,9 +294,108 @@ std::string FormatProbeItem( BOARD_ITEM* aItem )
}
void PCB_EDIT_FRAME::SendMessageToEESCHEMA( BOARD_ITEM* aSyncItem )
template <typename ItemContainer>
void collectItemsForSyncParts( ItemContainer& aItems, std::set<wxString>& parts )
{
std::string packet = FormatProbeItem( aSyncItem );
for( EDA_ITEM* item : aItems )
{
switch( item->Type() )
{
case PCB_GROUP_T:
{
PCB_GROUP* group = static_cast<PCB_GROUP*>( item );
collectItemsForSyncParts( group->GetItems(), parts );
break;
}
case PCB_FOOTPRINT_T:
{
FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
wxString ref = footprint->GetReference();
parts.emplace( wxT( "F" ) + EscapeString( ref, CTX_IPC ) );
break;
}
case PCB_PAD_T:
{
PAD* pad = static_cast<PAD*>( item );
FOOTPRINT* footprint = static_cast<FOOTPRINT*>( pad->GetParentFootprint() );
wxString ref = footprint->GetReference();
parts.emplace( wxT( "P" ) + EscapeString( ref, CTX_IPC ) + wxT( "/" )
+ EscapeString( pad->GetNumber(), CTX_IPC ) );
break;
}
default: break;
}
}
}
void PCB_EDIT_FRAME::SendSelectItemsToSch( const std::deque<EDA_ITEM*>& aItems,
EDA_ITEM* aFocusItem, bool aForce )
{
std::string command = "$SELECT: ";
if( aFocusItem )
{
std::deque<EDA_ITEM*> focusItems = { aFocusItem };
std::set<wxString> focusParts;
collectItemsForSyncParts( focusItems, focusParts );
if( focusParts.size() > 0 )
{
command += "1,";
command += *focusParts.begin();
command += ",";
}
else
{
command += "0,";
}
}
else
{
command += "0,";
}
std::set<wxString> parts;
collectItemsForSyncParts( aItems, parts );
if( parts.empty() )
return;
for( wxString part : parts )
{
command += part;
command += ",";
}
command.pop_back();
if( Kiface().IsSingle() )
{
SendCommand( MSG_TO_PCB, command );
}
else
{
// Typically ExpressMail is going to be s-expression packets, but since
// we have existing interpreter of the selection packet on the other
// side in place, we use that here.
Kiway().ExpressMail( FRAME_SCH, aForce ? MAIL_SELECTION_FORCE : MAIL_SELECTION, command,
this );
}
}
void PCB_EDIT_FRAME::SendCrossProbeNetName( const wxString& aNetName )
{
std::string packet = StrPrintf( "$NET: \"%s\"", TO_UTF8( aNetName ) );
if( !packet.empty() )
{
@ -314,9 +414,9 @@ void PCB_EDIT_FRAME::SendMessageToEESCHEMA( BOARD_ITEM* aSyncItem )
}
void PCB_EDIT_FRAME::SendCrossProbeNetName( const wxString& aNetName )
void PCB_EDIT_FRAME::SendCrossProbeItem( BOARD_ITEM* aSyncItem )
{
std::string packet = StrPrintf( "$NET: \"%s\"", TO_UTF8( aNetName ) );
std::string packet = FormatProbeItem( aSyncItem );
if( !packet.empty() )
{
@ -503,7 +603,7 @@ void PCB_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
std::vector<BOARD_ITEM*> items =
FindItemsFromSyncSelection( paramStr.substr( modeEnd + 1 ) );
m_syncingSchToPcbSelection = true; // recursion guard
m_probingSchToPcb = true; // recursion guard
if( selectConnections )
{
@ -516,7 +616,7 @@ void PCB_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
static_cast<void*>( &items ) );
}
m_syncingSchToPcbSelection = false;
m_probingSchToPcb = false;
}
break;

View File

@ -187,7 +187,7 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
m_SelLayerBox = nullptr;
m_show_layer_manager_tools = true;
m_supportsAutoSave = true;
m_syncingSchToPcbSelection = false;
m_probingSchToPcb = false;
// We don't know what state board was in when it was last saved, so we have to
// assume dirty
@ -778,6 +778,8 @@ void PCB_EDIT_FRAME::setupUIConditions()
ENABLE( SELECTION_CONDITIONS::OnlyTypes( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } ) ) );
mgr->SetConditions( PCB_ACTIONS::selectSameSheet,
ENABLE( SELECTION_CONDITIONS::OnlyTypes( { PCB_FOOTPRINT_T } ) ) );
mgr->SetConditions( PCB_ACTIONS::selectOnSchematic,
ENABLE( SELECTION_CONDITIONS::HasTypes( { PCB_PAD_T, PCB_FOOTPRINT_T, PCB_GROUP_T } ) ) );
SELECTION_CONDITION singleZoneCond = SELECTION_CONDITIONS::Count( 1 )

View File

@ -637,12 +637,25 @@ public:
void OnNetlistChanged( BOARD_NETLIST_UPDATER& aUpdater, bool* aRunDragCommand );
/**
* Send a message to the schematic editor so that it may move its cursor
* to a symbol with the same reference as the \a objectToSync.
* Send a message to the schematic editor to try to find schematic counterparts
* of specified PCB items and select them.
*
* @param objectToSync The object whose reference is used to synchronize Eeschema.
* @param aItems are the items to try to select on schematic.
* @param aFocusItem set to item to select and focus on even if selection can't be
* represented in Schematic editor fully.
* @param aForce select elements in Schematic editor whether or not the user has
* the select option chosen.
*/
void SendMessageToEESCHEMA( BOARD_ITEM* objectToSync );
void SendSelectItemsToSch( const std::deque<EDA_ITEM*>& aItems, EDA_ITEM* aFocusItem,
bool aForce );
/**
* Send a message to the schematic editor so that it may move its cursor
* to an item with the same reference as the \a aSyncItem and highlight it.
*
* @param aSyncItem The object whose reference is used to highlight in Eeschema.
*/
void SendCrossProbeItem( BOARD_ITEM* aSyncItem );
/**
* Send a net name to Eeschema for highlighting.
@ -795,7 +808,7 @@ public:
bool m_ZoneFillsDirty; // Board has been modified since last zone fill.
bool m_syncingSchToPcbSelection; // Recursion guard when synchronizing selection from schematic
bool m_probingSchToPcb; // Recursion guard when synchronizing selection from schematic
private:
friend struct PCB::IFACE;

View File

@ -1403,6 +1403,42 @@ int BOARD_EDITOR_CONTROL::ZoneDuplicate( const TOOL_EVENT& aEvent )
}
int BOARD_EDITOR_CONTROL::CrossProbeToSch( const TOOL_EVENT& aEvent )
{
doCrossProbePcbToSch( aEvent, false );
return 0;
}
int BOARD_EDITOR_CONTROL::ExplicitCrossProbeToSch( const TOOL_EVENT& aEvent )
{
doCrossProbePcbToSch( aEvent, true );
return 0;
}
void BOARD_EDITOR_CONTROL::doCrossProbePcbToSch( const TOOL_EVENT& aEvent, bool aForce )
{
// Don't get in an infinite loop PCB -> SCH -> PCB -> SCH -> ...
if( m_frame->m_probingSchToPcb )
return;
PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
const PCB_SELECTION& selection = selTool->GetSelection();
EDA_ITEM* focusItem = nullptr;
if( aEvent.Matches( EVENTS::PointSelectedEvent ) )
focusItem = selection.GetLastAddedItem();
m_frame->SendSelectItemsToSch( selection.GetItems(), focusItem, aForce );
// Update 3D viewer highlighting
m_frame->Update3DView( false, frame()->GetPcbNewSettings()->m_Display.m_Live3DRefresh );
}
int BOARD_EDITOR_CONTROL::EditFpInFpEditor( const TOOL_EVENT& aEvent )
{
PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
@ -1517,6 +1553,13 @@ void BOARD_EDITOR_CONTROL::setTransitions()
Go( &BOARD_EDITOR_CONTROL::EditFpInFpEditor, PCB_ACTIONS::editFpInFpEditor.MakeEvent() );
Go( &BOARD_EDITOR_CONTROL::EditFpInFpEditor, PCB_ACTIONS::editLibFpInFpEditor.MakeEvent() );
// Cross-select
Go( &BOARD_EDITOR_CONTROL::CrossProbeToSch, EVENTS::PointSelectedEvent );
Go( &BOARD_EDITOR_CONTROL::CrossProbeToSch, EVENTS::SelectedEvent );
Go( &BOARD_EDITOR_CONTROL::CrossProbeToSch, EVENTS::UnselectedEvent );
Go( &BOARD_EDITOR_CONTROL::CrossProbeToSch, EVENTS::ClearedEvent );
Go( &BOARD_EDITOR_CONTROL::ExplicitCrossProbeToSch, PCB_ACTIONS::selectOnSchematic.MakeEvent() );
// Other
Go( &BOARD_EDITOR_CONTROL::ToggleLockSelected, PCB_ACTIONS::toggleLock.MakeEvent() );
Go( &BOARD_EDITOR_CONTROL::LockSelected, PCB_ACTIONS::lock.MakeEvent() );

View File

@ -93,6 +93,12 @@ public:
int EditFpInFpEditor( const TOOL_EVENT& aEvent );
///< Notify Eeschema about selected items.
int CrossProbeToSch( const TOOL_EVENT& aEvent );
///< Equivalent to the above, but initiated by the user.
int ExplicitCrossProbeToSch( const TOOL_EVENT& aEvent );
/**
* Display a dialog to select a footprint to be added and allows the user to set its position.
*/
@ -128,6 +134,8 @@ private:
///< Set up handlers for various events.
void setTransitions() override;
void doCrossProbePcbToSch( const TOOL_EVENT& aEvent, bool aForce );
private:
PCB_EDIT_FRAME* m_frame;
bool m_inPlaceFootprint; // Re-entrancy guard for tool.

View File

@ -45,7 +45,6 @@ BOARD_INSPECTION_TOOL::BOARD_INSPECTION_TOOL() :
PCB_TOOL_BASE( "pcbnew.InspectionTool" ),
m_frame( nullptr )
{
m_probingSchToPcb = false;
m_dynamicData = nullptr;
}
@ -1245,39 +1244,18 @@ int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent )
}
int BOARD_INSPECTION_TOOL::CrossProbePcbToSch( const TOOL_EVENT& aEvent )
{
// Don't get in an infinite loop PCB -> SCH -> PCB -> SCH -> ...
if( m_probingSchToPcb || m_frame->m_syncingSchToPcbSelection )
return 0;
PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
const PCB_SELECTION& selection = selTool->GetSelection();
if( selection.Size() == 1 )
m_frame->SendMessageToEESCHEMA( static_cast<BOARD_ITEM*>( selection.Front() ) );
else
m_frame->SendMessageToEESCHEMA( nullptr );
// Update 3D viewer highlighting
m_frame->Update3DView( false, frame()->GetPcbNewSettings()->m_Display.m_Live3DRefresh );
return 0;
}
int BOARD_INSPECTION_TOOL::HighlightItem( const TOOL_EVENT& aEvent )
{
BOARD_ITEM* item = aEvent.Parameter<BOARD_ITEM*>();
m_probingSchToPcb = true; // recursion guard
m_frame->m_probingSchToPcb = true; // recursion guard
{
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
if( item )
m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, (void*) item );
}
m_probingSchToPcb = false;
m_frame->m_probingSchToPcb = false;
bool request3DviewRedraw = frame()->GetPcbNewSettings()->m_Display.m_Live3DRefresh;
@ -1363,7 +1341,7 @@ int BOARD_INSPECTION_TOOL::HighlightItem( const TOOL_EVENT& aEvent )
filter.lockedItems = saved;
// Clear the previous highlight
m_frame->SendMessageToEESCHEMA( nullptr );
//m_frame->SendMessageToEESCHEMA( nullptr );
bool highContrast = settings->GetHighContrast();
PCB_LAYER_ID contrastLayer = settings->GetPrimaryHighContrastLayer();
@ -1388,7 +1366,7 @@ int BOARD_INSPECTION_TOOL::HighlightItem( const TOOL_EVENT& aEvent )
BOARD_CONNECTED_ITEM* targetItem = static_cast<BOARD_CONNECTED_ITEM*>( collector[0] );
if( targetItem->Type() == PCB_PAD_T )
m_frame->SendMessageToEESCHEMA( targetItem );
m_frame->SendCrossProbeItem( targetItem );
net = targetItem->GetNetCode();
}
@ -1829,10 +1807,6 @@ void BOARD_INSPECTION_TOOL::doHideNet( int aNetCode, bool aHide )
void BOARD_INSPECTION_TOOL::setTransitions()
{
Go( &BOARD_INSPECTION_TOOL::CrossProbePcbToSch, EVENTS::SelectedEvent );
Go( &BOARD_INSPECTION_TOOL::CrossProbePcbToSch, EVENTS::UnselectedEvent );
Go( &BOARD_INSPECTION_TOOL::CrossProbePcbToSch, EVENTS::ClearedEvent );
Go( &BOARD_INSPECTION_TOOL::LocalRatsnestTool,
PCB_ACTIONS::localRatsnestTool.MakeEvent() );
Go( &BOARD_INSPECTION_TOOL::HideDynamicRatsnest,

View File

@ -57,9 +57,6 @@ public:
*/
int ShowStatisticsDialog( const TOOL_EVENT& aEvent );
///< Notify Eeschema about the selected item.
int CrossProbePcbToSch( const TOOL_EVENT& aEvent );
///< Highlight net belonging to the item under the cursor.
int HighlightNet( const TOOL_EVENT& aEvent );
@ -143,7 +140,6 @@ private:
private:
PCB_EDIT_FRAME* m_frame; // Pointer to the currently used edit frame.
bool m_probingSchToPcb; // Recursion guard when cross-probing to Eeschema
std::set<int> m_currentlyHighlighted; // Active net being highlighted, or -1 when off
std::set<int> m_lastHighlighted; // For toggling between last two highlighted nets

View File

@ -300,6 +300,7 @@ void DRC_TOOL::setTransitions()
Go( &DRC_TOOL::PrevMarker, ACTIONS::prevMarker.MakeEvent() );
Go( &DRC_TOOL::NextMarker, ACTIONS::nextMarker.MakeEvent() );
Go( &DRC_TOOL::ExcludeMarker, ACTIONS::excludeMarker.MakeEvent() );
Go( &DRC_TOOL::CrossProbe, EVENTS::PointSelectedEvent );
Go( &DRC_TOOL::CrossProbe, EVENTS::SelectedEvent );
}

View File

@ -1291,6 +1291,12 @@ TOOL_ACTION PCB_ACTIONS::selectSameSheet( "pcbnew.InteractiveSelection.SelectSam
_( "Selects all footprints and tracks in the same schematic sheet" ),
BITMAPS::select_same_sheet );
TOOL_ACTION PCB_ACTIONS::selectOnSchematic( "pcbnew.InteractiveSelection.SelectOnSchematic",
AS_GLOBAL, 0, "",
_( "Select on Schematic" ),
_( "Selects corresponding items in Schematic editor" ),
BITMAPS::select_same_sheet );
TOOL_ACTION PCB_ACTIONS::filterSelection( "pcbnew.InteractiveSelection.FilterSelection",
AS_GLOBAL, 0, "",
_( "Filter Selected Items..." ), _( "Remove items from the selection by type" ),

View File

@ -91,6 +91,9 @@ public:
/// Select all components on the same sheet as the selected footprint.
static TOOL_ACTION selectSameSheet;
/// Select symbols/pins on schematic corresponding to selected footprints/pads.
static TOOL_ACTION selectOnSchematic;
/// Filter the items in the current selection (invokes dialog)
static TOOL_ACTION filterSelection;

View File

@ -1464,6 +1464,7 @@ void PCB_CONTROL::setTransitions()
Go( &PCB_CONTROL::Paste, ACTIONS::paste.MakeEvent() );
Go( &PCB_CONTROL::Paste, ACTIONS::pasteSpecial.MakeEvent() );
Go( &PCB_CONTROL::UpdateMessagePanel, EVENTS::PointSelectedEvent );
Go( &PCB_CONTROL::UpdateMessagePanel, EVENTS::SelectedEvent );
Go( &PCB_CONTROL::UpdateMessagePanel, EVENTS::UnselectedEvent );
Go( &PCB_CONTROL::UpdateMessagePanel, EVENTS::ClearedEvent );

View File

@ -2425,6 +2425,7 @@ void PCB_POINT_EDITOR::setTransitions()
Go( &PCB_POINT_EDITOR::addCorner, PCB_ACTIONS::pointEditorAddCorner.MakeEvent() );
Go( &PCB_POINT_EDITOR::removeCorner, PCB_ACTIONS::pointEditorRemoveCorner.MakeEvent() );
Go( &PCB_POINT_EDITOR::modifiedSelection, EVENTS::SelectedItemsModified );
Go( &PCB_POINT_EDITOR::OnSelectionChange, EVENTS::PointSelectedEvent );
Go( &PCB_POINT_EDITOR::OnSelectionChange, EVENTS::SelectedEvent );
Go( &PCB_POINT_EDITOR::OnSelectionChange, EVENTS::UnselectedEvent );
Go( &PCB_POINT_EDITOR::changeEditMethod, ACTIONS::changeEditMethod.MakeEvent() );

View File

@ -86,6 +86,7 @@ public:
// This could be enabled if we have better logic for picking the target net with the mouse
// Add( PCB_ACTIONS::deselectNet );
Add( PCB_ACTIONS::selectSameSheet );
Add( PCB_ACTIONS::selectOnSchematic );
}
private:
@ -740,7 +741,7 @@ bool PCB_SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag,
}
}
bool anyAdded = false;
int addedCount = 0;
bool anySubtracted = false;
if( !m_additive && !m_subtractive && !m_exclusive_or )
@ -764,12 +765,17 @@ bool PCB_SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag,
else
{
select( collector[i] );
anyAdded = true;
addedCount++;
}
}
}
if( anyAdded )
if( addedCount == 1 )
{
m_toolMgr->ProcessEvent( EVENTS::PointSelectedEvent );
return true;
}
else if( addedCount > 1 )
{
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
return true;
@ -1536,12 +1542,12 @@ void PCB_SELECTION_TOOL::doSyncSelection( const std::vector<BOARD_ITEM*>& aItems
EDA_RECT bbox = m_selection.GetBoundingBox();
if( bbox.GetWidth() > 0 && bbox.GetHeight() > 0 )
if( bbox.GetWidth() != 0 && bbox.GetHeight() != 0 )
{
if( m_frame->GetPcbNewSettings()->m_CrossProbing.center_on_items )
{
if( m_frame->GetPcbNewSettings()->m_CrossProbing.zoom_to_fit )
zoomFitCrossProbeBBox( bbox );
ZoomFitCrossProbeBBox( bbox );
m_frame->FocusOnLocation( bbox.Centre() );
}
@ -1630,14 +1636,16 @@ void PCB_SELECTION_TOOL::zoomFitSelection()
}
void PCB_SELECTION_TOOL::zoomFitCrossProbeBBox( EDA_RECT bbox )
void PCB_SELECTION_TOOL::ZoomFitCrossProbeBBox( EDA_RECT bbox )
{
// Should recalculate the view to zoom in on the bbox.
auto view = getView();
if( bbox.GetWidth() == 0 && bbox.GetHeight() != 0 )
if( bbox.GetWidth() == 0 )
return;
bbox.Normalize();
//#define DEFAULT_PCBNEW_CODE // Un-comment for normal full zoom KiCad algorithm
#ifdef DEFAULT_PCBNEW_CODE
auto bbSize = bbox.Inflate( bbox.GetWidth() * 0.2f ).GetSize();

View File

@ -160,7 +160,7 @@ public:
void zoomFitSelection();
///< Zoom the screen to fit the bounding box for cross probing/selection sync.
void zoomFitCrossProbeBBox( EDA_RECT bbox );
void ZoomFitCrossProbeBBox( EDA_RECT bbox );
/**
* Enter the group at the head of the current selection.