﻿# encoding: utf-8
# api: powershell
# title: Add-on.HelpBrowser.psm1
# description: This file was uploaded by a PowerGUI Script Editor Add-on.
# version: 2.2.0.1358
# type: module
# author: Gyorgy Nemesmagasi                                                                                
# license: CC0
# x-poshcode-id: 4268
# x-archived: 2013-06-29T06:29:52
# x-published: 2013-06-26T15:04:00
#
#
#######################################################################################################################
# File:             Add-on.HelpBrowser.psm1                                                                           #
# Author:           Gyorgy Nemesmagasi                                                                                #
# Publisher:                                                                                                          #
# Copyright:        Â© 2010 . All rights reserved.                                                                     #
# Usage:            To load this module in your Script Editor:                                                        #
#                   1. Open the Script Editor.                                                                        #
#                   2. Select "PowerShell Libraries" from the File menu.                                              #
#                   3. Check the Add-on.HelpBrowser module.                                                           #
#                   4. Click on OK to close the "PowerShell Libraries" dialog.                                        #
#                   Alternatively you can load the module from the embedded console by invoking this:                 #
#                       Import-Module -Name Add-on.HelpBrowser
#                   Please provide feedback on the PowerGUI Forums.                                                   #
#######################################################################################################################

Set-StrictMode -Version 2


#region Initialize the Script Editor Add-on.

if ($Host.Name â��ne 'PowerGUIScriptEditorHost') { return }
if ($Host.Version -lt '2.2.0.1358') {
	[System.Windows.Forms.MessageBox]::Show("The ""$(Split-Path -Path $PSScriptRoot -Leaf)"" Add-on module requires version 2.2.0.1358 or later of the Script Editor. The current Script Editor version is $($Host.Version).$([System.Environment]::NewLine * 2)Please upgrade to version 2.2.0.1358 and try again.","Version 2.2.0.1358 or later is required",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Information) | Out-Null
	return
}

$pgse = [Quest.PowerGUI.SDK.ScriptEditorFactory]::CurrentInstance

#endregion

#region Load resources from disk.

$HelpBrowserIconLibrary = @{
	# TODO: Load icons into this table.
	#       eg. HelpBrowserIcon16 = New-Object System.Drawing.Icon -ArgumentList "$PSScriptRoot\Resources\HelpBrowser.ico",16,16
	#           HelpBrowserIcon32 = New-Object System.Drawing.Icon -ArgumentList "$PSScriptRoot\Resources\HelpBrowser.ico",32,32	
}

$HelpBrowserImageLibrary = @{
	# TODO: Load images into this table.
	#       eg. HelpBrowserImage16 = $iconLibrary['HelpBrowserIcon16'].ToBitmap()
	#           HelpBrowserImage32 = $iconLibrary['HelpBrowserIcon32'].ToBitmap()
}

#Load the treenodes' icons
$HelpIcon = New-Object System.Drawing.Icon -ArgumentList "$PSScriptRoot\Resources\Help.ico",16,16
$AliasIcon = New-Object System.Drawing.Icon -ArgumentList "$PSScriptRoot\Resources\Alias.ico",16,16
$CmdletIcon = New-Object System.Drawing.Icon -ArgumentList "$PSScriptRoot\Resources\Cmdlet.ico",16,16

#Construct the treeview's image list
$HelpBrowserImageList = New-Object -TypeName System.Windows.Forms.ImageList
$HelpBrowserImageList.Images.Add('HelpIcon', $HelpIcon.ToBitmap())
$HelpBrowserImageList.Images.Add('AliasIcon', $AliasIcon.ToBitmap())
$HelpBrowserImageList.Images.Add('CmdletIcon', $CmdletIcon.ToBitmap())

#endregion


#Set to $true if you like to obtain all the help text during load the module
$PreLoadHelpText = $false
#Size of the floatin Help Panel
$HelpPanelHeigh = 500
$HelpPanelWidth = 430
$HelpTextWidth = 80

#region Create Help Browser Panel

#region Define functions for TreeView control

	function AddNode {
		param(
			$Parent,
			$NodeName,
			$NodeText,
			$NodeImageIndex
		)
		$script:node = New-Object -TypeName System.Windows.Forms.TreeNode
		$node.Name = $NodeName
		$node.Text = $NodeText
		$node.ImageIndex = $NodeImageIndex
		$node.SelectedImageIndex = $NodeImageIndex
		$Parent.Nodes.Add($node)
	}
	
