Functions/PSPlus.Modules.Windows/New-DesktopLayoutRulesFromCurrentLayout.ps1

function New-DesktopLayoutRulesFromCurrentLayout
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false)]
        [string[]] $IncludeProcessNames = @(),

        [Parameter(Mandatory = $false)]
        [string[]] $ExcludeProcessNames = @(),

        [Parameter(Mandatory = $false)]
        [string[]] $IncludeClassNames = @(),

        [Parameter(Mandatory = $false)]
        [string[]] $ExcludeClassNames = @()
    )

    # Snap all processes
    $pidToProcessNameMap = Get-Process | ConvertTo-Dictionary { $_.Id } { $_.ProcessName }

    # Snap all top level windows
    $topLevelWindows = Get-Windows

    # Generate the window layout rules
    $windowLayoutRules = $topLevelWindows | ForEach-Object {
        $window = $_

        # Make sure the window is still alive while we are getting all the data
        if (-not $window.IsWindow()) {
            return
        }

        $isVisible = $window.IsWindowVisible()
        $windowPid = $window.GetWindowProcessId()
        $parentWindow = $window.GetParent()
        $className = $window.GetClassName()
        $windowPlacement = $window.GetWindowPlacement()
        $windowScreenRect = $window.GetWindowRect()

        if ($parentWindow.IsWindow()) {
            return
        }

        if (-not $window.IsWindow()) {
            return
        }

        # If the window is not visible, ignore. (Minimized windows will still have WS_VISIBLE style)
        if (-not $isVisible) {
            return
        }

        if (($windowPlacement.NormalPosition.Left -eq 0) -and
            ($windowPlacement.NormalPosition.Top -eq 0) -and
            ($windowPlacement.NormalPosition.Right -eq 0) -and
            ($windowPlacement.NormalPosition.Bottom -eq 0)) {
            return
        }

        $windowProcessName = [string]::Empty;
        if (-not $pidToProcessNameMap.TryGetValue($windowPid, [ref] $windowProcessName)) {
            return
        }

        $isWindowProcessIncluded = Test-IsStringIncluded $windowProcessName $IncludeProcessNames $ExcludeProcessNames
        if (!$isWindowProcessIncluded) {
            return
        }

        $isClassNameIncluded = Test-IsStringIncluded $className $includeClassNames $excludeClassNames
        if (!$isClassNameIncluded) {
            return
        }

        $LayoutRuleFieldNames = [PSPlus.Core.Windows.Window.DesktopLayout.LayoutRuleFieldNames]
        $windowLayoutRule = @{
            $LayoutRuleFieldNames::Match = @{
                $LayoutRuleFieldNames::MatchProcessName = $windowProcessName
                $LayoutRuleFieldNames::MatchProcessNamePattern = $null
                $LayoutRuleFieldNames::MatchClassName = $className
                $LayoutRuleFieldNames::MatchClassNamePattern = $null
                $LayoutRuleFieldNames::MatchWindowTitlePattern = $null
            }
            $LayoutRuleFieldNames::Placement = @{
                $LayoutRuleFieldNames::PlacementWindowFlags = $windowPlacement.Flags
                $LayoutRuleFieldNames::PlacementWindowShowCmd = $windowPlacement.ShowCmd
                $LayoutRuleFieldNames::PlacementWindowMinPositionX = $windowPlacement.MinPosition.X
                $LayoutRuleFieldNames::PlacementWindowMinPositionY = $windowPlacement.MinPosition.Y
                $LayoutRuleFieldNames::PlacementWindowMaxPositionX = $windowPlacement.MaxPosition.X
                $LayoutRuleFieldNames::PlacementWindowMaxPositionY = $windowPlacement.MaxPosition.Y
                $LayoutRuleFieldNames::PlacementWindowNormalPositionLeft = $windowScreenRect.Left
                $LayoutRuleFieldNames::PlacementWindowNormalPositionTop = $windowScreenRect.Top
                $LayoutRuleFieldNames::PlacementWindowNormalPositionRight = $windowScreenRect.Right
                $LayoutRuleFieldNames::PlacementWindowNormalPositionBottom = $windowScreenRect.Bottom
            }
        }

        Write-Host ("Generating rules for window {0} of process {1} at ({2}, {3}) - ({4}, {5})." -f $className, $windowProcessName, $windowScreenRect.Left, $windowScreenRect.Top, $windowScreenRect.Right, $windowScreenRect.Bottom)

        return $windowLayoutRule
    } 

    return $windowLayoutRules
}

function Test-IsClassNameIncluded($window, $includeClassNames, $excludeClassNames) {
    if (-not $window.IsWindow()) {
        return $false
    }

    $className = $window.GetClassName()
    return Test-IsStringIncluded $className $includeClassNames $excludeClassNames
}

function Test-IsStringIncluded([string] $s, [string[]] $includeRegexes, [string[]] $excludeRegexes) {
    if ($includeRegexes -ne $null -and $includeRegexes.Count -gt 0) {
        $isStringIncluded = $includeRegexes | Test-Any { Select-String -InputObject $s -Pattern $_ -Quiet }
        if (!$isStringIncluded) {
            return $false
        }
    }

    if ($excludeRegexes -ne $null -and $excludeRegexes.Count -gt 0) {
        $isStringExcluded = $excludeRegexes | Test-Any { Select-String -InputObject $s -Pattern $_ -Quiet }
        if ($isStringExcluded) {
            return $false
        }
    }

    return $true
}