diff --git a/common/settings/app_settings.cpp b/common/settings/app_settings.cpp
index 94394cfe8c..19f616cba8 100644
--- a/common/settings/app_settings.cpp
+++ b/common/settings/app_settings.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2020 Jon Evans <jon@craftyjon.com>
- * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software: you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -129,6 +129,9 @@ APP_SETTINGS_BASE::APP_SETTINGS_BASE( const std::string& aFilename, int aSchemaV
 
     addParamsForWindow( &m_Window, "window" );
 
+    m_params.emplace_back( new PARAM<bool>( "cross_probing.on_selection",
+            &m_CrossProbing.on_selection, true ) );
+
     m_params.emplace_back( new PARAM<bool>( "cross_probing.center_on_items",
             &m_CrossProbing.center_on_items, true ) );
 
diff --git a/common/string_utils.cpp b/common/string_utils.cpp
index 17a7d45103..694f367a57 100644
--- a/common/string_utils.cpp
+++ b/common/string_utils.cpp
@@ -174,6 +174,17 @@ wxString EscapeString( const wxString& aSource, ESCAPE_CONTEXT aContext )
             else
                 converted += c;
         }
+        else if( aContext == CTX_IPC )
+        {
+            if( c == '/' )
+                converted += "{slash}";
+            else if( c == ',' )
+                converted += "{comma}";
+            else if( c == '\"' )
+                converted += "{dblquote}";
+            else
+                converted += c;
+        }
         else if( aContext == CTX_QUOTED_STR )
         {
             if( c == '\"' )
@@ -276,6 +287,7 @@ wxString UnescapeString( const wxString& aSource )
             else if( token == wxS( "backslash" ) ) newbuf.append( wxS( "\\" ) );
             else if( token == wxS( "slash" ) )     newbuf.append( wxS( "/" ) );
             else if( token == wxS( "bar" ) )       newbuf.append( wxS( "|" ) );
+            else if( token == wxS( "comma" ) )     newbuf.append( wxS( "," ) );
             else if( token == wxS( "colon" ) )     newbuf.append( wxS( ":" ) );
             else if( token == wxS( "space" ) )     newbuf.append( wxS( " " ) );
             else if( token == wxS( "dollar" ) )    newbuf.append( wxS( "$" ) );
diff --git a/common/tool/selection_conditions.cpp b/common/tool/selection_conditions.cpp
index 2314493abe..954ad71bd1 100644
--- a/common/tool/selection_conditions.cpp
+++ b/common/tool/selection_conditions.cpp
@@ -62,6 +62,12 @@ SELECTION_CONDITION SELECTION_CONDITIONS::HasType( KICAD_T aType )
 }
 
 