#endregion

#region Create the control that will appear in the dockable window.

#region Build the help tree
# TODO: Replace this with a definition for the control that you want to appear in the dockable window
$helpBrowser = New-Object System.Windows.Forms.TreeView
$helpBrowser.ImageList = $HelpBrowserImageList

#Construct the help about nodes
AddNode $helpBrowser 'About' 'About' 0
Get-Help about | ForEach-Object { AddNode $helpBrowser.Nodes[0] $_.Name $_.Name 0 }

#Construct the help Alias nodes
AddNode $helpBrowser 'Alias' 'Alias' 1
Get-Alias | ForEach-Object {
  AddNode $helpBrowser.Nodes[1] $_.Name ($_.Name + '  ('  + $_.ResolvedCommandName + ')')  1
}

#Construct the help Cmdlet nodes
AddNode $helpBrowser  'Cmdlet' 'Cmdlet' 2
Get-Command -CommandType Cmdlet | ForEach-Object {
	$CmdNodeText = $_.Name
	#Add the refered cmdlets to the text
	if (get-alias -Definition $_.Name -ErrorAction SilentlyContinue) {
		$CmdNodeText  +=  "  ("+ [string]::join(" ,", (get-alias -Definition $_.Name -ErrorAction SilentlyContinue | ForEach-Object {$_.Name}) ) + ")"	
	}	
	AddNode $helpBrowser.Nodes[2] $_.Name $CmdNodeText 2
}
#endregion

#region Add NodeMouseClick event
#Add the node click event. It updates the help panel with the help text based on the selected node
$helpBrowser.Add_NodeMouseClick( [System.Windows.Forms.TreeNodeMouseClickEventHandler] { param($sender, $e)
	#Do nothing if it is a left click or the refered node is top level one
	if (($e.Button -ne [System.Windows.Forms.MouseButtons]::Left) -or
		 ($e.Node.Level -ne 1)) {
		return
	}	
	
	#Check the help text is already obtained
	if ($e.Node.Tag -eq $null) {
		#Obtain the help about text
		if ($e.Node.Parent.Name -eq 'About') {
			$e.Node.Tag = Get-Help $e.Node.Name  | Out-String -Width $HelpTextWidth
		}
		#Obtain the help text of the alias or the cmdlet 
		else {
			#$e.Node.Tag = Get-Help $e.Node.Name  | Out-String -Width 200
			#Here the Get-Help doesn't work. One workaround is call an external powershell process and save the output to a file
			powershell.exe -OutputFormat Text -command "Get-Help -Name $($e.Node.Name) -Full | Out-File -Width $($HelpTextWidth) $Env:temp\HelpText.txt"
			$e.Node.Tag = [System.String]::Join("`n", (Get-Content -ReadCount 0 "$Env:TEMP\HelpText.txt"))				
		}
	}
	#Update the Help panel
	$HelpWindow.Control.Text = $e.Node.Tag

})
#endregion

#region Add NodeMouseDoubleClick event
#Add the node doubleclick event to the treeview. Double click on the a non about node insert the alias or the cmdlet to the current editor documant
$helpBrowser.add_NodeMouseDoubleClick( [System.Windows.Forms.TreeNodeMouseClickEventHandler] { param($sender, $e)
	#Do nothing if it is a left click or the refered node is top level one or it's chiled of about node
	#or the active tab editor tab document window
	if (($e.Button -ne [System.Windows.Forms.MouseButtons]::Left) -or 
		($e.Node.Level -ne 1) -or ($e.Node.Parent.Name -eq 'About')  -or ($pgSE.CurrentDocumentWindow -eq $null)) {
		return
	}
	
	#Check the current document window contains the SyntaxEditor control
	if (($originalWindowProperty = $pgSE.CurrentDocumentWindow.GetType().GetProperty('OriginalWindow',[System.Reflection.BindingFlags]'NonPublic,Instance')) -and
	    ($originalWindow = $originalWindowProperty.GetValue($pgSE.CurrentDocumentWindow,$null)) -and
	    ($originalWindow | Get-Member -MemberType Property -Name PSControl -ErrorAction SilentlyContinue) -and
	    ($scriptEditorControl = $originalWindow.PSControl) -and 
		($scriptEditorControl.GetType().BaseType.FullName -eq 'ActiproSoftware.SyntaxEditor.SyntaxEditor')) {
			#Insert the selected node's text
			$scriptEditorControl.Document.InsertText([ActiproSoftware.SyntaxEditor.DocumentModificationType]::Typing,$scriptEditorControl.Caret.Offset, $e.Node.Name)
	}
	
})
#endregion

