diff --git a/pcbnew/dialogs/panel_setup_rules_help_8expression_functions.h b/pcbnew/dialogs/panel_setup_rules_help_8expression_functions.h index 58d01be378..483f637bdf 100644 --- a/pcbnew/dialogs/panel_setup_rules_help_8expression_functions.h +++ b/pcbnew/dialogs/panel_setup_rules_help_8expression_functions.h @@ -1,108 +1,118 @@ // Do not edit this file, it is autogenerated by CMake from the .md file _HKI( "### Expression functions\n" -"\n" -"All function parameters support simple wildcards (`*` and `?`).\n" -"<br><br>\n" -"\n" -" A.enclosedByArea('<zone_name>')\n" -"True if all of `A` lies within the given zone's outline.\n" -"\n" -"NB: this is potentially a more expensive call than `intersectsArea()`. Use `intersectsArea()`\n" -"where possible.\n" -"<br><br>\n" -"\n" -" A.existsOnLayer('<layer_name>')\n" -"True if `A` exists on the given layer. The layer name can be\n" -"either the name assigned in Board Setup > Board Editor Layers or\n" -"the canonical name (ie: `F.Cu`).\n" -"\n" -"NB: this returns true if `A` is on the given layer, independently\n" -"of whether or not the rule is being evaluated for that layer.\n" -"For the latter use a `(layer \"layer_name\")` clause in the rule.\n" -"<br><br>\n" -"\n" -" A.fromTo('x', 'y')\n" -"True if the object exists on the copper path between the given \n" -"pads. `x` and `y` are the full names of pads in the design, such as \n" -"`R1-Pad1`.\n" -"<br><br>\n" -"\n" -" A.getField('<field_name>')\n" -"The value of the given field. Only footprints have fields, so a field is only returned if\n" -"`A` is a footprint.\n" -"<br><br>\n" -"\n" -" A.hasComponentClass('<component_class_name>')\n" -"True if the set of component classes assigned to `A` contains the named \n" -"component class.\n" -"<br><br>\n" -"\n" -" A.hasNetclass('<netclass_name>')\n" -"True if `A` has had the given netclass assigned to it, either by an explicit netclass label\n" -"or through a pattern match assignment.\n" -"<br><br>\n" -"\n" -" A.inDiffPair('<net_name>')\n" -"True if `A` has a net that is part of the specified differential pair.\n" -"`<net_name>` is the base name of the differential pair. For example, `inDiffPair('/CLK')`\n" -"matches items in the `/CLK_P` and `/CLK_N` nets.\n" -"<br><br>\n" -"\n" -" A.intersectsArea('<zone_name>')\n" -"True if any part of `A` lies within the given zone's outline.\n" -"<br><br>\n" -"\n" -" A.intersectsCourtyard('<footprint_identifier>')\n" -"True if any part of `A` lies within the given footprint's principal courtyard.\n" -"<br><br>\n" -"\n" -" A.intersectsFrontCourtyard('<footprint_identifier>')\n" -"True if any part of `A` lies within the given footprint's front courtyard.\n" -"<br><br>\n" -"\n" -" A.intersectsBackCourtyard('<footprint_identifier>')\n" -"True if any part of `A` lies within the given footprint's back courtyard.\n" -"<br><br>\n" -"\n" -"The `footprint_identifier` listed above can be one of the following:\n" -"\n" -"1. A reference designator, possibly containing wildcards `*` and `?`\n" -"2. A footprint library identifier such as `LibName:FootprintName`. In this case,\n" -" the library identifier must contain the `:` character to separate the library\n" -" name from the footprint name, and either name may contain wildcards.\n" -"3. A component class, in the form `${Class:ClassName}`. The keyword `Class` is not\n" -" case-sensitive, but component class names are case-sensitive.\n" -"\n" -"<br>\n" -"\n" -" A.isBlindBuriedVia()\n" -"True if `A` is a blind/buried via.\n" -"<br><br>\n" -"\n" -" AB.isCoupledDiffPair()\n" -"True if `A` and `B` are members of the same diff pair.\n" -"<br><br>\n" -"\n" -" A.isMicroVia()\n" -"True if `A` is a microvia.\n" -"<br><br>\n" -"\n" -" A.isPlated()\n" -"True if `A` has a hole which is plated.\n" -"<br><br>\n" -"\n" -" A.memberOfGroup('<group_name>')\n" -"True if `A` is a member of the given group. The name can contain wildcards.\n" -"Includes nested membership.\n" -"<br><br>\n" -"\n" -" A.memberOfFootprint('<footprint_identifier>')\n" -"True if `A` is a member of a given footprint (for example, a pad or graphic shape defined\n" -"inside that footprint). The various ways of specifying `footprint_identifier` are described above.\n" -"<br><br>\n" -"\n" -" A.memberOfSheet('<sheet_path>')\n" -"True if `A` is a member of the given schematic sheet. The sheet path can contain wildcards.\n" -"<br><br>\n" -"\n" -"" ); + "\n" + "All function parameters support simple wildcards (`*` and `?`).\n" + "<br><br>\n" + "\n" + " A.enclosedByArea('<zone_name>')\n" + "True if all of `A` lies within the given zone's outline.\n" + "\n" + "NB: this is potentially a more expensive call than `intersectsArea()`. Use " + "`intersectsArea()`\n" + "where possible.\n" + "<br><br>\n" + "\n" + " A.existsOnLayer('<layer_name>')\n" + "True if `A` exists on the given layer. The layer name can be\n" + "either the name assigned in Board Setup > Board Editor Layers or\n" + "the canonical name (ie: `F.Cu`).\n" + "\n" + "NB: this returns true if `A` is on the given layer, independently\n" + "of whether or not the rule is being evaluated for that layer.\n" + "For the latter use a `(layer \"layer_name\")` clause in the rule.\n" + "<br><br>\n" + "\n" + " A.fromTo('x', 'y')\n" + "True if the object exists on the copper path between the given \n" + "pads. `x` and `y` are the full names of pads in the design, such as \n" + "`R1-Pad1`.\n" + "<br><br>\n" + "\n" + " A.getField('<field_name>')\n" + "The value of the given field. Only footprints have fields, so a field is only returned if\n" + "`A` is a footprint.\n" + "<br><br>\n" + "\n" + " A.hasComponentClass('<component_class_name>')\n" + "True if the set of component classes assigned to `A` contains the named \n" + "component class.\n" + "<br><br>\n" + "\n" + " A.hasNetclass('<netclass_name>')\n" + "True if `A` has had the given netclass assigned to it, either by an explicit netclass " + "label\n" + "or through a pattern match assignment.\n" + "<br><br>\n" + "\n" + " A.inDiffPair('<net_name>')\n" + "True if `A` has a net that is part of the specified differential pair.\n" + "`<net_name>` is the base name of the differential pair. For example, `inDiffPair('/CLK')`\n" + "matches items in the `/CLK_P` and `/CLK_N` nets.\n" + "<br><br>\n" + "\n" + " A.intersectsArea('<zone_name>')\n" + "True if any part of `A` lies within the given zone's outline.\n" + "<br><br>\n" + "\n" + " A.intersectsCourtyard('<footprint_identifier>')\n" + "True if any part of `A` lies within the given footprint's principal courtyard.\n" + "<br><br>\n" + "\n" + " A.intersectsFrontCourtyard('<footprint_identifier>')\n" + "True if any part of `A` lies within the given footprint's front courtyard.\n" + "<br><br>\n" + "\n" + " A.intersectsBackCourtyard('<footprint_identifier>')\n" + "True if any part of `A` lies within the given footprint's back courtyard.\n" + "<br><br>\n" + "\n" + "The `footprint_identifier` listed above can be one of the following:\n" + "\n" + "1. A reference designator, possibly containing wildcards `*` and `?`\n" + "2. A footprint library identifier such as `LibName:FootprintName`. In this case,\n" + " the library identifier must contain the `:` character to separate the library\n" + " name from the footprint name, and either name may contain wildcards.\n" + "3. A component class, in the form `${Class:ClassName}`. The keyword `Class` is not\n" + " case-sensitive, but component class names are case-sensitive.\n" + "\n" + "<br>\n" + "\n" + " A.isBlindBuriedVia()\n" + "True if `A` is a blind/buried via.\n" + "<br><br>\n" + "\n" + " AB.isCoupledDiffPair()\n" + "True if `A` and `B` are members of the same diff pair.\n" + "<br><br>\n" + "\n" + " A.isMicroVia()\n" + "True if `A` is a microvia.\n" + "<br><br>\n" + "\n" + " A.isPlated()\n" + "True if `A` has a hole which is plated.\n" + "<br><br>\n" + "\n" + " A.memberOfGroup('<group_name>')\n" + "True if `A` is a member of the given group. The name can contain wildcards.\n" + "Includes nested membership.\n" + "<br><br>\n" + "\n" + " A.memberOfFootprint('<footprint_identifier>')\n" + "True if `A` is a member of a given footprint (for example, a pad or graphic shape defined\n" + "inside that footprint). The various ways of specifying `footprint_identifier` are " + "described above.\n" + "<br><br>\n" + "\n" + " A.memberOfSheet('<sheet_path>')\n" + "True if `A` is a member of the given schematic sheet. The sheet path can contain " + "wildcards.\n" + "<br><br>\n" + "\n" + " A.memberOfSheetOrChildren('<sheet_path>')\n" + "True if `A` is a member of the given schematic sheet, or any of its child hierarchical " + "sheets. The sheet path can \n" + "contain wildcards.\n" + "<br><br>\n" + "\n" + "" ); diff --git a/pcbnew/dialogs/panel_setup_rules_help_8expression_functions.md b/pcbnew/dialogs/panel_setup_rules_help_8expression_functions.md index b89fe32b90..aa82e065ed 100644 --- a/pcbnew/dialogs/panel_setup_rules_help_8expression_functions.md +++ b/pcbnew/dialogs/panel_setup_rules_help_8expression_functions.md @@ -102,5 +102,10 @@ inside that footprint). The various ways of specifying `footprint_identifier` a A.memberOfSheet('<sheet_path>') True if `A` is a member of the given schematic sheet. The sheet path can contain wildcards. +<br><br> + + A.memberOfSheetOrChildren('<sheet_path>') +True if `A` is a member of the given schematic sheet, or any of its child hierarchical sheets. The sheet path can +contain wildcards. <br><br> diff --git a/pcbnew/pcbexpr_functions.cpp b/pcbnew/pcbexpr_functions.cpp index d6e3106995..1585c1c3dd 100644 --- a/pcbnew/pcbexpr_functions.cpp +++ b/pcbnew/pcbexpr_functions.cpp @@ -995,6 +995,69 @@ static void memberOfSheetFunc( LIBEVAL::CONTEXT* aCtx, void* self ) } +static void memberOfSheetOrChildrenFunc( LIBEVAL::CONTEXT* aCtx, void* self ) +{ + LIBEVAL::VALUE* arg = aCtx->Pop(); + LIBEVAL::VALUE* result = aCtx->AllocValue(); + + result->Set( 0.0 ); + aCtx->Push( result ); + + if( !arg || arg->AsString().IsEmpty() ) + { + if( aCtx->HasErrorCallback() ) + aCtx->ReportError( MISSING_SHEET_ARG( wxT( "memberOfSheetOrChildren()" ) ) ); + + return; + } + + PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self ); + BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr; + + if( !item ) + return; + + result->SetDeferredEval( + [item, arg]() -> double + { + FOOTPRINT* fp = item->GetParentFootprint(); + + if( !fp && item->Type() == PCB_FOOTPRINT_T ) + fp = static_cast<FOOTPRINT*>( item ); + + if( !fp ) + return 0.0; + + wxString sheetName = fp->GetSheetname(); + wxString refName = arg->AsString(); + + if( sheetName.EndsWith( wxT( "/" ) ) ) + sheetName.RemoveLast(); + if( refName.EndsWith( wxT( "/" ) ) ) + refName.RemoveLast(); + + wxArrayString sheetPath = wxSplit( sheetName, '/' ); + wxArrayString refPath = wxSplit( refName, '/' ); + + if( refPath.size() > sheetPath.size() ) + return 0.0; + + if( ( refName.Matches( wxT( "/" ) ) || refName.IsEmpty() ) && sheetName.IsEmpty() ) + { + return 1.0; + } + + for( size_t i = 0; i < refPath.size(); i++ ) + { + if( !sheetPath[i].Matches( refPath[i] ) ) + return 0.0; + } + + return 1.0; + } ); +} + + #define MISSING_REF_ARG( f ) \ wxString::Format( _( "Missing footprint argument (reference designator) to %s." ), f ) @@ -1316,6 +1379,7 @@ void PCBEXPR_BUILTIN_FUNCTIONS::RegisterAllFunctions() RegisterFunc( wxT( "memberOfGroup('x')" ), memberOfGroupFunc ); RegisterFunc( wxT( "memberOfFootprint('x')" ), memberOfFootprintFunc ); RegisterFunc( wxT( "memberOfSheet('x')" ), memberOfSheetFunc ); + RegisterFunc( wxT( "memberOfSheetOrChildren('x')" ), memberOfSheetOrChildrenFunc ); RegisterFunc( wxT( "fromTo('x','y')" ), fromToFunc ); RegisterFunc( wxT( "isCoupledDiffPair()" ), isCoupledDiffPairFunc ); diff --git a/pcbnew/tools/multichannel_tool.cpp b/pcbnew/tools/multichannel_tool.cpp index 3e60ba260b..d955a21373 100644 --- a/pcbnew/tools/multichannel_tool.cpp +++ b/pcbnew/tools/multichannel_tool.cpp @@ -99,8 +99,8 @@ bool MULTICHANNEL_TOOL::identifyComponentsInRuleArea( ZONE* aRul { case RULE_AREA_PLACEMENT_SOURCE_TYPE::SHEETNAME: { - ruleText = - wxT( "A.memberOfSheet('" ) + aRuleArea->GetRuleAreaPlacementSource() + wxT( "')" ); + ruleText = wxT( "A.memberOfSheetOrChildren('" ) + aRuleArea->GetRuleAreaPlacementSource() + + wxT( "')" ); break; } case RULE_AREA_PLACEMENT_SOURCE_TYPE::COMPONENT_CLASS: