Add-PowerGUIMenu.ps1
function Add-PowerGUIMenu { <# .Synopsis Helper function to add menus to the PowerGUI Script Editor .Description Makes adding menus to the PowerGUI Script Editor easier. Add-PowerGUIMenu accepts a hashtable of menus. Each key is the name of the menu. Keys are automatically alphabetized, unless the Each value can be one of three things: - A Script Block Selecting the menu item will run the script block - A Hashtable The value will be used to create a nested menu - A Script Block with a note property of ShortcutKey Selecting the menu item will run the script block. The ShortcutKey will be used to assign a shortcut key to the item .Example Add-PowerGuiMenu -Name "Get" @{ "Process" = { Get-Process } "Service" = { Get-Service } "Hotfix" = {Get-Hotfix} } .Example Add-PowerGuiMenu -Name "Verb" @{ Get = @{ Process = { Get-Process } Service = { Get-Service } Hotfix = { Get-Hotfix } } Import = @{ Module = { Import-Module } } } #> [CmdletBinding(DefaultParameterSetName='AddMenuItem')] param( #The name of the menu to create [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] [String] $Name, # The contents of the menu [Parameter(Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true, ParameterSetName='AddMenuItem')] [Hashtable]$Menu, # The Menu File [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, ParameterSetName='MenuFile')] [Alias('Fullname')] [string] $MenuFile, # The root of the menu. This is used automatically by Add-IseMenu when it # creates nested menus. [Parameter(ParameterSetName='AddMenuItem')] [PSObject] $Root, # If PassThru is set, the menu items will be outputted to the pipeline [switch]$PassThru, # If Merge is set, menu items will be merged with existing menus rather than # recreating the entire menu. [switch]$Merge, # If DoNotSort is set, menu items will not be sorted alphabetically [switch]$doNotSort ) begin { $pgSE= [Quest.PowerGUI.SDK.ScriptEditorFactory]::CurrentInstance Set-StrictMode -Off $myCommandName = $MyInvocation.InvocationName } process { if ($psCmdlet.ParameterSetName -eq 'MenuFile') { $resolvedPsPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($MenuFile) $Menu = & $MenuFile } $menuPointer = $null if (-not $Root) { # The "root" is actually a list of menu names that lead to the current item $existingMenu = $pgSE.Menus | Where-Object { $_.Command.FullName -eq "Menu.${Name}" } if ($existingMenu) { if (-not $Merge) { $existingMenu.Items.Clear() } $menuPointer = $existingMenu } else { $menuPointer = New-Object Quest.PowerGUI.SDK.MenuCommand "Menu","$Name" -Property @{ Text = $Name.Replace("_", "&") } $pgSE.Menus.Add($menuPointer) } } else { if ($Root -is [string] -and $newMenu) { $Root = $newMenu } $menuPointer = $Root <# $currentItem = $null for ($i = 0; $i -lt $Root.Count; $i++) { if ($i -eq 0) { # First case, root is the menu $currentItem = $pgSE.Menus | Where-Object { $_.Command.FullName -eq $Root[$i] } } else { $currentItem = $currentItem.Items | Where-Object { $_.Command.FullName -eq $Root[$i] } } if (-not $currentItem) { break } } if ($currentItem) { $menuPointer = $currentItem } #> } $menuItems = $Menu.GetEnumerator() if (-not $doNotSort) { $menuItems = $menu.GetEnumerator() | Sort-Object Key } else { $menuItems = $menu.GetEnumerator() } foreach ($menuItem in $menuItems) { switch ($menuItem.Value) { { $_ -is [Hashtable] } { # Nested menu, recurse $newMenu = New-Object Quest.PowerGUI.SDK.MenuCommand "Menu","$($menuItem.Key)" -Property @{ Text = $menuItem.Key.Replace("_", "&") } $r = $root + "Menu.$($MenuItem.Key)" Add-PowerGUIMenu -Name $menuItem.Key -Menu:$_ -root $newMenu -merge:$merge -passThru:$passThru if (-not $Root) { $pgSE.Menus.Item("Menu.${Name}").Items.Add($newMenu) } else { $menuPointer.Items.Add($newMenu) } break } default { $scriptBlock= [ScriptBlock]::Create( " [Quest.PowerGUI.SDK.ScriptEditorFactory]::CurrentInstance.Execute({$_},`$true) " ) # To correctly add and remove command entries, they have to be unique. # To uniquify them, make the command the full path to the object # by peeking up the callstack and finding out the names of the parent menus # Nifty trick, right? $restOfName = @(Get-PSCallStack | Where-Object { $_.InvocationInfo.InvocatioName -eq $myCommand } | Select-Object -ExpandProperty InvocationInfo | ForEach-Object { $_.BoundParameters.Name } | Where-Object { $_ }) $restOfName += $menuItem.Key $ofs = "." $fullname = "Menu.${restOfName}" $itemCommand = New-Object Quest.PowerGUI.SDK.ItemCommand "Menu","$restOfName" -Property @{ Text = $menuItem.Key.Replace("_", "&") ScriptBlock = $scriptBlock } $oldCommands = $pgSE.Commands | Where-Object { $_.FullName -eq $fullname } if ($oldCommands) { foreach ($cmd in $oldCommands) { $null = $pgSE.Commands.Remove($cmd) } } if ($_.ShortcutKey) { # Add a shortcut key $saferKey = $_.ShortcutKey.Replace("ALT", "Alt").Replace("CONTROL", "Control").Replace("SHIFT", "Shift").Replace("LEFT","Left").Replace("RIGHT", "Right") $itemCommand.AddShortcut($saferKey) } if ($_.Image) { # Add an image # Expand the image string. By doing this here it enables the menu to use $psScriptRoot # which lets images be stored within a module $expandedImageString = $ExecutionContext.InvokeCommand.ExpandString($_.Image) $existsAndValidPAth = Resolve-Path $expandedImageString -ErrorAction SilentlyContinue $realItem= Get-Item $existsAndValidPAth if ($existsAndValidPAth ) { $image = [Drawing.Image]::FromFile($realItem.Fullname) if ($image) { $itemCommand.Image = $image } } } $null = $pgSE.Commands.Add($itemCommand) if (-not $Root) { $pgSE.Menus.Item("Menu.${Name}").Items.Add($itemCommand) } else { $MenuPointer.Items.Add($itemCommand) } if ($passThru) { $itemCommand } } } } } } |