#region Pre-load the help text
#The other work around of the Get-Help problem to enumerate the help text during module loading
#It's very time consuming so disabled by default
if ($PreLoadHelpText) {
	$helpBrowser.Nodes[0].Nodes | ForEach-Object {$_.Tag = Get-Help $_.Name -Full | Out-String -Width $HelpTextWidth }
	$helpBrowser.Nodes[1].Nodes | ForEach-Object {$_.Tag = Get-Help $_.Name -Full | Out-String -Width $HelpTextWidth }
	$helpBrowser.Nodes[2].Nodes | ForEach-Object {$_.Tag = Get-Help $_.Name -Full | Out-String -Width $HelpTextWidth }
}
#endregion

#endregion

#region Create and/or initialize the HelpBrowser dockable window.

if (-not ($HelpBrowserWindow = $pgse.ToolWindows['HelpBrowser'])) {
	$HelpBrowserWindow = $pgse.ToolWindows.Add('HelpBrowser')
	$HelpBrowserWindow.Title = '&Help Browser' -replace '&'
	$HelpBrowserWindow.Control = $helpBrowser
	$HelpBrowserWindow.Control.Parent.CanBecomeDocument = [ActiproSoftware.ComponentModel.DefaultableBoolean]::False
	$HelpBrowserWindow.Control.Parent.Image = $HelpIcon.ToBitmap()	
	$HelpBrowserWindow.Visible = $true
} 
else {
		$HelpBrowserWindow.Control = $helpBrowser
}

#endregion

#region Create a Go menu command to activate the HelpBrowser window.

if (-not ($goToHelpBrowserCommand = $pgse.Commands['GoCommand.HelpBrowser'])) {
	$goToHelpBrowserCommand = New-Object -TypeName Quest.PowerGUI.SDK.ItemCommand -ArgumentList 'GoCommand', 'HelpBrowser'
	$goToHelpBrowserCommand.Text = '&Help Browser'
	$goToHelpBrowserCommand.Image = $HelpIcon.ToBitmap()
	if ($goMenu = $pgse.Menus['MenuBar.Go']) {
		$index = $goMenu.Items.Count + 1
		if ($index -lt 10) {
			$goToHelpBrowserCommand.AddShortcut("Ctrl+${index}")
		}
	}
	$goToHelpBrowserCommand.ScriptBlock = {
		$pgse = [Quest.PowerGUI.SDK.ScriptEditorFactory]::CurrentInstance
		if ($HelpBrowserWindow = $pgse.ToolWindows['HelpBrowser']) {
			$HelpBrowserWindow.Visible = $true
			$HelpBrowserWindow.Control.Invoke([EventHandler]{$HelpBrowserWindow.Control.Parent.Activate($true)})
		}
	}

	$pgse.Commands.Add($goToHelpBrowserCommand)
}

#endregion

#region Add the Go to HelpBrowser command to the Go menu.

if ($goMenu = $pgse.Menus['MenuBar.Go']) {
	$goMenu.Items.Add($goToHelpBrowserCommand)
}

#endregion

#endregion

#region Create Help Panel

	#region Create the control that will appear in the dockable window.

	$HelpControl = New-Object System.Windows.Forms.RichTextBox
	$HelpControl.Text = @"
Help browser