+SELECTION_CONDITION SELECTION_CONDITIONS::HasTypes( const KICAD_T aTypes[] )
+{
+    return std::bind( &SELECTION_CONDITIONS::hasTypesFunc, _1, aTypes );
+}
+
+
 SELECTION_CONDITION SELECTION_CONDITIONS::OnlyType( KICAD_T aType )
 {
     return std::bind( &SELECTION_CONDITIONS::onlyTypeFunc, _1, aType );
@@ -94,7 +100,10 @@ SELECTION_CONDITION SELECTION_CONDITIONS::LessThan( int aNumber )
 
 bool SELECTION_CONDITIONS::hasTypeFunc( const SELECTION& aSelection, KICAD_T aType )
 {
-    for( const auto& item : aSelection )
+    if( aSelection.Empty() )
+        return false;
+
+    for( const EDA_ITEM* item : aSelection )
     {
         if( item->Type() == aType )
             return true;
@@ -104,6 +113,21 @@ bool SELECTION_CONDITIONS::hasTypeFunc( const SELECTION& aSelection, KICAD_T aTy
 }
 
 
+bool SELECTION_CONDITIONS::hasTypesFunc( const SELECTION& aSelection, const KICAD_T aTypes[] )
+{
+    if( aSelection.Empty() )
+        return false;
+
+    for( const EDA_ITEM* item : aSelection )
+    {
+        if( item->IsType( aTypes ) )
+            return true;
+    }
+
+    return false;
+}
+
+
 bool SELECTION_CONDITIONS::onlyTypeFunc( const SELECTION& aSelection, KICAD_T aType )
 {
     if( aSelection.Empty() )
@@ -111,7 +135,7 @@ bool SELECTION_CONDITIONS::onlyTypeFunc( const SELECTION& aSelection, KICAD_T aT
 
     KICAD_T types[] = { aType, EOT };
 
-    for( const auto& item : aSelection )
+    for( const EDA_ITEM* item : aSelection )
     {
         if( !item->IsType( types ) )
             return false;
@@ -126,7 +150,7 @@ bool SELECTION_CONDITIONS::onlyTypesFunc( const SELECTION& aSelection, const KIC
     if( aSelection.Empty() )
         return false;
 
-    for( const auto& item : aSelection )
+    for( const EDA_ITEM* item : aSelection )
     {
         if( !item->IsType( aTypes ) )
             return false;
diff --git a/eeschema/cross-probing.cpp b/eeschema/cross-probing.cpp
index d14b79e5f0..835f88c91e 100644
--- a/eeschema/cross-probing.cpp
+++ b/eeschema/cross-probing.cpp
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
  * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
- * Copyright (C) 2004-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2004-2022 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -473,6 +473,116 @@ void SCH_EDIT_FRAME::SendMessageToPCBNEW( EDA_ITEM* aObjectToSync, SCH_SYMBOL* a
 }
 
 
+std::string FormatProbeItems( bool aSelectConnections, const std::deque<EDA_ITEM*>& aItems )
+{
+    std::string        command = "";
+    std::set<wxString> parts;
+
+    for( EDA_ITEM* item : aItems )
+    {
+        switch( item->Type() )
+        {
+        case SCH_SYMBOL_T:
+        {
+            SCH_SYMBOL* symbol = (SCH_SYMBOL*) item;
+
+            wxString ref = symbol->GetField( REFERENCE_FIELD )->GetText();
+
+            parts.insert( "F" + EscapeString( ref, CTX_IPC ) );
+
+            break;
+        }
+
+        case SCH_SHEET_T:
+        {
+            // For cross probing, we need the full path of the sheet, because
+            // in complex hierarchies the sheet uuid of not unique
+            SCH_SHEET* sheet = (SCH_SHEET*) item;
+            wxString   full_path;
+
+            SCH_SHEET* parent = sheet;
+
+            while( ( parent = dynamic_cast<SCH_SHEET*>( parent->GetParent() ) ) )
+            {
+                if( parent->GetParent() && parent->GetParent()->Type() == SCH_SHEET_T )
+                {
+                    // The root sheet has no parent sheet and path is just "/"
+
+                    full_path.Prepend( parent->m_Uuid.AsString() );
+                    full_path.Prepend( "/" );
+                }
+            }
+
+            full_path += "/" + sheet->m_Uuid.AsString();
+
+            parts.insert( "S" + full_path );
+
+            break;
+        }
+
+        case SCH_PIN_T:
+        {
+            SCH_PIN*    pin = (SCH_PIN*) item;
+            SCH_SYMBOL* symbol = pin->GetParentSymbol();
+
+            wxString ref = symbol->GetField( REFERENCE_FIELD )->GetText();
+
+            parts.insert( "P" + EscapeString( ref, CTX_IPC ) + "/"
+                          + EscapeString( pin->GetShownNumber(), CTX_IPC ) );
+
+            break;
+        }
+
+        default: break;
+        }
+    }
+
+
+    if( !parts.empty() )
+    {
+        command = "$SELECT: ";
+
+        if( aSelectConnections )
+            command += "1";
+        else
+            command += "0";
+
+        command += ",";
+
+        for( wxString part : parts )
+        {
+            command += part;
+            command += ",";
+        }
+
+        command.pop_back();
+    }
+
+    return command;
+}
+
+
+void SCH_EDIT_FRAME::SendSelectItems( bool aSelectConnections, const std::deque<EDA_ITEM*>& aItems )
+{
+    std::string packet = FormatProbeItems( aSelectConnections, aItems );
+
+    if( !packet.empty() )
+    {
+        if( Kiface().IsSingle() )
+        {
+            SendCommand( MSG_TO_PCB, packet );
+        }
+        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_PCB_EDITOR, MAIL_SELECTION, packet, this );
+        }
+    }
+}
+
+
 void SCH_EDIT_FRAME::SendCrossProbeNetName( const wxString& aNetName )
 {
     // The command is a keyword followed by a quoted string.
diff --git a/eeschema/dialogs/panel_eeschema_display_options.cpp b/eeschema/dialogs/panel_eeschema_display_options.cpp
index 24aa21ed6e..8bad59ae92 100644
--- a/eeschema/dialogs/panel_eeschema_display_options.cpp
+++ b/eeschema/dialogs/panel_eeschema_display_options.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2009 Wayne Stambaugh <stambaughw@gmail.com>
- * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -66,6 +66,7 @@ void PANEL_EESCHEMA_DISPLAY_OPTIONS::loadEEschemaSettings( EESCHEMA_SETTINGS* cf
     m_selWidthCtrl->SetValue( cfg->m_Selection.selection_thickness );
     m_highlightWidthCtrl->SetValue( cfg->m_Selection.highlight_thickness );
 
+    m_checkCrossProbeOnSelection->SetValue( cfg->m_CrossProbing.on_selection );
     m_checkCrossProbeCenter->SetValue( cfg->m_CrossProbing.center_on_items );
     m_checkCrossProbeZoom->SetValue( cfg->m_CrossProbing.zoom_to_fit );
     m_checkCrossProbeAutoHighlight->SetValue( cfg->m_CrossProbing.auto_highlight );
@@ -106,9 +107,10 @@ bool PANEL_EESCHEMA_DISPLAY_OPTIONS::TransferDataFromWindow()
     cfg->m_Selection.selection_thickness = KiROUND( m_selWidthCtrl->GetValue() );
     cfg->m_Selection.highlight_thickness = KiROUND( m_highlightWidthCtrl->GetValue() );
 
+    cfg->m_CrossProbing.on_selection = m_checkCrossProbeOnSelection->GetValue();
     cfg->m_CrossProbing.center_on_items = m_checkCrossProbeCenter->GetValue();
-    cfg->m_CrossProbing.zoom_to_fit     = m_checkCrossProbeZoom->GetValue();
-    cfg->m_CrossProbing.auto_highlight  = m_checkCrossProbeAutoHighlight->GetValue();
+    cfg->m_CrossProbing.zoom_to_fit = m_checkCrossProbeZoom->GetValue();
+    cfg->m_CrossProbing.auto_highlight = m_checkCrossProbeAutoHighlight->GetValue();
 
     m_galOptsPanel->TransferDataFromWindow();
 
diff --git a/eeschema/dialogs/panel_eeschema_display_options_base.cpp b/eeschema/dialogs/panel_eeschema_display_options_base.cpp
index 66e877d870..10e422a017 100644
--- a/eeschema/dialogs/panel_eeschema_display_options_base.cpp
+++ b/eeschema/dialogs/panel_eeschema_display_options_base.cpp
@@ -1,5 +1,5 @@
 ///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Oct 26 2018)
+// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
 // http://www.wxformbuilder.org/
 //
 // PLEASE DO *NOT* EDIT THIS FILE!
@@ -112,6 +112,10 @@ PANEL_EESCHEMA_DISPLAY_OPTIONS_BASE::PANEL_EESCHEMA_DISPLAY_OPTIONS_BASE( wxWind
 	wxStaticBoxSizer* sbSizer31;
 	sbSizer31 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Cross-probing") ), wxVERTICAL );
 
+	m_checkCrossProbeOnSelection = new wxCheckBox( sbSizer31->GetStaticBox(), wxID_ANY, _("Cross-probe on selection"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_checkCrossProbeOnSelection->SetValue(true);
+	sbSizer31->Add( m_checkCrossProbeOnSelection, 0, wxALL, 5 );
+
 	m_checkCrossProbeCenter = new wxCheckBox( sbSizer31->GetStaticBox(), wxID_ANY, _("Center view on cross-probed items"), wxDefaultPosition, wxDefaultSize, 0 );
 	m_checkCrossProbeCenter->SetValue(true);
 	sbSizer31->Add( m_checkCrossProbeCenter, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
diff --git a/eeschema/dialogs/panel_eeschema_display_options_base.fbp b/eeschema/dialogs/panel_eeschema_display_options_base.fbp
index a5bee52063..744befaf8f 100644
--- a/eeschema/dialogs/panel_eeschema_display_options_base.fbp
+++ b/eeschema/dialogs/panel_eeschema_display_options_base.fbp
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
 <wxFormBuilder_Project>
-    <FileVersion major="1" minor="15" />
+    <FileVersion major="1" minor="16" />
     <object class="Project" expanded="1">
         <property name="class_decoration"></property>
         <property name="code_generation">C++</property>
@@ -14,6 +14,7 @@
         <property name="file">panel_eeschema_display_options_base</property>
         <property name="first_id">1000</property>
         <property name="help_provider">none</property>
+        <property name="image_path_wrapper_function_name"></property>
         <property name="indent_with_spaces"></property>
         <property name="internationalize">1</property>
         <property name="name">PanelEeschemaDisplayOptions</property>
@@ -25,6 +26,7 @@
         <property name="skip_php_events">1</property>
         <property name="skip_python_events">1</property>
         <property name="ui_table">UI</property>
+        <property name="use_array_enum">0</property>
         <property name="use_enum">1</property>
         <property name="use_microsoft_bom">0</property>
         <object class="Panel" expanded="1">
@@ -46,6 +48,7 @@
             <property name="size">-1,-1</property>
             <property name="subclass">RESETTABLE_PANEL; widgets/resettable_panel.h; Not forward_declare</property>
             <property name="tooltip"></property>
+            <property name="two_step_creation">0</property>
             <property name="window_extra_style"></property>
             <property name="window_name"></property>
             <property name="window_style">wxTAB_TRAVERSAL</property>
@@ -1169,6 +1172,70 @@
                                 <property name="orient">wxVERTICAL</property>
                                 <property name="parent">1</property>
                                 <property name="permission">none</property>
+                                <object class="sizeritem" expanded="1">
+                                    <property name="border">5</property>
+                                    <property name="flag">wxALL</property>
+                                    <property name="proportion">0</property>
+                                    <object class="wxCheckBox" expanded="1">
+                                        <property name="BottomDockable">1</property>
+                                        <property name="LeftDockable">1</property>
+                                        <property name="RightDockable">1</property>
+                                        <property name="TopDockable">1</property>
+                                        <property name="aui_layer"></property>
+                                        <property name="aui_name"></property>
+                                        <property name="aui_position"></property>
+                                        <property name="aui_row"></property>
+                                        <property name="best_size"></property>
+                                        <property name="bg"></property>
+                                        <property name="caption"></property>
+                                        <property name="caption_visible">1</property>
+                                        <property name="center_pane">0</property>
+                                        <property name="checked">1</property>
+                                        <property name="close_button">1</property>
+                                        <property name="context_help"></property>
+                                        <property name="context_menu">1</property>
+                                        <property name="default_pane">0</property>
+                                        <property name="dock">Dock</property>
+                                        <property name="dock_fixed">0</property>
+                                        <property name="docking">Left</property>
+                                        <property name="enabled">1</property>
+                                        <property name="fg"></property>
+                                        <property name="floatable">1</property>
+                                        <property name="font"></property>
+                                        <property name="gripper">0</property>
+                                        <property name="hidden">0</property>
+                                        <property name="id">wxID_ANY</property>
+                                        <property name="label">Cross-probe on selection</property>
+                                        <property name="max_size"></property>
+                                        <property name="maximize_button">0</property>
+                                        <property name="maximum_size"></property>
+                                        <property name="min_size"></property>
+                                        <property name="minimize_button">0</property>
+                                        <property name="minimum_size"></property>
+                                        <property name="moveable">1</property>
+                                        <property name="name">m_checkCrossProbeOnSelection</property>
+                                        <property name="pane_border">1</property>
+                                        <property name="pane_position"></property>
+                                        <property name="pane_size"></property>
+                                        <property name="permission">protected</property>
+                                        <property name="pin_button">1</property>
+                                        <property name="pos"></property>
+                                        <property name="resize">Resizable</property>
+                                        <property name="show">1</property>
+                                        <property name="size"></property>
+                                        <property name="style"></property>
+                                        <property name="subclass">; ; forward_declare</property>
+                                        <property name="toolbar_pane">0</property>
+                                        <property name="tooltip"></property>
+                                        <property name="validator_data_type"></property>
+                                        <property name="validator_style">wxFILTER_NONE</property>
+                                        <property name="validator_type">wxDefaultValidator</property>
+                                        <property name="validator_variable"></property>
+                                        <property name="window_extra_style"></property>
+                                        <property name="window_name"></property>
+                                        <property name="window_style"></property>
+                                    </object>
+                                </object>
                                 <object class="sizeritem" expanded="1">
                                     <property name="border">5</property>
                                     <property name="flag">wxBOTTOM|wxRIGHT|wxLEFT</property>
diff --git a/eeschema/dialogs/panel_eeschema_display_options_base.h b/eeschema/dialogs/panel_eeschema_display_options_base.h
index 749bd7f211..b7f10161db 100644
--- a/eeschema/dialogs/panel_eeschema_display_options_base.h
+++ b/eeschema/dialogs/panel_eeschema_display_options_base.h
@@ -1,5 +1,5 @@
 ///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Oct 26 2018)
+// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
 // http://www.wxformbuilder.org/
 //
 // PLEASE DO *NOT* EDIT THIS FILE!
@@ -54,6 +54,7 @@ class PANEL_EESCHEMA_DISPLAY_OPTIONS_BASE : public RESETTABLE_PANEL
 		wxStaticText* m_highlightColorNote;
 		wxStaticText* m_highlightWidthLabel;
 		wxSpinCtrlDouble* m_highlightWidthCtrl;
+		wxCheckBox* m_checkCrossProbeOnSelection;
 		wxCheckBox* m_checkCrossProbeCenter;
 		wxCheckBox* m_checkCrossProbeZoom;
 		wxCheckBox* m_checkCrossProbeAutoHighlight;
@@ -61,6 +62,7 @@ class PANEL_EESCHEMA_DISPLAY_OPTIONS_BASE : public RESETTABLE_PANEL
 	public:
 
 		PANEL_EESCHEMA_DISPLAY_OPTIONS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL, const wxString& name = wxEmptyString );
+
 		~PANEL_EESCHEMA_DISPLAY_OPTIONS_BASE();
 
 };
diff --git a/eeschema/sch_edit_frame.h b/eeschema/sch_edit_frame.h
index 9a7d586ddc..3ff744642f 100644
--- a/eeschema/sch_edit_frame.h
+++ b/eeschema/sch_edit_frame.h
@@ -307,6 +307,14 @@ public:
      */
     void SendMessageToPCBNEW( EDA_ITEM* aObjectToSync, SCH_SYMBOL* aPart );
 
+    /**
+     * Sends items to Pcbnew for selection
+     *
+     * @param aSelectConnections - set to select connected tracks/vias too
+     * @param aElements are the items to select
+     */
+    void SendSelectItems( bool aSelectConnections, const std::deque<EDA_ITEM*>& aElements );
+
     /**
      * Sends a net name to Pcbnew for highlighting
      *
diff --git a/eeschema/tools/ee_actions.cpp b/eeschema/tools/ee_actions.cpp
index 6e2467e241..4988728b14 100644
--- a/eeschema/tools/ee_actions.cpp
+++ b/eeschema/tools/ee_actions.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2019 CERN
- * Copyright (C) 2019-2020 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2019-2022 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -641,9 +641,10 @@ TOOL_ACTION EE_ACTIONS::generateBOM( "eeschema.EditorControl.generateBOM",
         _( "Generate BOM..." ), _( "Generate a bill of materials for the current schematic" ),
         BITMAPS::post_bom );
 
-TOOL_ACTION EE_ACTIONS::explicitCrossProbe( "eeschema.EditorControl.explicitCrossProbe",
+TOOL_ACTION EE_ACTIONS::selectOnPCB( "eeschema.EditorControl.selectOnPCB",
         AS_GLOBAL, 0, "",
-        _( "Highlight on PCB" ), _( "Highlight corresponding items in PCB editor" ),
+        _( "Select on PCB" ),
+        _( "Select corresponding items in PCB editor" ),
         BITMAPS::select_same_sheet );
 
 TOOL_ACTION EE_ACTIONS::toggleHiddenPins( "eeschema.EditorControl.showHiddenPins",
diff --git a/eeschema/tools/ee_actions.h b/eeschema/tools/ee_actions.h
index a2523d61fc..763c0fdfc8 100644
--- a/eeschema/tools/ee_actions.h
+++ b/eeschema/tools/ee_actions.h
@@ -201,7 +201,7 @@ public:
     static TOOL_ACTION toggleERCExclusions;
     static TOOL_ACTION toggleSyncedPinsMode;
     static TOOL_ACTION restartMove;
-    static TOOL_ACTION explicitCrossProbe;
+    static TOOL_ACTION selectOnPCB;
     static TOOL_ACTION pushPinLength;
     static TOOL_ACTION pushPinNameSize;
     static TOOL_ACTION pushPinNumSize;
diff --git a/eeschema/tools/ee_selection_tool.cpp b/eeschema/tools/ee_selection_tool.cpp
index f0195a7978..7577fb0d5e 100644
--- a/eeschema/tools/ee_selection_tool.cpp
+++ b/eeschema/tools/ee_selection_tool.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2019 CERN
- * Copyright (C) 2019-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2019-2022 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -171,11 +171,14 @@ bool EE_SELECTION_TOOL::Init()
                                         SCH_PIN_T,
                                         EOT };
 
+    static KICAD_T crossProbingTypes[] = { SCH_SYMBOL_T, SCH_PIN_T, SCH_SHEET_T, EOT };
+
     auto wireSelection =      E_C::MoreThan( 0 ) && E_C::OnlyType( SCH_ITEM_LOCATE_WIRE_T );
     auto busSelection =       E_C::MoreThan( 0 ) && E_C::OnlyType( SCH_ITEM_LOCATE_BUS_T );
     auto wireOrBusSelection = E_C::MoreThan( 0 ) && E_C::OnlyTypes( wireOrBusTypes );
     auto connectedSelection = E_C::MoreThan( 0 ) && E_C::OnlyTypes( connectedTypes );
-    auto sheetSelection =     E_C::Count( 1 )    && E_C::OnlyType( SCH_SHEET_T );
+    auto sheetSelection = E_C::Count( 1 ) && E_C::OnlyType( SCH_SHEET_T );
+    auto crossProbingSelection = E_C::MoreThan( 0 ) && E_C::HasTypes( crossProbingTypes );
 
     auto schEditSheetPageNumberCondition =
             [&] ( const SELECTION& aSel )
@@ -211,7 +214,7 @@ bool EE_SELECTION_TOOL::Init()
     auto& menu = m_menu.GetMenu();
 
     menu.AddItem( EE_ACTIONS::enterSheet,         sheetSelection && EE_CONDITIONS::Idle, 1 );
-    menu.AddItem( EE_ACTIONS::explicitCrossProbe, sheetSelection && EE_CONDITIONS::Idle, 1 );
+    menu.AddItem( EE_ACTIONS::selectOnPCB,        crossProbingSelection && EE_CONDITIONS::Idle, 1 );
     menu.AddItem( EE_ACTIONS::leaveSheet,         belowRootSheetCondition, 1 );
 
     menu.AddSeparator( 100 );
diff --git a/eeschema/tools/sch_editor_control.cpp b/eeschema/tools/sch_editor_control.cpp
index 7aca89b7d4..3935a8be25 100644
--- a/eeschema/tools/sch_editor_control.cpp
+++ b/eeschema/tools/sch_editor_control.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2019 CERN
- * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -672,63 +672,14 @@ void SCH_EDITOR_CONTROL::doCrossProbeSchToPcb( const TOOL_EVENT& aEvent, bool aF
     if( m_probingPcbToSch )
         return;
 
-    EE_SELECTION_TOOL* selTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
-    SCH_ITEM*          item = nullptr;
-    SCH_SYMBOL*        symbol = nullptr;
-
-    if( aForce )
-    {
-        EE_SELECTION& selection = selTool->RequestSelection();
-
-        if( selection.GetSize() >= 1 )
-            item = (SCH_ITEM*) selection.Front();
-    }
-    else
-    {
-        EE_SELECTION& selection = selTool->GetSelection();
-
-        if( selection.GetSize() >= 1 )
-            item = (SCH_ITEM*) selection.Front();
-    }
-
-    if( !item )
-    {
-        if( aForce )
-            m_frame->SendMessageToPCBNEW( nullptr, nullptr );
-
+    if( !aForce && !m_frame->eeconfig()->m_CrossProbing.on_selection )
         return;
-    }
 
+    EE_SELECTION_TOOL*      selTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
 
-    switch( item->Type() )
-    {
-    case SCH_FIELD_T:
-    case LIB_FIELD_T:
-        if( item->GetParent() && item->GetParent()->Type() == SCH_SYMBOL_T )
-        {
-            symbol = (SCH_SYMBOL*) item->GetParent();
-            m_frame->SendMessageToPCBNEW( item, symbol );
-        }
-        break;
+    EE_SELECTION& selection = aForce ? selTool->RequestSelection() : selTool->GetSelection();
 
-    case SCH_SYMBOL_T:
-        symbol = (SCH_SYMBOL*) item;
-        m_frame->SendMessageToPCBNEW( item, symbol );
-        break;
-
-    case SCH_PIN_T:
-        symbol = (SCH_SYMBOL*) item->GetParent();
-        m_frame->SendMessageToPCBNEW( static_cast<SCH_PIN*>( item ), symbol );
-        break;
-
-    case SCH_SHEET_T:
-        if( aForce )
-            m_frame->SendMessageToPCBNEW( item, nullptr );
-        break;
-
-    default:
-        break;
-    }
+    m_frame->SendSelectItems( false, selection.GetItems() );
 }
 
 
@@ -2318,7 +2269,7 @@ void SCH_EDITOR_CONTROL::setTransitions()
     Go( &SCH_EDITOR_CONTROL::CrossProbeToPcb,       EVENTS::SelectedEvent );
     Go( &SCH_EDITOR_CONTROL::CrossProbeToPcb,       EVENTS::UnselectedEvent );
     Go( &SCH_EDITOR_CONTROL::CrossProbeToPcb,       EVENTS::ClearedEvent );
-    Go( &SCH_EDITOR_CONTROL::ExplicitCrossProbeToPcb, EE_ACTIONS::explicitCrossProbe.MakeEvent() );
+    Go( &SCH_EDITOR_CONTROL::ExplicitCrossProbeToPcb, EE_ACTIONS::selectOnPCB.MakeEvent() );
 
 #ifdef KICAD_SPICE
     Go( &SCH_EDITOR_CONTROL::SimProbe,              EE_ACTIONS::simProbe.MakeEvent() );
diff --git a/eeschema/tools/sch_editor_control.h b/eeschema/tools/sch_editor_control.h
index a15670f2da..531acbf87e 100644
--- a/eeschema/tools/sch_editor_control.h
+++ b/eeschema/tools/sch_editor_control.h
@@ -84,8 +84,7 @@ public:
     ///< Notifies pcbnew about the selected item.
     int CrossProbeToPcb( const TOOL_EVENT& aEvent );
 
-    ///< Equivalent to the above, but initiated by the user.  We also do SCH_SHEETs on this
-    ///< one (they're too slow on big projects for the auto version above).
+    ///< Equivalent to the above, but initiated by the user.
     int ExplicitCrossProbeToPcb( const TOOL_EVENT& aEvent );
 
 #ifdef KICAD_SPICE
@@ -151,7 +150,7 @@ public:
      * Find a symbol in the schematic and an item in this symbol.
      *
      * @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 
+     * @param aReference The symbol reference designator to find, or to display in
      *                   status bar if aPath is specified
      * @param aSearchHierarchy If false, search the current sheet only.  Otherwise,
      *                         the entire hierarchy
diff --git a/include/mail_type.h b/include/mail_type.h
index 9ceee272d9..12daf41c42 100644
--- a/include/mail_type.h
+++ b/include/mail_type.h
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2014 CERN
- * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.TXT for contributors.
+ * Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.TXT for contributors.
  * @author Maciej Suminski <maciej.suminski@cern.ch>
  *
  * This program is free software; you can redistribute it and/or
@@ -37,6 +37,7 @@
 enum MAIL_T
 {
     MAIL_CROSS_PROBE,              // PCB<->SCH, CVPCB->SCH cross-probing.
+    MAIL_SELECTION,                // SCH->PCB selection synchronization.
     MAIL_ASSIGN_FOOTPRINTS,        // CVPCB->SCH footprint stuffing
     MAIL_SCH_SAVE,                 // CVPCB->SCH save the schematic
     MAIL_EESCHEMA_NETLIST,         // SCH->CVPCB netlist immediately after launching CVPCB
diff --git a/include/settings/app_settings.h b/include/settings/app_settings.h
index f5eee63526..8e23dbc42a 100644
--- a/include/settings/app_settings.h
+++ b/include/settings/app_settings.h
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2020 Jon Evans <jon@craftyjon.com>
- * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software: you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -29,6 +29,7 @@
  */
 struct CROSS_PROBING_SETTINGS
 {
+    bool on_selection;  ///< Synchronize the selection for multiple items too
     bool center_on_items; ///< Automatically pan to cross-probed items
     bool zoom_to_fit;     ///< Zoom to fit items (ignored if center_on_items is off)
     bool auto_highlight;  ///< Automatically turn on highlight mode in the target frame
diff --git a/include/string_utils.h b/include/string_utils.h
index 3e9e574541..f41e128ec8 100644
--- a/include/string_utils.h
+++ b/include/string_utils.h
@@ -53,6 +53,7 @@ enum ESCAPE_CONTEXT
 {
     CTX_NETNAME,
     CTX_LIBID,
+    CTX_IPC,
     CTX_QUOTED_STR,
     CTX_LINE,
     CTX_FILENAME,
diff --git a/include/tool/selection_conditions.h b/include/tool/selection_conditions.h
index d4d482764f..186c799237 100644
--- a/include/tool/selection_conditions.h
+++ b/include/tool/selection_conditions.h
@@ -131,6 +131,16 @@ public:
      */
     static SELECTION_CONDITION HasType( KICAD_T aType );
 
+    /**
+     * Create a functor that tests if among the selected items there is at least one of a
+     * given types.
+     *
+     * @param aTypes is an array containing types that are searched. It has to be ended with
+     *               #KICAD_T::EOT as end marker.
+     * @return Functor testing for presence of items of a given types.
+     */
+    static SELECTION_CONDITION HasTypes( const KICAD_T aTypes[] );
+
     /**
      * Create a functor that tests if the selected items are *only* of given type.
      *
@@ -179,6 +189,9 @@ private:
     ///< Helper function used by HasType()
     static bool hasTypeFunc( const SELECTION& aSelection, KICAD_T aType );
 
+    ///< Helper function used by HasTypes()
+    static bool hasTypesFunc( const SELECTION& aSelection, const KICAD_T aTypes[] );
+
     ///< Helper function used by OnlyType()
     static bool onlyTypeFunc( const SELECTION& aSelection, KICAD_T aType );
 
diff --git a/pcbnew/cross-probing.cpp b/pcbnew/cross-probing.cpp
index 87dac9f59e..ed717c63e4 100644
--- a/pcbnew/cross-probing.cpp
+++ b/pcbnew/cross-probing.cpp
@@ -1,7 +1,7 @@
 /*
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
- * Copyright (C) 2019-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2019-2022 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -41,6 +41,7 @@
 #include <eda_dde.h>
 #include <kiface_base.h>
 #include <kiway_express.h>
+#include <string_utils.h>
 #include <netlist_reader/pcb_netlist.h>
 #include <netlist_reader/board_netlist_updater.h>
 #include <painter.h>
@@ -277,133 +278,7 @@ void PCB_EDIT_FRAME::ExecuteRemoteCommand( const char* cmdline )
     {
         if( crossProbingSettings.zoom_to_fit )
         {
-//#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();
-            auto screenSize = view->ToWorld( GetCanvas()->GetClientSize(), false );
-
-            // The "fabs" on x ensures the right answer when the view is flipped
-            screenSize.x = std::max( 10.0, fabs( screenSize.x ) );
-            screenSize.y = std::max( 10.0, screenSize.y );
-            double ratio = std::max( fabs( bbSize.x / screenSize.x ),
-                                     fabs( bbSize.y / screenSize.y ) );
-
-            // Try not to zoom on every cross-probe; it gets very noisy
-            if( crossProbingSettings.zoom_to_fit && ( ratio < 0.5 || ratio > 1.0 ) )
-                view->SetScale( view->GetScale() / ratio );
- #endif // DEFAULT_PCBNEW_CODE
-
-#ifndef DEFAULT_PCBNEW_CODE  // Do the scaled zoom
-            auto bbSize     = bbox.Inflate( bbox.GetWidth() * 0.2f ).GetSize();
-            auto screenSize = view->ToWorld( GetCanvas()->GetClientSize(), false );
-
-            // This code tries to come up with a zoom factor that doesn't simply zoom in
-            // to the cross probed component, 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 footprint (like a processor) or a small footprint (like a resistor).
-            // This ratio is not useful by itself as a scaling factor.  It must be "bent" to
-            // provide good scaling at varying component sizes.  Bigger components need less
-            // scaling than small ones.
-            double currTextHeight = Millimeter2iu( DEFAULT_TEXT_SIZE );
-
-            double compRatio     = bbSize.y / currTextHeight; // Ratio of component to text height
-
-            // This will end up as the scaling factor we apply to "ratio".
-            double compRatioBent = 1.0;
-
-            // This is similar to the original KiCad code that scaled the zoom to make sure
-            // components were visible on screen.  It's simply a ratio of screen size to
-            // component size, and its job is to zoom in to make the component fullscreen.
-            // Earlier in the code the component BBox is given a 20% margin to add some
-            // breathing room. We compare the height of this enlarged component bbox to the
-            // default text height.  If a component will end up with the sides clipped, we
-            // adjust later to make sure it fits on screen.
-            //
-            // The "fabs" on x ensures the right answer when the view is flipped
-            screenSize.x = std::max( 10.0, fabs( 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 ) );
-
-            // LUT to scale zoom ratio to provide reasonable schematic context.  Must work
-            // with footprints of varying sizes (e.g. 0402 package and 200 pin BGA).
-            // "first" is used as the input and "second" as the output
-            //
-            // "first" = compRatio (footprint height / default text height)
-            // "second" = Amount to scale ratio by
-            std::vector<std::pair<double, double>> lut{
-                { 1, 8 },
-                { 1.5, 5 },
-                { 3, 3 },
-                { 4.5, 2.5 },
-                { 8, 2.0 },
-                { 12, 1.7 },
-                { 16, 1.5 },
-                { 24, 1.3 },
-                { 32, 1.0 },
-            };
-
-
-            std::vector<std::pair<double, double>>::iterator it;
-
-            compRatioBent = lut.back().second; // Large component default
-
-            if( compRatio >= lut.front().first )
-            {
-                // Use LUT to do linear interpolation of "compRatio" within "first", then
-                // use that result to linearly interpolate "second" which gives the scaling
-                // factor needed.
-
-                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 component default
-            }
-
-            // 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.  This will apply to parts that
-            // are much wider than they are tall.
-
-            if( bbSize.x > screenSize.x * ratio * compRatioBent )
-            {
-                // Use standard KiCad zoom algorithm for parts too wide to fit 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 )
-                view->SetScale( view->GetScale() / ratio );
-#endif // ifndef DEFAULT_PCBNEW_CODE
+            GetToolManager()->GetTool<PCB_SELECTION_TOOL>()->zoomFitCrossProbeBBox( bbox );
         }
 
         FocusOnLocation( (wxPoint) bbox.Centre() );
@@ -510,6 +385,77 @@ void PCB_EDIT_FRAME::SendCrossProbeNetName( const wxString& aNetName )
 }
 
 
+std::vector<BOARD_ITEM*> PCB_EDIT_FRAME::FindItemsFromSyncSelection( std::string syncStr )
+{
+    wxArrayString syncArray = wxStringTokenize( syncStr, "," );
+
+    std::vector<BOARD_ITEM*> items;
+
+    for( FOOTPRINT* footprint : GetBoard()->Footprints() )
+    {
+        if( footprint == nullptr )
+            continue;
+
+        wxString fpSheetPath = footprint->GetPath().AsString().BeforeLast( '/' );
+        wxString fpUUID = footprint->m_Uuid.AsString();
+
+        if( fpSheetPath.IsEmpty() )
+            fpSheetPath += '/';
+
+        if( fpUUID.empty() )
+            continue;
+
+        wxString fpRefEscaped = EscapeString( footprint->GetReference(), CTX_IPC );
+
+        for( wxString syncEntry : syncArray )
+        {
+            if( syncEntry.empty() )
+                continue;
+
+            wxString syncData = syncEntry.substr( 1 );
+
+            switch( syncEntry.GetChar( 0 ).GetValue() )
+            {
+            case 'S': // Select sheet with subsheets: S<Sheet path>
+                if( fpSheetPath.StartsWith( syncData ) )
+                {
+                    items.push_back( footprint );
+                }
+                break;
+            case 'F': // Select footprint: F<Reference>
+                if( syncData == fpRefEscaped )
+                {
+                    items.push_back( footprint );
+                }
+                break;
+            case 'P': // Select pad: P<Footprint reference>/<Pad number>
+            {
+                if( syncData.StartsWith( fpRefEscaped ) )
+                {
+                    wxString selectPadNumberEscaped =
+                            syncData.substr( fpRefEscaped.size() + 1 ); // Skips the slash
+
+                    wxString selectPadNumber = UnescapeString( selectPadNumberEscaped );
+
+                    for( PAD* pad : footprint->Pads() )
+                    {
+                        if( selectPadNumber == pad->GetNumber() )
+                        {
+                            items.push_back( pad );
+                        }
+                    }
+                }
+                break;
+            }
+            default: break;
+            }
+        }
+    }
+
+    return items;
+}
+
+
 void PCB_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
 {
     std::string& payload = mail.GetPayload();
@@ -575,6 +521,50 @@ void PCB_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
         ExecuteRemoteCommand( payload.c_str() );
         break;
 
+    case MAIL_SELECTION:
+    {
+        // $SELECT: <mode 0 - only footprints, 1 - with connections>,<spec1>,<spec2>,<spec3>
+        std::string prefix = "$SELECT: ";
+
+        if( !payload.compare( 0, prefix.size(), prefix ) )
+        {
+            std::string del = ",";
+            std::string paramStr = payload.substr( prefix.size() );
+            int         modeEnd = paramStr.find( del );
+            bool        selectConnections = false;
+
+            try
+            {
+                if( std::stoi( paramStr.substr( 0, modeEnd ) ) == 1 )
+                    selectConnections = true;
+            }
+            catch( std::invalid_argument& )
+            {
+                wxFAIL;
+            }
+
+            std::vector<BOARD_ITEM*> items =
+                    FindItemsFromSyncSelection( paramStr.substr( modeEnd + 1 ) );
+
+            m_syncingSchToPcbSelection = true; // recursion guard
+
+            if( selectConnections )
+            {
+                GetToolManager()->RunAction( PCB_ACTIONS::syncSelectionWithNets, true,
+                                             static_cast<void*>( &items ) );
+            }
+            else
+            {
+                GetToolManager()->RunAction( PCB_ACTIONS::syncSelection, true,
+                                             static_cast<void*>( &items ) );
+            }
+
+            m_syncingSchToPcbSelection = false;
+        }
+
+        break;
+    }
+
     case MAIL_PCB_UPDATE:
         m_toolManager->RunAction( ACTIONS::updatePcbFromSchematic, true );
         break;
diff --git a/pcbnew/dialogs/panel_display_options.cpp b/pcbnew/dialogs/panel_display_options.cpp
index 19f35b9c3d..963173d4d0 100644
--- a/pcbnew/dialogs/panel_display_options.cpp
+++ b/pcbnew/dialogs/panel_display_options.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2015 Jean-Pierre Charras, jean-pierre.charras at wanadoo.fr
- * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software: you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -57,6 +57,7 @@ void PANEL_DISPLAY_OPTIONS::loadPCBSettings( PCBNEW_SETTINGS* aCfg )
     m_OptDisplayPadNoConn->SetValue( aCfg->m_Display.m_DisplayPadNoConnects );
     m_ShowNetNamesOption->SetSelection( aCfg->m_Display.m_DisplayNetNamesMode );
     m_live3Drefresh->SetValue( aCfg->m_Display.m_Live3DRefresh );
+    m_checkCrossProbeOnSelection->SetValue( aCfg->m_CrossProbing.on_selection );
     m_checkCrossProbeCenter->SetValue( aCfg->m_CrossProbing.center_on_items );
     m_checkCrossProbeZoom->SetValue( aCfg->m_CrossProbing.zoom_to_fit );
     m_checkCrossProbeAutoHighlight->SetValue( aCfg->m_CrossProbing.auto_highlight );
@@ -99,7 +100,7 @@ bool PANEL_DISPLAY_OPTIONS::TransferDataFromWindow()
         cfg->m_Display.m_DisplayPadNoConnects = m_OptDisplayPadNoConn->GetValue();
         cfg->m_Display.m_DisplayNetNamesMode = m_ShowNetNamesOption->GetSelection();
         cfg->m_Display.m_Live3DRefresh = m_live3Drefresh->GetValue();
-        cfg->m_CrossProbing.center_on_items = m_checkCrossProbeCenter->GetValue();
+        cfg->m_CrossProbing.on_selection = m_checkCrossProbeOnSelection->GetValue();
         cfg->m_CrossProbing.zoom_to_fit = m_checkCrossProbeZoom->GetValue();
         cfg->m_CrossProbing.auto_highlight = m_checkCrossProbeAutoHighlight->GetValue();
     }
diff --git a/pcbnew/dialogs/panel_display_options_base.cpp b/pcbnew/dialogs/panel_display_options_base.cpp
index aad076e38b..28d8b68711 100644
--- a/pcbnew/dialogs/panel_display_options_base.cpp
+++ b/pcbnew/dialogs/panel_display_options_base.cpp
@@ -1,5 +1,5 @@
 ///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Oct 26 2018)
+// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
 // http://www.wxformbuilder.org/
 //
 // PLEASE DO *NOT* EDIT THIS FILE!
@@ -73,6 +73,10 @@ PANEL_DISPLAY_OPTIONS_BASE::PANEL_DISPLAY_OPTIONS_BASE( wxWindow* parent, wxWind
 	wxStaticBoxSizer* sbSizer3;
 	sbSizer3 = new wxStaticBoxSizer( new wxStaticBox( pcbPage, wxID_ANY, _("Cross-probing") ), wxVERTICAL );
 
+	m_checkCrossProbeOnSelection = new wxCheckBox( sbSizer3->GetStaticBox(), wxID_ANY, _("Cross-probe on selection"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_checkCrossProbeOnSelection->SetValue(true);
+	sbSizer3->Add( m_checkCrossProbeOnSelection, 0, wxALL, 5 );
+
 	m_checkCrossProbeCenter = new wxCheckBox( sbSizer3->GetStaticBox(), wxID_ANY, _("Scroll cross-probed items into view"), wxDefaultPosition, wxDefaultSize, 0 );
 	m_checkCrossProbeCenter->SetValue(true);
 	sbSizer3->Add( m_checkCrossProbeCenter, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
diff --git a/pcbnew/dialogs/panel_display_options_base.fbp b/pcbnew/dialogs/panel_display_options_base.fbp
index 986e35c77d..f7d585b70c 100644
--- a/pcbnew/dialogs/panel_display_options_base.fbp
+++ b/pcbnew/dialogs/panel_display_options_base.fbp
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
 <wxFormBuilder_Project>
-    <FileVersion major="1" minor="15" />
+    <FileVersion major="1" minor="16" />
     <object class="Project" expanded="1">
         <property name="class_decoration"></property>
         <property name="code_generation">C++</property>
@@ -14,6 +14,7 @@
         <property name="file">panel_display_options_base</property>
         <property name="first_id">1000</property>
         <property name="help_provider">none</property>
+        <property name="image_path_wrapper_function_name"></property>
         <property name="indent_with_spaces"></property>
         <property name="internationalize">1</property>
         <property name="name">PanelDisplayOptions</property>
@@ -25,6 +26,7 @@
         <property name="skip_php_events">1</property>
         <property name="skip_python_events">1</property>
         <property name="ui_table">UI</property>
+        <property name="use_array_enum">0</property>
         <property name="use_enum">1</property>
         <property name="use_microsoft_bom">0</property>
         <object class="Panel" expanded="1">
@@ -46,6 +48,7 @@
             <property name="size">-1,-1</property>
             <property name="subclass">RESETTABLE_PANEL; widgets/resettable_panel.h; Not forward_declare</property>
             <property name="tooltip"></property>
+            <property name="two_step_creation">0</property>
             <property name="window_extra_style"></property>
             <property name="window_name"></property>
             <property name="window_style">wxTAB_TRAVERSAL</property>
@@ -608,6 +611,70 @@
                                                     <property name="orient">wxVERTICAL</property>
                                                     <property name="parent">1</property>
                                                     <property name="permission">none</property>
+                                                    <object class="sizeritem" expanded="1">
+                                                        <property name="border">5</property>
+                                                        <property name="flag">wxALL</property>
+                                                        <property name="proportion">0</property>
+                                                        <object class="wxCheckBox" expanded="1">
+                                                            <property name="BottomDockable">1</property>
+                                                            <property name="LeftDockable">1</property>
+                                                            <property name="RightDockable">1</property>
+                                                            <property name="TopDockable">1</property>
+                                                            <property name="aui_layer"></property>
+                                                            <property name="aui_name"></property>
+                                                            <property name="aui_position"></property>
+                                                            <property name="aui_row"></property>
+                                                            <property name="best_size"></property>
+                                                            <property name="bg"></property>
+                                                            <property name="caption"></property>
+                                                            <property name="caption_visible">1</property>
+                                                            <property name="center_pane">0</property>
+                                                            <property name="checked">1</property>
+                                                            <property name="close_button">1</property>
+                                                            <property name="context_help"></property>
+                                                            <property name="context_menu">1</property>
+                                                            <property name="default_pane">0</property>
+                                                            <property name="dock">Dock</property>
+                                                            <property name="dock_fixed">0</property>
+                                                            <property name="docking">Left</property>
+                                                            <property name="enabled">1</property>
+                                                            <property name="fg"></property>
+                                                            <property name="floatable">1</property>
+                                                            <property name="font"></property>
+                                                            <property name="gripper">0</property>
+                                                            <property name="hidden">0</property>
+                                                            <property name="id">wxID_ANY</property>
+                                                            <property name="label">Cross-probe on selection</property>
+                                                            <property name="max_size"></property>
+                                                            <property name="maximize_button">0</property>
+                                                            <property name="maximum_size"></property>
+                                                            <property name="min_size"></property>
+                                                            <property name="minimize_button">0</property>
+                                                            <property name="minimum_size"></property>
+                                                            <property name="moveable">1</property>
+                                                            <property name="name">m_checkCrossProbeOnSelection</property>
+                                                            <property name="pane_border">1</property>
+                                                            <property name="pane_position"></property>
+                                                            <property name="pane_size"></property>
+                                                            <property name="permission">protected</property>
+                                                            <property name="pin_button">1</property>
+                                                            <property name="pos"></property>
+                                                            <property name="resize">Resizable</property>
+                                                            <property name="show">1</property>
+                                                            <property name="size"></property>
+                                                            <property name="style"></property>
+                                                            <property name="subclass">; ; forward_declare</property>
+                                                            <property name="toolbar_pane">0</property>
+                                                            <property name="tooltip"></property>
+                                                            <property name="validator_data_type"></property>
+                                                            <property name="validator_style">wxFILTER_NONE</property>
+                                                            <property name="validator_type">wxDefaultValidator</property>
+                                                            <property name="validator_variable"></property>
+                                                            <property name="window_extra_style"></property>
+                                                            <property name="window_name"></property>
+                                                            <property name="window_style"></property>
+                                                        </object>
+                                                    </object>
                                                     <object class="sizeritem" expanded="1">
                                                         <property name="border">5</property>
                                                         <property name="flag">wxBOTTOM|wxRIGHT|wxLEFT</property>
diff --git a/pcbnew/dialogs/panel_display_options_base.h b/pcbnew/dialogs/panel_display_options_base.h
index 8b8db94e68..f5458f31ae 100644
--- a/pcbnew/dialogs/panel_display_options_base.h
+++ b/pcbnew/dialogs/panel_display_options_base.h
@@ -1,5 +1,5 @@
 ///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Oct 26 2018)
+// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
 // http://www.wxformbuilder.org/
 //
 // PLEASE DO *NOT* EDIT THIS FILE!
@@ -45,6 +45,7 @@ class PANEL_DISPLAY_OPTIONS_BASE : public RESETTABLE_PANEL
 		wxCheckBox* m_OptDisplayPadNoConn;
 		wxRadioBox* m_OptDisplayTracksClearance;
 		wxCheckBox* m_OptDisplayPadClearence;
+		wxCheckBox* m_checkCrossProbeOnSelection;
 		wxCheckBox* m_checkCrossProbeCenter;
 		wxCheckBox* m_checkCrossProbeZoom;
 		wxCheckBox* m_checkCrossProbeAutoHighlight;
@@ -53,6 +54,7 @@ class PANEL_DISPLAY_OPTIONS_BASE : public RESETTABLE_PANEL
 	public:
 
 		PANEL_DISPLAY_OPTIONS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL, const wxString& name = wxEmptyString );
+
 		~PANEL_DISPLAY_OPTIONS_BASE();
 
 };
diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp
index 8f7bbfa0f6..4dec609300 100644
--- a/pcbnew/pcb_edit_frame.cpp
+++ b/pcbnew/pcb_edit_frame.cpp
@@ -4,7 +4,7 @@
  * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
  * Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  * Copyright (C) 2013 Wayne Stambaugh <stambaughw@gmail.com>
- * Copyright (C) 2013-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2013-2022 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software: you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -175,10 +175,10 @@ END_EVENT_TABLE()
 
 
 PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
-    PCB_BASE_EDIT_FRAME( aKiway, aParent, FRAME_PCB_EDITOR, _( "PCB Editor" ), wxDefaultPosition,
-                         wxDefaultSize, KICAD_DEFAULT_DRAWFRAME_STYLE, PCB_EDIT_FRAME_NAME ),
-    m_exportNetlistAction( nullptr ),
-    m_findDialog( nullptr )
+        PCB_BASE_EDIT_FRAME( aKiway, aParent, FRAME_PCB_EDITOR, _( "PCB Editor" ),
+                             wxDefaultPosition, wxDefaultSize, KICAD_DEFAULT_DRAWFRAME_STYLE,
+                             PCB_EDIT_FRAME_NAME ),
+        m_exportNetlistAction( nullptr ), m_findDialog( nullptr )
 {
     m_maximizeByDefault = true;
     m_showBorderAndTitleBlock = true;   // true to display sheet references
@@ -187,6 +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;
 
     // We don't know what state board was in when it was last saved, so we have to
     // assume dirty
diff --git a/pcbnew/pcb_edit_frame.h b/pcbnew/pcb_edit_frame.h
index e42c08a0bd..efecdd267b 100644
--- a/pcbnew/pcb_edit_frame.h
+++ b/pcbnew/pcb_edit_frame.h
@@ -121,6 +121,11 @@ public:
 
     void KiwayMailIn( KIWAY_EXPRESS& aEvent ) override;
 
+    /**
+     * Used to find items by selection synchronization spec string.
+     */
+    std::vector<BOARD_ITEM*> FindItemsFromSyncSelection( std::string syncStr );
+
     /**
      * Show the Find dialog.
      */
@@ -782,6 +787,8 @@ public:
 
     bool m_ZoneFillsDirty;               // Board has been modified since last zone fill.
 
+    bool m_syncingSchToPcbSelection; // Recursion guard when synchronizing selection from schematic
+
 private:
     friend struct PCB::IFACE;
     friend class APPEARANCE_CONTROLS;
diff --git a/pcbnew/tools/board_inspection_tool.cpp b/pcbnew/tools/board_inspection_tool.cpp
index 4f60bc0ec1..7dd0d0320c 100644
--- a/pcbnew/tools/board_inspection_tool.cpp
+++ b/pcbnew/tools/board_inspection_tool.cpp
@@ -1,7 +1,7 @@
 /*
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
- * Copyright (C) 2019-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2019-2022 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -866,7 +866,10 @@ 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 )
+    if( m_probingSchToPcb || m_frame->m_syncingSchToPcbSelection )
+        return 0;
+
+    if( !frame()->Settings().m_CrossProbing.on_selection )
         return 0;
 
     PCB_SELECTION_TOOL*  selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
diff --git a/pcbnew/tools/pcb_actions.cpp b/pcbnew/tools/pcb_actions.cpp
index 8e82fad03c..25251bd9fd 100644
--- a/pcbnew/tools/pcb_actions.cpp
+++ b/pcbnew/tools/pcb_actions.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2013-2016 CERN
- * Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors.
  * @author Maciej Suminski <maciej.suminski@cern.ch>
  *
  * This program is free software; you can redistribute it and/or
@@ -1240,6 +1240,12 @@ TOOL_ACTION PCB_ACTIONS::selectConnection( "pcbnew.InteractiveSelection.SelectCo
         _( "Selects a connection or expands an existing selection to junctions, pads, or entire connections" ),
         BITMAPS::add_tracks );
 
+TOOL_ACTION PCB_ACTIONS::syncSelection( "pcbnew.InteractiveSelection.SyncSelection",
+        AS_GLOBAL );
+
+TOOL_ACTION PCB_ACTIONS::syncSelectionWithNets( "pcbnew.InteractiveSelection.SyncSelectionWithNets",
+        AS_GLOBAL );
+
 TOOL_ACTION PCB_ACTIONS::selectNet( "pcbnew.InteractiveSelection.SelectNet",
         AS_GLOBAL, 0, "",
         _( "Select All Tracks in Net" ),
diff --git a/pcbnew/tools/pcb_actions.h b/pcbnew/tools/pcb_actions.h
index ca40498076..77bd02d082 100644
--- a/pcbnew/tools/pcb_actions.h
+++ b/pcbnew/tools/pcb_actions.h
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2013-2016 CERN
- * Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * @author Maciej Suminski <maciej.suminski@cern.ch>
  *
@@ -66,6 +66,12 @@ public:
     static TOOL_ACTION selectItems;
     static TOOL_ACTION unselectItems;
 
+    /// Sets selection to specified items, zooms to fit, if enabled
+    static TOOL_ACTION syncSelection;
+
+    /// Sets selection to specified items with connected nets, zooms to fit, if enabled
+    static TOOL_ACTION syncSelectionWithNets;
+
     /// Run a selection menu to select from a list of items
     static TOOL_ACTION selectionMenu;
 
diff --git a/pcbnew/tools/pcb_selection_tool.cpp b/pcbnew/tools/pcb_selection_tool.cpp
index e9ac7b11e4..ed59071a90 100644
--- a/pcbnew/tools/pcb_selection_tool.cpp
+++ b/pcbnew/tools/pcb_selection_tool.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2013-2017 CERN
- * Copyright (C) 2018-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2018-2022 KiCad Developers, see AUTHORS.txt for contributors.
  * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  * @author Maciej Suminski <maciej.suminski@cern.ch>
  *
@@ -25,6 +25,7 @@
  */
 
 #include <limits>
+#include <cmath>
 #include <functional>
 using namespace std::placeholders;
 #include <core/kicad_algo.h>
@@ -62,6 +63,7 @@ using namespace std::placeholders;
 #include <wx/event.h>
 #include <wx/timer.h>
 #include <wx/log.h>
+#include <profile.h>
 
 
 class SELECT_MENU : public ACTION_MENU
@@ -1100,34 +1102,49 @@ int PCB_SELECTION_TOOL::expandConnection( const TOOL_EVENT& aEvent )
 
     for( const EDA_ITEM* item : m_selection.GetItems() )
     {
-        if( dynamic_cast<const BOARD_CONNECTED_ITEM*>( item ) )
+        if( item->Type() == PCB_FOOTPRINT_T || BOARD_CONNECTED_ITEM::ClassOf( item ) )
             initialCount++;
     }
 
     if( initialCount == 0 )
         selectCursor( true, connectedItemFilter );
 
+    m_frame->SetStatusText( _( "Select/Expand Connection..." ) );
+
     for( STOP_CONDITION stopCondition : { STOP_AT_JUNCTION, STOP_AT_PAD, STOP_NEVER } )
     {
-        // copy the selection, since we're going to iterate and modify
         std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
 
         for( EDA_ITEM* item : selectedItems )
             item->ClearTempFlags();
 
+        std::vector<BOARD_CONNECTED_ITEM*> startItems;
+
         for( EDA_ITEM* item : selectedItems )
         {
-            PCB_TRACK* trackItem = dynamic_cast<PCB_TRACK*>( item );
+            if( item->Type() == PCB_FOOTPRINT_T )
+            {
+                FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
 
-            // Track items marked SKIP_STRUCT have already been visited
-            if( trackItem && !( trackItem->GetFlags() & SKIP_STRUCT ) )
-                selectConnectedTracks( *trackItem, stopCondition );
+                for( PAD* pad : footprint->Pads() )
+                {
+                    startItems.push_back( pad );
+                }
+            }
+            else if( BOARD_CONNECTED_ITEM::ClassOf( item ) )
+            {
+                startItems.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
+            }
         }
 
+        selectAllConnectedTracks( startItems, stopCondition );
+
         if( m_selection.GetItems().size() > initialCount )
             break;
     }
 
+    m_frame->SetStatusText( wxEmptyString );
+
     // Inform other potentially interested tools
     if( m_selection.Size() > 0 )
         m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
@@ -1136,165 +1153,220 @@ int PCB_SELECTION_TOOL::expandConnection( const TOOL_EVENT& aEvent )
 }
 
 
-void PCB_SELECTION_TOOL::selectConnectedTracks( BOARD_CONNECTED_ITEM& aStartItem,
-                                                STOP_CONDITION aStopCondition )
+void PCB_SELECTION_TOOL::selectAllConnectedTracks(
+        const std::vector<BOARD_CONNECTED_ITEM*>& aStartItems, STOP_CONDITION aStopCondition )
 {
-    constexpr KICAD_T      types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, EOT };
-    constexpr PCB_LAYER_ID ALL_LAYERS = UNDEFINED_LAYER;
+    constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, EOT };
+    const LSET        allCuMask = LSET::AllCuMask();
+
+    PROF_TIMER refreshTimer;
+    double     refreshIntervalMs = 500; // Refresh display with this interval to indicate progress
+    int        lastSelectionSize = m_selection.GetSize();
 
     auto connectivity = board()->GetConnectivity();
-    auto connectedItems = connectivity->GetConnectedItems( &aStartItem, types, true );
 
     std::map<VECTOR2I, std::vector<PCB_TRACK*>> trackMap;
     std::map<VECTOR2I, PCB_VIA*>                viaMap;
     std::map<VECTOR2I, PAD*>                    padMap;
+    std::set<PAD*>                              startPadSet;
+    std::vector<BOARD_CONNECTED_ITEM*>          cleanupItems;
+    std::vector<std::pair<VECTOR2I, LSET>>      activePts;
 
-    // Build maps of connected items
-    for( BOARD_CONNECTED_ITEM* item : connectedItems )
+    for( BOARD_CONNECTED_ITEM* startItem : aStartItems )
     {
-        switch( item->Type() )
+        // Track starting pads
+        if( startItem->Type() == PCB_PAD_T )
+            startPadSet.insert( static_cast<PAD*>( startItem ) );
+    }
+
+    for( BOARD_CONNECTED_ITEM* startItem : aStartItems )
+    {
+        if( startItem->HasFlag( SKIP_STRUCT ) ) // Skip already visited items
+            continue;
+
+        std::vector<BOARD_CONNECTED_ITEM*> connectedItems =
+                connectivity->GetConnectedItems( startItem, types, true );
+
+        // Build maps of connected items
+        for( BOARD_CONNECTED_ITEM* item : connectedItems )
+        {
+            switch( item->Type() )
+            {
+            case PCB_ARC_T:
+            case PCB_TRACE_T:
+            {
+                PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
+                trackMap[track->GetStart()].push_back( track );
+                trackMap[track->GetEnd()].push_back( track );
+                break;
+            }
+
+            case PCB_VIA_T:
+            {
+                PCB_VIA* via = static_cast<PCB_VIA*>( item );
+                viaMap[via->GetStart()] = via;
+                break;
+            }
+
+            case PCB_PAD_T:
+            {
+                PAD* pad = static_cast<PAD*>( item );
+                padMap[pad->GetPosition()] = pad;
+                break;
+            }
+
+            default: break;
+            }
+        }
+
+        // Set up the initial active points
+        switch( startItem->Type() )
         {
         case PCB_ARC_T:
         case PCB_TRACE_T:
         {
-            PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
-            trackMap[ track->GetStart() ].push_back( track );
-            trackMap[ track->GetEnd() ].push_back( track );
+            PCB_TRACK* track = static_cast<PCB_TRACK*>( startItem );
+
+            activePts.push_back( { track->GetStart(), track->GetLayerSet() } );
+            activePts.push_back( { track->GetEnd(), track->GetLayerSet() } );
             break;
         }
 
         case PCB_VIA_T:
-        {
-            PCB_VIA* via = static_cast<PCB_VIA*>( item );
-            viaMap[ via->GetStart() ] = via;
+            activePts.push_back( { startItem->GetPosition(), startItem->GetLayerSet() } );
             break;
-        }
 
         case PCB_PAD_T:
-        {
-            PAD* pad = static_cast<PAD*>( item );
-            padMap[ pad->GetPosition() ] = pad;
+            activePts.push_back( { startItem->GetPosition(), startItem->GetLayerSet() } );
             break;
+
+        default: break;
         }
 
-        default:
-            break;
-        }
+        bool expand = true;
+        int  failSafe = 0;
 
-        item->ClearFlags( TEMP_SELECTED );
-    }
-
-    std::vector<std::pair<VECTOR2I, PCB_LAYER_ID>> activePts;
-
-    // Set up the initial active points
-    switch( aStartItem.Type() )
-    {
-    case PCB_ARC_T:
-    case PCB_TRACE_T:
-    {
-        PCB_TRACK* track = static_cast<PCB_TRACK*>( &aStartItem );
-
-        activePts.push_back( { track->GetStart(), track->GetLayer() } );
-        activePts.push_back( { track->GetEnd(), track->GetLayer() } );
-    }
-        break;
-
-    case PCB_VIA_T:
-        activePts.push_back( { aStartItem.GetPosition(), ALL_LAYERS } );
-        break;
-
-    case PCB_PAD_T:
-        activePts.push_back( { aStartItem.GetPosition(), ALL_LAYERS } );
-        break;
-
-    default:
-        break;
-    }
-
-    bool expand = true;
-    int  failSafe = 0;
-
-    // Iterative push from all active points
-    while( expand && failSafe++ < 100000 )
-    {
-        expand = false;
-
-        for( int i = activePts.size() - 1; i >= 0; --i )
+        // Iterative push from all active points
+        while( expand && failSafe++ < 100000 )
         {
-            VECTOR2I     pt = activePts[i].first;
-            PCB_LAYER_ID layer = activePts[i].second;
-            size_t       pt_count = 0;
+            expand = false;
 
-            for( PCB_TRACK* track : trackMap[pt] )
+            for( int i = activePts.size() - 1; i >= 0; --i )
             {
-                if( layer == ALL_LAYERS || layer == track->GetLayer() )
-                    pt_count++;
-            }
+                VECTOR2I pt = activePts[i].first;
+                LSET     layerSetCu = activePts[i].second & allCuMask;
 
-            if( aStopCondition == STOP_AT_JUNCTION )
-            {
-                if( pt_count > 2
-                        || ( viaMap.count( pt ) && layer != ALL_LAYERS )
-                        || ( padMap.count( pt ) && layer != ALL_LAYERS ) )
+                auto viaIt = viaMap.find( pt );
+                auto padIt = padMap.find( pt );
+
+                bool gotVia = ( viaIt != viaMap.end() )
+                              && ( layerSetCu & ( viaIt->second->GetLayerSet() ) ).any();
+
+                bool gotPad = ( padIt != padMap.end() )
+                              && ( layerSetCu & ( padIt->second->GetLayerSet() ) ).any();
+
+                bool gotNonStartPad =
+                        gotPad && ( startPadSet.find( padIt->second ) == startPadSet.end() );
+
+                if( aStopCondition == STOP_AT_JUNCTION )
                 {
-                    activePts.erase( activePts.begin() + i );
-                    continue;
+                    size_t pt_count = 0;
+
+                    for( PCB_TRACK* track : trackMap[pt] )
+                    {
+                        if( layerSetCu.Contains( track->GetLayer() ) )
+                            pt_count++;
+                    }
+
+                    if( pt_count > 2 || gotVia || gotNonStartPad )
+                    {
+                        activePts.erase( activePts.begin() + i );
+                        continue;
+                    }
                 }
-            }
-            else if( aStopCondition == STOP_AT_PAD )
-            {
-                if( padMap.count( pt ) )
+                else if( aStopCondition == STOP_AT_PAD )
                 {
-                    activePts.erase( activePts.begin() + i );
-                    continue;
+                    if( gotNonStartPad )
+                    {
+                        activePts.erase( activePts.begin() + i );
+                        continue;
+                    }
                 }
-            }
 
-            if( padMap.count( pt ) )
-            {
-                PAD* pad = padMap[ pt ];
-
-                if( !( pad->GetFlags() & TEMP_SELECTED ) )
+                if( gotPad )
                 {
-                    pad->SetFlags( TEMP_SELECTED );
-                    activePts.push_back( { pad->GetPosition(), ALL_LAYERS } );
-                    expand = true;
+                    PAD* pad = padIt->second;
+
+                    if( !pad->HasFlag( SKIP_STRUCT ) )
+                    {
+                        pad->SetFlags( SKIP_STRUCT );
+                        cleanupItems.push_back( pad );
+
+                        activePts.push_back( { pad->GetPosition(), pad->GetLayerSet() } );
+                        expand = true;
+                    }
                 }
-            }
 
-            for( PCB_TRACK* track : trackMap[ pt ] )
-            {
-                if( layer != ALL_LAYERS && track->GetLayer() != layer )
-                    continue;
-
-                if( !track->IsSelected() )
+                for( PCB_TRACK* track : trackMap[pt] )
                 {
-                    select( track );
+                    if( !layerSetCu.Contains( track->GetLayer() ) )
+                        continue;
 
-                    if( track->GetStart() == pt )
-                        activePts.push_back( { track->GetEnd(), track->GetLayer() } );
-                    else
-                        activePts.push_back( { track->GetStart(), track->GetLayer() } );
+                    if( !track->IsSelected() )
+                        select( track );
 
-                    expand = true;
+                    if( !track->HasFlag( SKIP_STRUCT ) )
+                    {
+                        track->SetFlags( SKIP_STRUCT );
+                        cleanupItems.push_back( track );
+
+                        if( track->GetStart() == pt )
+                            activePts.push_back( { track->GetEnd(), track->GetLayerSet() } );
+                        else
+                            activePts.push_back( { track->GetStart(), track->GetLayerSet() } );
+
+                        expand = true;
+                    }
                 }
-            }
 
-            if( viaMap.count( pt ) )
-            {
-                PCB_VIA* via = viaMap[ pt ];
-
-                if( !via->IsSelected() )
+                if( viaMap.count( pt ) )
                 {
-                    select( via );
-                    activePts.push_back( { via->GetPosition(), ALL_LAYERS } );
-                    expand = true;
+                    PCB_VIA* via = viaMap[pt];
+
+                    if( !via->IsSelected() )
+                        select( via );
+
+                    if( !via->HasFlag( SKIP_STRUCT ) )
+                    {
+                        via->SetFlags( SKIP_STRUCT );
+                        cleanupItems.push_back( via );
+
+                        activePts.push_back( { via->GetPosition(), via->GetLayerSet() } );
+                        expand = true;
+                    }
                 }
+
+                activePts.erase( activePts.begin() + i );
             }
 
-            activePts.erase( activePts.begin() + i );
+            // Refresh display for the feel of progress
+            if( refreshTimer.msecs() >= refreshIntervalMs )
+            {
+                if( m_selection.Size() != lastSelectionSize )
+                {
+                    m_frame->GetCanvas()->ForceRefresh();
+                    lastSelectionSize = m_selection.Size();
+                }
+
+                refreshTimer.Start();
+            }
         }
     }
+
+    for( BOARD_CONNECTED_ITEM* item : cleanupItems )
+    {
+        item->ClearFlags( SKIP_STRUCT );
+    }
 }
 
 
@@ -1348,7 +1420,7 @@ int PCB_SELECTION_TOOL::selectNet( const TOOL_EVENT& aEvent )
 
 void PCB_SELECTION_TOOL::selectAllItemsOnSheet( wxString& aSheetPath )
 {
-    std::list<FOOTPRINT*> footprintList;
+    std::vector<BOARD_ITEM*> footprints;
 
     // store all footprints that are on that sheet path
     for( FOOTPRINT* footprint : board()->Footprints() )
@@ -1356,52 +1428,88 @@ void PCB_SELECTION_TOOL::selectAllItemsOnSheet( wxString& aSheetPath )
         if( footprint == nullptr )
             continue;
 
-        wxString footprint_path = footprint->GetPath().AsString().BeforeLast('/');
+        wxString footprint_path = footprint->GetPath().AsString().BeforeLast( '/' );
 
         if( aSheetPath.IsEmpty() )
             aSheetPath += '/';
 
         if( footprint_path == aSheetPath )
-            footprintList.push_back( footprint );
+            footprints.push_back( footprint );
     }
 
-    // Generate a list of all pads, and of all nets they belong to.
-    std::list<int>  netcodeList;
-    std::list<PAD*> padList;
-
-    for( FOOTPRINT* footprint : footprintList )
+    for( BOARD_ITEM* i : footprints )
     {
-        for( PAD* pad : footprint->Pads() )
+        if( i != nullptr )
+            select( i );
+    }
+
+    selectConnections( footprints );
+}
+
+
+void PCB_SELECTION_TOOL::selectConnections( const std::vector<BOARD_ITEM*>& aItems )
+{
+    // Generate a list of all pads, and of all nets they belong to.
+    std::list<int>                     netcodeList;
+    std::vector<BOARD_CONNECTED_ITEM*> padList;
+
+    for( BOARD_ITEM* item : aItems )
+    {
+        switch( item->Type() )
         {
+        case PCB_FOOTPRINT_T:
+        {
+            for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
+            {
+                if( pad->IsConnected() )
+                {
+                    netcodeList.push_back( pad->GetNetCode() );
+                    padList.push_back( pad );
+                }
+            }
+
+            break;
+        }
+        case PCB_PAD_T:
+        {
+            PAD* pad = static_cast<PAD*>( item );
+
             if( pad->IsConnected() )
             {
                 netcodeList.push_back( pad->GetNetCode() );
                 padList.push_back( pad );
             }
+
+            break;
+        }
+        default: break;
         }
     }
 
+    // Sort for binary search
+    std::sort( padList.begin(), padList.end() );
+
     // remove all duplicates
     netcodeList.sort();
     netcodeList.unique();
 
-    for( PAD* pad : padList )
-        selectConnectedTracks( *pad, STOP_NEVER );
+    selectAllConnectedTracks( padList, STOP_AT_PAD );
 
     // now we need to find all footprints that are connected to each of these nets then we need
-    // to determine if these footprints are in the list of footprints belonging to this sheet
-    std::list<int> removeCodeList;
+    // to determine if these footprints are in the list of footprints
+    std::vector<int>  removeCodeList;
     constexpr KICAD_T padType[] = { PCB_PAD_T, EOT };
 
     for( int netCode : netcodeList )
     {
-        for( BOARD_CONNECTED_ITEM* mitem : board()->GetConnectivity()->GetNetItems( netCode,
-                                                                                    padType ) )
+        for( BOARD_CONNECTED_ITEM* mitem :
+             board()->GetConnectivity()->GetNetItems( netCode, padType ) )
         {
-            if( mitem->Type() == PCB_PAD_T && !alg::contains( footprintList, mitem->GetParent() ) )
+            if( mitem->Type() == PCB_PAD_T
+                && !std::binary_search( padList.begin(), padList.end(), mitem ) )
             {
-                // if we cannot find the footprint of the pad in the footprintList then we can
-                // assume that that footprint is not located in the same schematic, therefore
+                // if we cannot find the pad in the padList then we can
+                // assume that that pad should not be used, therefore
                 // invalidate this netcode.
                 removeCodeList.push_back( netCode );
                 break;
@@ -1409,31 +1517,21 @@ void PCB_SELECTION_TOOL::selectAllItemsOnSheet( wxString& aSheetPath )
         }
     }
 
-    // remove all duplicates
-    removeCodeList.sort();
-    removeCodeList.unique();
-
     for( int removeCode : removeCodeList )
     {
         netcodeList.remove( removeCode );
     }
 
-    std::list<BOARD_CONNECTED_ITEM*> localConnectionList;
-    constexpr KICAD_T trackViaType[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, EOT };
+    std::vector<BOARD_CONNECTED_ITEM*> localConnectionList;
+    constexpr KICAD_T                  trackViaType[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, EOT };
 
     for( int netCode : netcodeList )
     {
-        for( BOARD_CONNECTED_ITEM* item : board()->GetConnectivity()->GetNetItems( netCode,
-                                                                                   trackViaType ) )
+        for( BOARD_CONNECTED_ITEM* item :
+             board()->GetConnectivity()->GetNetItems( netCode, trackViaType ) )
             localConnectionList.push_back( item );
     }
 
-    for( BOARD_ITEM* i : footprintList )
-    {
-        if( i != nullptr )
-            select( i );
-    }
-
     for( BOARD_CONNECTED_ITEM* i : localConnectionList )
     {
         if( i != nullptr )
@@ -1442,27 +1540,55 @@ void PCB_SELECTION_TOOL::selectAllItemsOnSheet( wxString& aSheetPath )
 }
 
 
-void PCB_SELECTION_TOOL::zoomFitSelection()
+int PCB_SELECTION_TOOL::syncSelection( const TOOL_EVENT& aEvent )
 {
-    // Should recalculate the view to zoom in on the selection.
-    auto selectionBox = m_selection.GetBoundingBox();
-    auto view = getView();
+    std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
 
-    VECTOR2D screenSize = view->ToWorld( m_frame->GetCanvas()->GetClientSize(), false );
-    screenSize.x = std::max( 10.0, screenSize.x );
-    screenSize.y = std::max( 10.0, screenSize.y );
+    if( items )
+        doSyncSelection( *items, false );
 
-    if( selectionBox.GetWidth() != 0  || selectionBox.GetHeight() != 0 )
+    return 0;
+}
+
+
+int PCB_SELECTION_TOOL::syncSelectionWithNets( const TOOL_EVENT& aEvent )
+{
+    std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
+
+    if( items )
+        doSyncSelection( *items, true );
+
+    return 0;
+}
+
+
+void PCB_SELECTION_TOOL::doSyncSelection( const std::vector<BOARD_ITEM*>& aItems, bool aWithNets )
+{
+    ClearSelection( true /*quiet mode*/ );
+
+    // Perform individual selection of each item before processing the event.
+    for( BOARD_ITEM* item : aItems )
+        select( item );
+
+    if( aWithNets )
+        selectConnections( aItems );
+
+    EDA_RECT bbox = m_selection.GetBoundingBox();
+
+    if( m_frame->Settings().m_CrossProbing.center_on_items )
     {
-        VECTOR2D vsize = selectionBox.GetSize();
-        double scale = view->GetScale() / std::max( fabs( vsize.x / screenSize.x ),
-                fabs( vsize.y / screenSize.y ) );
-        view->SetScale( scale );
-        view->SetCenter( selectionBox.Centre() );
-        view->Add( &m_selection );
+        if( m_frame->Settings().m_CrossProbing.zoom_to_fit )
+            zoomFitCrossProbeBBox( bbox );
+
+        m_frame->FocusOnLocation( bbox.Centre() );
     }
 
+    view()->UpdateAllLayersColor();
+
     m_frame->GetCanvas()->ForceRefresh();
+
+    if( m_selection.Size() > 0 )
+        m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
 }
 
 
@@ -1519,6 +1645,158 @@ int PCB_SELECTION_TOOL::selectSameSheet( const TOOL_EVENT& aEvent )
 }
 
 
+void PCB_SELECTION_TOOL::zoomFitSelection()
+{
+    // Should recalculate the view to zoom in on the selection.
+    auto selectionBox = m_selection.GetBoundingBox();
+    auto view = getView();
+
+    VECTOR2D screenSize = view->ToWorld( m_frame->GetCanvas()->GetClientSize(), false );
+    screenSize.x = std::max( 10.0, screenSize.x );
+    screenSize.y = std::max( 10.0, screenSize.y );
+
+    if( selectionBox.GetWidth() != 0 || selectionBox.GetHeight() != 0 )
+    {
+        VECTOR2D vsize = selectionBox.GetSize();
+        double   scale = view->GetScale()
+                       / std::max( fabs( vsize.x / screenSize.x ), fabs( vsize.y / screenSize.y ) );
+        view->SetScale( scale );
+        view->SetCenter( selectionBox.Centre() );
+        view->Add( &m_selection );
+    }
+
+    m_frame->GetCanvas()->ForceRefresh();
+}
+
+
+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 )
+        return;
+
+        //#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();
+    auto screenSize = view->ToWorld( GetCanvas()->GetClientSize(), false );
+
+    // The "fabs" on x ensures the right answer when the view is flipped
+    screenSize.x = std::max( 10.0, fabs( screenSize.x ) );
+    screenSize.y = std::max( 10.0, screenSize.y );
+    double ratio = std::max( fabs( bbSize.x / screenSize.x ), fabs( bbSize.y / screenSize.y ) );
+
+    // Try not to zoom on every cross-probe; it gets very noisy
+    if( crossProbingSettings.zoom_to_fit && ( ratio < 0.5 || ratio > 1.0 ) )
+        view->SetScale( view->GetScale() / ratio );
+#endif // DEFAULT_PCBNEW_CODE
+
+#ifndef DEFAULT_PCBNEW_CODE // Do the scaled zoom
+    auto bbSize = bbox.Inflate( bbox.GetWidth() * 0.2f ).GetSize();
+    auto screenSize = view->ToWorld( m_frame->GetCanvas()->GetClientSize(), false );
+
+    // This code tries to come up with a zoom factor that doesn't simply zoom in
+    // to the cross probed component, 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 footprint (like a processor) or a small footprint (like a resistor).
+    // This ratio is not useful by itself as a scaling factor.  It must be "bent" to
+    // provide good scaling at varying component sizes.  Bigger components need less
+    // scaling than small ones.
+    double currTextHeight = Millimeter2iu( DEFAULT_TEXT_SIZE );
+
+    double compRatio = bbSize.y / currTextHeight; // Ratio of component to text height
+
+    // This will end up as the scaling factor we apply to "ratio".
+    double compRatioBent = 1.0;
+
+    // This is similar to the original KiCad code that scaled the zoom to make sure
+    // components were visible on screen.  It's simply a ratio of screen size to
+    // component size, and its job is to zoom in to make the component fullscreen.
+    // Earlier in the code the component BBox is given a 20% margin to add some
+    // breathing room. We compare the height of this enlarged component bbox to the
+    // default text height.  If a component will end up with the sides clipped, we
+    // adjust later to make sure it fits on screen.
+    //
+    // The "fabs" on x ensures the right answer when the view is flipped
+    screenSize.x = std::max( 10.0, fabs( 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 ) );
+
+    // LUT to scale zoom ratio to provide reasonable schematic context.  Must work
+    // with footprints of varying sizes (e.g. 0402 package and 200 pin BGA).
+    // "first" is used as the input and "second" as the output
+    //
+    // "first" = compRatio (footprint height / default text height)
+    // "second" = Amount to scale ratio by
+    std::vector<std::pair<double, double>> lut{
+        { 1, 8 },    { 1.5, 5 },  { 3, 3 },    { 4.5, 2.5 }, { 8, 2.0 },
+        { 12, 1.7 }, { 16, 1.5 }, { 24, 1.3 }, { 32, 1.0 },
+    };
+
+
+    std::vector<std::pair<double, double>>::iterator it;
+
+    compRatioBent = lut.back().second; // Large component default
+
+    if( compRatio >= lut.front().first )
+    {
+        // Use LUT to do linear interpolation of "compRatio" within "first", then
+        // use that result to linearly interpolate "second" which gives the scaling
+        // factor needed.
+
+        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 component default
+    }
+
+    // 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.  This will apply to parts that
+    // are much wider than they are tall.
+
+    if( bbSize.x > screenSize.x * ratio * compRatioBent )
+    {
+        // Use standard KiCad zoom algorithm for parts too wide to fit 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 )
+        view->SetScale( view->GetScale() / ratio );
+#endif // ifndef DEFAULT_PCBNEW_CODE
+}
+
+
 void PCB_SELECTION_TOOL::FindItem( BOARD_ITEM* aItem )
 {
     bool cleared = false;
@@ -2772,6 +3050,9 @@ void PCB_SELECTION_TOOL::setTransitions()
     Go( &PCB_SELECTION_TOOL::expandConnection,    PCB_ACTIONS::selectConnection.MakeEvent() );
     Go( &PCB_SELECTION_TOOL::selectNet,           PCB_ACTIONS::selectNet.MakeEvent() );
     Go( &PCB_SELECTION_TOOL::selectNet,           PCB_ACTIONS::deselectNet.MakeEvent() );
+    Go( &PCB_SELECTION_TOOL::syncSelection, PCB_ACTIONS::syncSelection.MakeEvent() );
+    Go( &PCB_SELECTION_TOOL::syncSelectionWithNets,
+        PCB_ACTIONS::syncSelectionWithNets.MakeEvent() );
     Go( &PCB_SELECTION_TOOL::selectSameSheet,     PCB_ACTIONS::selectSameSheet.MakeEvent() );
     Go( &PCB_SELECTION_TOOL::selectSheetContents,
         PCB_ACTIONS::selectOnSheetFromEeschema.MakeEvent() );
diff --git a/pcbnew/tools/pcb_selection_tool.h b/pcbnew/tools/pcb_selection_tool.h
index 614a2058d7..1ff6f5fe0f 100644
--- a/pcbnew/tools/pcb_selection_tool.h
+++ b/pcbnew/tools/pcb_selection_tool.h
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2013-2017 CERN
- * Copyright (C) 2017-2021 KiCad Developers, see AUTHORS.TXT for contributors.
+ * Copyright (C) 2017-2022 KiCad Developers, see AUTHORS.TXT for contributors.
  *
  * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  * @author Maciej Suminski <maciej.suminski@cern.ch>
@@ -181,6 +181,9 @@ public:
     ///< Zoom the screen to center and fit the current selection.
     void zoomFitSelection();
 
+    ///< Zoom the screen to fit the bounding box for cross probing/selection sync.
+    void zoomFitCrossProbeBBox( EDA_RECT bbox );
+
     BOARD* GetBoard() const
     {
         return board();
@@ -304,7 +307,8 @@ private:
      *
      * @param aStopCondition where to stop selecting more items
      */
-    void selectConnectedTracks( BOARD_CONNECTED_ITEM& aSourceItem, STOP_CONDITION aStopCondition );
+    void selectAllConnectedTracks( const std::vector<BOARD_CONNECTED_ITEM*>& aStartItems,
+                                   STOP_CONDITION                            aStopCondition );
 
     /**
      * Select all items with the given net code.
@@ -314,6 +318,11 @@ private:
      */
     void selectAllItemsOnNet( int aNetCode, bool aSelect = true );
 
+    /*
+     * Select tracks and vias connected to specified board items.
+     */
+    void selectConnections( const std::vector<BOARD_ITEM*>& aItems );
+
     /**
      * Select all items with the given sheet timestamp/UUID name (the sheet path).
      *
@@ -328,6 +337,12 @@ private:
     ///< (same sheet path).
     int selectSameSheet( const TOOL_EVENT& aEvent );
 
+    ///< Set selection to items passed by parameter and connected nets (optionally).
+    ///< Zooms to fit, if enabled
+    int  syncSelection( const TOOL_EVENT& aEvent );
+    int  syncSelectionWithNets( const TOOL_EVENT& aEvent );
+    void doSyncSelection( const std::vector<BOARD_ITEM*>& aItems, bool aWithNets );
+
     ///< Invoke filter dialog and modify current selection
     int filterSelection( const TOOL_EVENT& aEvent );
 
diff --git a/qa/qa_utils/mocks.cpp b/qa/qa_utils/mocks.cpp
index 1726cf39ee..8e74d655f3 100644
--- a/qa/qa_utils/mocks.cpp
+++ b/qa/qa_utils/mocks.cpp
@@ -435,8 +435,8 @@ int PCB_SELECTION_TOOL::expandConnection( const TOOL_EVENT& aEvent )
 }
 
 
-void PCB_SELECTION_TOOL::selectConnectedTracks( BOARD_CONNECTED_ITEM& aStartItem,
-                                                STOP_CONDITION aStopCondition )
+void PCB_SELECTION_TOOL::selectAllConnectedTracks(
+        const std::vector<BOARD_CONNECTED_ITEM*>& aStartItems, STOP_CONDITION aStopCondition )
 {
 }