created by Gyorgy Nemesmagasi
Â© 2010 . All rights reserved.
"@
	
	#endregion

	#region Create and/or initialize the Help dockable window.

	if (-not ($HelpWindow = $pgse.ToolWindows['Help'])) {
		$HelpWindow = $pgse.ToolWindows.Add('Help')
		$HelpWindow.Title = '&Help' -replace '&'
		$HelpWindow.Control = $HelpControl
		$HelpWindow.Visible = $true
		$HelpWindow.Control.Parent.Image = $HelpIcon.ToBitmap()
		#Set the window state, size and reposition
		$HelpWindow.Control.Invoke([EventHandler]{$HelpWindow.Control.Parent.State = 'Floating'})
		$HelpWindow.Control.Invoke([EventHandler]{$HelpWindow.Control.Parent.FloatingSize = New-Object system.Drawing.Size($HelpPanelWidth,$HelpPanelHeigh)})
		$moveHeigh = [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea.Height - $HelpPanelHeigh
		$moveWidth = [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea.Width - $HelpPanelWidth
		$HelpWindow.Control.Invoke([EventHandler]{$HelpWindow.Control.Parent.FloatingLocation = New-Object system.Drawing.Size($moveWidth,$moveHeigh)})
		$HelpWindow.Control.Invoke([EventHandler]{$HelpWindow.Control.Parent.Activate($true)})
	} else {
		$HelpWindow.Control = $HelpControl
	}

	#endregion

	#region Create a Go menu command to activate the Help window.

	if (-not ($goToHelpCommand = $pgse.Commands['GoCommand.Help'])) {
		$goToHelpCommand = New-Object -TypeName Quest.PowerGUI.SDK.ItemCommand -ArgumentList 'GoCommand', 'Help'
		$goToHelpCommand.Text = '&Help'
		$goToHelpCommand.Image = $HelpIcon.ToBitmap()
		if ($goMenu = $pgse.Menus['MenuBar.Go']) {
			$index = $goMenu.Items.Count + 1
			if ($index -lt 10) {
				$goToHelpCommand.AddShortcut("Ctrl+${index}")
			}
		}
		$goToHelpCommand.ScriptBlock = {
			$pgse = [Quest.PowerGUI.SDK.ScriptEditorFactory]::CurrentInstance
			if ($HelpWindow = $pgse.ToolWindows['Help']) {
				$HelpWindow.Visible = $true
				$HelpWindow.Control.Invoke([EventHandler]{$HelpWindow.Control.Parent.Activate($true)})
			}
		}

		$pgse.Commands.Add($goToHelpCommand)
	}

	#endregion

	#region Add the Go to Help command to the Go menu.

	if ($goMenu = $pgse.Menus['MenuBar.Go']) {
		$goMenu.Items.Add($goToHelpCommand)
	}

	#endregion

#endregion



#region Clean-up the Add-on when it is removed.

$ExecutionContext.SessionState.Module.OnRemove = {
	$pgse = [Quest.PowerGUI.SDK.ScriptEditorFactory]::CurrentInstance
	
	#region Clean-up the HelpBrowser Panel
	
	#Remove the menu
	if (($goMenu = $pgse.Menus['MenuBar.Go']) -and
	    ($scriptExplorerCmdItem = $goMenu.Items['GoCommand.HelpBrowser'])) {
		$goMenu.Items.Remove($scriptExplorerCmdItem) | Out-Null
	}
	#Remove the commmand
	if ($scriptExplorerCmdItem = $pgse.Commands['GoCommand.HelpBrowser']) {
		$pgse.Commands.Remove($scriptExplorerCmdItem) | Out-Null
	}
	#Remove the toolwindow
	if ($PGScriptExplorer = $pgse.ToolWindows['HelpBrowser']) {
		$pgse.ToolWindows.Remove($PGScriptExplorer) | Out-Null
	}
	#endregion
	
	#region Clean-up the Help Panel
	
	#Remove the menu
	if (($goMenu = $pgse.Menus['MenuBar.Go']) -and
	    ($scriptExplorerCmdItem = $goMenu.Items['GoCommand.Help'])) {
		$goMenu.Items.Remove($scriptExplorerCmdItem) | Out-Null
	}
	#Remove the commmand
	if ($scriptExplorerCmdItem = $pgse.Commands['GoCommand.Help']) {
		$pgse.Commands.Remove($scriptExplorerCmdItem) | Out-Null
	}
	#Remove the toolwindow
	if ($PGScriptExplorer = $pgse.ToolWindows['Help']) {
		$pgse.ToolWindows.Remove($PGScriptExplorer) | Out-Null
	}
	#endregion
}

#endregion
