├── .gitignore ├── .travis.yml ├── LICENSE ├── PSmacOS ├── PSmacOS.psd1 └── PSmacOS.psm1 ├── Resources ├── Screenshot-Out-GridView.png ├── Screenshot-Show-MessageBox-Caution.png ├── Screenshot-Show-MessageBox-Note.png ├── Screenshot-Show-MessageBox-Plain.png └── Screenshot-Show-MessageBox-Stop.png ├── TODO ├── Tests └── Clipboard.Tests.ps1 ├── build.ps1 ├── deploy.psdeploy.ps1 ├── docs ├── Get-Clipboard.md ├── Out-GridView.md ├── Set-Clipboard.md └── Show-MessageBox.md ├── psake.ps1 ├── readme.md └── src ├── Cmdlets ├── Clipboard.cs ├── OutGridView.cs └── ShowMessageBox.cs ├── GridViewer ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── GVApplicationDelegate.h ├── GVApplicationDelegate.m ├── GVObjectTableViewController.h ├── GVObjectTableViewController.m ├── GVWindowController.h ├── GVWindowController.m ├── GridIcon.icns ├── GridViewer.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── Info.plist ├── Storyboard.storyboard ├── main.m └── ps_black_64.svg ├── MarshalExtensions.cs ├── NativeBridge ├── Clipboard.cs ├── GridView.cs └── MessageBox.cs ├── PSObjectHelper.cs ├── PSmacOS.csproj ├── PSmacOS.sln ├── global.json ├── libpsmacosbridging ├── CMakeLists.txt ├── bridge_memory.m ├── bridge_messagebox.m ├── bridge_pasteboard.m └── libpsmacosbridging.h └── nuget.config /.gitignore: -------------------------------------------------------------------------------- 1 | BuildOutput 2 | **/bin 3 | **/obj 4 | **/CMakeFiles 5 | src/libpsmacosbridging/build 6 | src/GridViewer/build 7 | 8 | build/ 9 | DerivedData/ 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | *.moved-aside 20 | *.xccheckout 21 | *.xcscmblueprint 22 | *.hmap 23 | *.ipa 24 | *.dSYM.zip 25 | *.dSYM 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: osx 2 | osx_image: xcode10 3 | language: csharp 4 | mono: none 5 | dotnet: 2.1.4 6 | 7 | addons: 8 | homebrew: 9 | casks: 10 | - powershell 11 | 12 | script: 13 | - pwsh -f "./build.ps1" 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Charlie Schmidt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PSmacOS/PSmacOS.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'PSmacOS' 3 | # 4 | # Generated by: Charlie Schmidt 5 | # 6 | # Generated on: 9/27/18 7 | # 8 | 9 | @{ 10 | 11 | # Script module or binary module file associated with this manifest. 12 | RootModule = 'PSmacOS.psm1' 13 | 14 | # Version number of this module. 15 | ModuleVersion = '0.0.8' 16 | 17 | # Supported PSEditions 18 | CompatiblePSEditions = @('Core') 19 | 20 | # ID used to uniquely identify this module 21 | GUID = '71e27bd1-3801-4bfe-b710-93d96365db31' 22 | 23 | # Author of this module 24 | Author = 'Charlie Schmidt' 25 | 26 | # Company or vendor of this module 27 | CompanyName = 'Unknown' 28 | 29 | # Copyright statement for this module 30 | Copyright = '(c) Charlie Schmidt. All rights reserved.' 31 | 32 | # Description of the functionality provided by this module 33 | Description = 'Cmdlets for macOS' 34 | 35 | # Minimum version of the PowerShell engine required by this module 36 | PowerShellVersion = '6.0' 37 | 38 | # Name of the PowerShell host required by this module 39 | # PowerShellHostName = '' 40 | 41 | # Minimum version of the PowerShell host required by this module 42 | # PowerShellHostVersion = '' 43 | 44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 45 | # DotNetFrameworkVersion = '' 46 | 47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 48 | # CLRVersion = '' 49 | 50 | # Processor architecture (None, X86, Amd64) required by this module 51 | # ProcessorArchitecture = '' 52 | 53 | # Modules that must be imported into the global environment prior to importing this module 54 | # RequiredModules = @() 55 | 56 | # Assemblies that must be loaded prior to importing this module 57 | # RequiredAssemblies = @() 58 | 59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 60 | # ScriptsToProcess = @() 61 | 62 | # Type files (.ps1xml) to be loaded when importing this module 63 | # TypesToProcess = @() 64 | 65 | # Format files (.ps1xml) to be loaded when importing this module 66 | # FormatsToProcess = @() 67 | 68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 69 | NestedModules = @('bin\PSmacOS.dll') 70 | 71 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. 72 | FunctionsToExport = @() 73 | 74 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. 75 | CmdletsToExport = @('Get-Clipboard','Set-Clipboard','Out-GridView','Show-MessageBox') 76 | 77 | # Variables to export from this module 78 | VariablesToExport = @() 79 | 80 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. 81 | AliasesToExport = @() 82 | 83 | # DSC resources to export from this module 84 | # DscResourcesToExport = @() 85 | 86 | # List of all modules packaged with this module 87 | # ModuleList = @() 88 | 89 | # List of all files packaged with this module 90 | # FileList = @() 91 | 92 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 93 | PrivateData = @{ 94 | 95 | PSData = @{ 96 | 97 | # Tags applied to this module. These help with module discovery in online galleries. 98 | Tags = @('macOS') 99 | 100 | # A URL to the license for this module. 101 | LicenseUri = 'https://github.com/charlieschmidt/PSmacOS/blob/master/LICENSE' 102 | 103 | # A URL to the main website for this project. 104 | ProjectUri = 'https://github.com/charlieschmidt/PSmacOS' 105 | 106 | # A URL to an icon representing this module. 107 | # IconUri = '' 108 | 109 | # ReleaseNotes of this module 110 | # ReleaseNotes = '' 111 | 112 | } # End of PSData hashtable 113 | 114 | } # End of PrivateData hashtable 115 | 116 | # HelpInfo URI of this module 117 | # HelpInfoURI = '' 118 | 119 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 120 | # DefaultCommandPrefix = '' 121 | 122 | } 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /PSmacOS/PSmacOS.psm1: -------------------------------------------------------------------------------- 1 | Write-Verbose "Setting GridViewer binary +x" 2 | chmod +x $PSScriptRoot/bin/Contents/MacOS/GridViewer 3 | -------------------------------------------------------------------------------- /Resources/Screenshot-Out-GridView.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlieschmidt/PSmacOS/09f3b8d0af3eb1239ceee5208372a358f5334181/Resources/Screenshot-Out-GridView.png -------------------------------------------------------------------------------- /Resources/Screenshot-Show-MessageBox-Caution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlieschmidt/PSmacOS/09f3b8d0af3eb1239ceee5208372a358f5334181/Resources/Screenshot-Show-MessageBox-Caution.png -------------------------------------------------------------------------------- /Resources/Screenshot-Show-MessageBox-Note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlieschmidt/PSmacOS/09f3b8d0af3eb1239ceee5208372a358f5334181/Resources/Screenshot-Show-MessageBox-Note.png -------------------------------------------------------------------------------- /Resources/Screenshot-Show-MessageBox-Plain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlieschmidt/PSmacOS/09f3b8d0af3eb1239ceee5208372a358f5334181/Resources/Screenshot-Show-MessageBox-Plain.png -------------------------------------------------------------------------------- /Resources/Screenshot-Show-MessageBox-Stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlieschmidt/PSmacOS/09f3b8d0af3eb1239ceee5208372a358f5334181/Resources/Screenshot-Show-MessageBox-Stop.png -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlieschmidt/PSmacOS/09f3b8d0af3eb1239ceee5208372a358f5334181/TODO -------------------------------------------------------------------------------- /Tests/Clipboard.Tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module $PSScriptRoot/../BuildOutput/PSmacOS/PSmacOS.psd1 2 | 3 | InModuleScope 'PSmacOS' { 4 | Describe "Clipboard Tests" { 5 | It "Gets and sets the macOS clipboard" { 6 | "asdf" | Set-Clipboard 7 | Get-Clipboard | Should -Be "asdf" 8 | 9 | Set-Clipboard "asdf","asdf" 10 | Get-Clipboard | Should -Be "asdf`nasdf" 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | [cmdletbinding()] 2 | param ($Task = 'Default',[switch]$Fast) 3 | 4 | # Grab nuget bits, install modules, set build variables, start build. 5 | Get-PackageProvider -Name NuGet -ForceBootstrap | Out-Null 6 | 7 | 8 | function Resolve-Module 9 | { 10 | [Cmdletbinding()] 11 | param 12 | ( 13 | [Parameter(Mandatory)] 14 | [string[]]$Name 15 | ) 16 | 17 | Process 18 | { 19 | foreach ($ModuleName in $Name) 20 | { 21 | $Module = Get-Module -Name $ModuleName -ListAvailable 22 | Write-Verbose -Message "Resolving Module $($ModuleName)" 23 | 24 | if ($Module) 25 | { 26 | $Version = $Module | Measure-Object -Property Version -Maximum | Select-Object -ExpandProperty Maximum 27 | $GalleryVersion = Find-Module -Name $ModuleName -Repository PSGallery | Measure-Object -Property Version -Maximum | Select-Object -ExpandProperty Maximum 28 | 29 | if ($Version -lt $GalleryVersion) 30 | { 31 | 32 | if ((Get-PSRepository -Name PSGallery).InstallationPolicy -ne 'Trusted') { Set-PSRepository -Name PSGallery -InstallationPolicy Trusted } 33 | 34 | Write-Verbose -Message "$($ModuleName) Installed Version [$($Version.tostring())] is outdated. Installing Gallery Version [$($GalleryVersion.tostring())]" 35 | 36 | Install-Module -Name $ModuleName -Force 37 | Import-Module -Name $ModuleName -Force -RequiredVersion $GalleryVersion 38 | } 39 | else 40 | { 41 | Write-Verbose -Message "Module Installed, Importing $($ModuleName)" 42 | Import-Module -Name $ModuleName -Force -RequiredVersion $Version 43 | } 44 | } 45 | else 46 | { 47 | Write-Verbose -Message "$($ModuleName) Missing, installing Module" 48 | Install-Module -Name $ModuleName -Force 49 | Import-Module -Name $ModuleName -Force -RequiredVersion $Version 50 | } 51 | } 52 | } 53 | } 54 | 55 | if ($Fast.IsPresent -eq $false) { 56 | # Grab nuget bits, install modules, set build variables, start build. 57 | Get-PackageProvider -Name NuGet -ForceBootstrap | Out-Null 58 | 59 | Resolve-Module Psake, PSDeploy, platyPS, Pester, BuildHelpers 60 | } else { 61 | Import-Module Psake, PSDeploy, platyPS, Pester, BuildHelpers 62 | } 63 | 64 | Set-BuildEnvironment -Force 65 | 66 | Invoke-psake -buildFile .\psake.ps1 -taskList $Task -nologo 67 | 68 | exit ( [int]( -not $psake.build_success ) ) 69 | -------------------------------------------------------------------------------- /deploy.psdeploy.ps1: -------------------------------------------------------------------------------- 1 | if( 2 | $env:BHProjectName -and $env:BHProjectName.Count -eq 1 -and 3 | $env:BHBuildSystem -eq 'Travis CI' -and 4 | $env:BHBranchName -eq "master" -and 5 | ($env:BHCommitMessage -match '!deploy' -or $env:BHCommitMessage -match '!release') 6 | ) 7 | { 8 | push-location BuildOutput 9 | Deploy Module { 10 | By PSGalleryModule { 11 | FromSource "$($ENV:BHBuildOutput)/$($ENV:BHProjectName)" 12 | To PSGallery 13 | WithOptions @{ 14 | ApiKey = $ENV:NugetApiKey 15 | } 16 | } 17 | } 18 | pop-location 19 | } 20 | else 21 | { 22 | "Skipping deployment: To deploy, ensure that...`n" + 23 | "`t* You are in a known build system (Current: $ENV:BHBuildSystem)`n" + 24 | "`t* You are committing to the master branch (Current: $ENV:BHBranchName) `n" + 25 | "`t* Your commit message includes !deploy or !release (Current: $ENV:BHCommitMessage)" | 26 | Write-Host 27 | } 28 | -------------------------------------------------------------------------------- /docs/Get-Clipboard.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSmacOS.dll-Help.xml 3 | Module Name: PSmacOS 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Get-Clipboard 9 | 10 | ## SYNOPSIS 11 | Get the macOS clipboard 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Get-Clipboard [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | Get the contents of the macOS clipboard and return them as a string. This is similar to the `pbpaste` command native to macOS, but with a PowerShell bent. 21 | 22 | ## EXAMPLES 23 | 24 | ### Example 1 25 | ```powershell 26 | PS C:\> Get-Clipboard 27 | ``` 28 | 29 | Fetch the contents of the clipboard and output them 30 | 31 | ## PARAMETERS 32 | 33 | ### CommonParameters 34 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. 35 | For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). 36 | 37 | ## INPUTS 38 | 39 | ### None 40 | ## OUTPUTS 41 | 42 | ### System.String 43 | ## NOTES 44 | 45 | ## RELATED LINKS 46 | [Set-Clipboard]() 47 | -------------------------------------------------------------------------------- /docs/Out-GridView.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSmacOS.dll-Help.xml 3 | Module Name: PSmacOS 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Out-GridView 9 | 10 | ## SYNOPSIS 11 | Sends output to an interactive table in a separate window. 12 | 13 | ## SYNTAX 14 | 15 | ### PassThru (Default) 16 | ``` 17 | Out-GridView [-InputObject ] [-Title ] [-PassThru] [] 18 | ``` 19 | 20 | ### Wait 21 | ``` 22 | Out-GridView [-InputObject ] [-Title ] [-Wait] [] 23 | ``` 24 | 25 | ### OutputMode 26 | ``` 27 | Out-GridView [-InputObject ] [-Title ] [-OutputMode ] [] 28 | ``` 29 | 30 | ## DESCRIPTION 31 | The Out-GridView cmdlet sends the output from a command to a grid view window where the output is displayed in an interactive table. 32 | 33 | ## EXAMPLES 34 | 35 | ### Example 1 36 | ```powershell 37 | PS C:\> Get-Process | Out-GridView 38 | ``` 39 | 40 | Gets current processes and sends them to a grid view window 41 | 42 | ### Example 2 43 | ```powershell 44 | PS C:\> Get-Process | Out-GridView -PassThru | Export-Csv selected-processes.csv 45 | ``` 46 | 47 | Gets current processes and sends them to a grid view window, the console will block until the window is closed. Any rows selected in the grid view window will be output from the function 48 | 49 | ## PARAMETERS 50 | 51 | ### -InputObject 52 | Specifies that the cmdlet accepts input for Out-GridView. 53 | 54 | ```yaml 55 | Type: PSObject 56 | Parameter Sets: (All) 57 | Aliases: 58 | 59 | Required: False 60 | Position: Named 61 | Default value: None 62 | Accept pipeline input: True (ByValue) 63 | Accept wildcard characters: False 64 | ``` 65 | 66 | ### -OutputMode 67 | Specifies the items that the interactive window sends down the pipeline as input to other commands. By default, this cmdlet does not generate any output. To send items from the interactive window down the pipeline, click to select the items and then click OK. 68 | The values of this parameter determine how many items you can send down the pipeline. 69 | 70 | * None. No items. This is the default value. 71 | * Single. Zero items or one item. Use this value when the next command can take only one input object. 72 | * Multiple. Zero, one, or many items. Use this value when the next command can take multiple input objects. This value is equivalent to the Passthru parameter. 73 | 74 | ```yaml 75 | Type: OutputModeOption 76 | Parameter Sets: OutputMode 77 | Aliases: 78 | Accepted values: None, Single, Multiple 79 | 80 | Required: False 81 | Position: Named 82 | Default value: None 83 | Accept pipeline input: False 84 | Accept wildcard characters: False 85 | ``` 86 | 87 | ### -PassThru 88 | Indicates that the cmdlet sends items from the interactive window down the pipeline as input to other commands. By default, this cmdlet does not generate any output. This parameter is equivalent to using the Multiple value of the OutputMode parameter. 89 | 90 | ```yaml 91 | Type: SwitchParameter 92 | Parameter Sets: PassThru 93 | Aliases: 94 | 95 | Required: False 96 | Position: Named 97 | Default value: None 98 | Accept pipeline input: False 99 | Accept wildcard characters: False 100 | ``` 101 | 102 | ### -Title 103 | Specifies the text that appears in the title bar of the Out-GridView window. 104 | 105 | ```yaml 106 | Type: String 107 | Parameter Sets: (All) 108 | Aliases: 109 | 110 | Required: False 111 | Position: Named 112 | Default value: None 113 | Accept pipeline input: False 114 | Accept wildcard characters: False 115 | ``` 116 | 117 | ### -Wait 118 | Indicates that the cmdlet suppresses the command prompt and prevents PowerShell from closing until the Out-GridView window is closed. By default, the command prompt returns when the Out-GridView window opens. 119 | 120 | ```yaml 121 | Type: SwitchParameter 122 | Parameter Sets: Wait 123 | Aliases: 124 | 125 | Required: False 126 | Position: Named 127 | Default value: None 128 | Accept pipeline input: False 129 | Accept wildcard characters: False 130 | ``` 131 | 132 | ### CommonParameters 133 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. 134 | For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). 135 | 136 | ## INPUTS 137 | 138 | ### System.Management.Automation.PSObject 139 | ## OUTPUTS 140 | 141 | ### System.Object 142 | ## NOTES 143 | 144 | ## RELATED LINKS 145 | [Show-MessageBox]() 146 | -------------------------------------------------------------------------------- /docs/Set-Clipboard.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSmacOS.dll-Help.xml 3 | Module Name: PSmacOS 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Set-Clipboard 9 | 10 | ## SYNOPSIS 11 | Set the macOS clipboard 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Set-Clipboard [-Value] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | Set the macOS clipboard from a string or string array. This is similar to the `pbcopy` command native to macOS, but with a PowerShell bent. 21 | 22 | ## EXAMPLES 23 | 24 | ### Example 1 25 | ```powershell 26 | PS C:\> Set-Clipboard -Value "TheString" 27 | ``` 28 | 29 | Set the conents of the clipboard to `TheString` 30 | 31 | ## PARAMETERS 32 | 33 | ### -Value 34 | The value to set to the clipboard 35 | 36 | ```yaml 37 | Type: String[] 38 | Parameter Sets: (All) 39 | Aliases: 40 | 41 | Required: True 42 | Position: 0 43 | Default value: None 44 | Accept pipeline input: True (ByPropertyName, ByValue) 45 | Accept wildcard characters: False 46 | ``` 47 | 48 | ### CommonParameters 49 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. 50 | For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). 51 | 52 | ## INPUTS 53 | 54 | ### System.String[] 55 | ## OUTPUTS 56 | 57 | ### System.Object 58 | ## NOTES 59 | 60 | ## RELATED LINKS 61 | [Get-Clipboard]() 62 | -------------------------------------------------------------------------------- /docs/Show-MessageBox.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSmacOS.dll-Help.xml 3 | Module Name: PSmacOS 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Show-MessageBox 9 | 10 | ## SYNOPSIS 11 | Show a message/alert box for the user to respond to 12 | 13 | ## SYNTAX 14 | 15 | ### SwitchButtons (Default) 16 | ``` 17 | Show-MessageBox [-Title ] -Message [-Timeout ] [-Type ] [-Buttons ] 18 | [] 19 | ``` 20 | 21 | ### AnyButtons 22 | ``` 23 | Show-MessageBox [-Title ] -Message [-Timeout ] [-Type ] 24 | [-ButtonOneLabel ] [-ButtonTwoLabel ] [-ButtonThreeLabel ] [] 25 | ``` 26 | 27 | ## DESCRIPTION 28 | Show a native message/alert box for the user to respond to 29 | 30 | ## EXAMPLES 31 | 32 | ### Example 1 33 | ```powershell 34 | PS C:\> Show-MessageBox -Title "Title" -Message "This is the message text. It is important." 35 | ``` 36 | 37 | Shows a message box with 1 button, on the right side, with text of `Ok` 38 | 39 | ### Example 2 40 | ```powershell 41 | PS C:\> Show-MessageBox -Title "Oh Noes" -Message "Something terrible happened." -Buttons "AbortRetryIgnore" 42 | ``` 43 | 44 | Shows a message box with 3 buttons - Abort, Retry, Ignore. 45 | 46 | ### Example 3 47 | ```powershell 48 | PS C:\> Show-MessageBox -Title "Custom!" -Message "Some weird message" -ButtonOneLabel "Push Me" -ButtonTwoLabel "Don't Push Me" 49 | ``` 50 | 51 | Shows a message box with 2 custom buttons. 52 | 53 | 54 | ## PARAMETERS 55 | 56 | ### -ButtonOneLabel 57 | Text for the first button 58 | 59 | ```yaml 60 | Type: String 61 | Parameter Sets: AnyButtons 62 | Aliases: 63 | 64 | Required: False 65 | Position: Named 66 | Default value: Ok 67 | Accept pipeline input: True (ByPropertyName, ByValue) 68 | Accept wildcard characters: False 69 | ``` 70 | 71 | ### -ButtonTwoLabel 72 | Text for the second button 73 | 74 | ```yaml 75 | Type: String 76 | Parameter Sets: AnyButtons 77 | Aliases: 78 | 79 | Required: False 80 | Position: Named 81 | Default value: None 82 | Accept pipeline input: True (ByPropertyName, ByValue) 83 | Accept wildcard characters: False 84 | ``` 85 | 86 | ### -ButtonThreeLabel 87 | Text for the third button 88 | 89 | ```yaml 90 | Type: String 91 | Parameter Sets: AnyButtons 92 | Aliases: 93 | 94 | Required: False 95 | Position: Named 96 | Default value: None 97 | Accept pipeline input: True (ByPropertyName, ByValue) 98 | Accept wildcard characters: False 99 | ``` 100 | 101 | ### -Buttons 102 | Use a predefined set of buttons 103 | 104 | ```yaml 105 | Type: String 106 | Parameter Sets: SwitchButtons 107 | Aliases: 108 | Accepted values: AbortRetryIgnore, OK, OKCancel, RetryCancel, YesNo, YesNoCancel 109 | 110 | Required: False 111 | Position: Named 112 | Default value: None 113 | Accept pipeline input: False 114 | Accept wildcard characters: False 115 | ``` 116 | 117 | ### -Message 118 | Main body text of the message box 119 | 120 | ```yaml 121 | Type: String 122 | Parameter Sets: (All) 123 | Aliases: 124 | 125 | Required: True 126 | Position: Named 127 | Default value: None 128 | Accept pipeline input: True (ByPropertyName, ByValue) 129 | Accept wildcard characters: False 130 | ``` 131 | 132 | ### -Timeout 133 | Keep the message box open for a certain time, then close and return indicating no selection 134 | 135 | ```yaml 136 | Type: Double 137 | Parameter Sets: (All) 138 | Aliases: 139 | 140 | Required: False 141 | Position: Named 142 | Default value: None 143 | Accept pipeline input: True (ByPropertyName, ByValue) 144 | Accept wildcard characters: False 145 | ``` 146 | 147 | ### -Title 148 | Title for the message box window 149 | 150 | ```yaml 151 | Type: String 152 | Parameter Sets: (All) 153 | Aliases: 154 | 155 | Required: False 156 | Position: Named 157 | Default value: None 158 | Accept pipeline input: True (ByPropertyName, ByValue) 159 | Accept wildcard characters: False 160 | ``` 161 | 162 | ### -Type 163 | Type for the message box window. This will change the icon used in the alert. 164 | 165 | * Plain - default macOS application icon (paper with A from ruler/paintbrush) 166 | * Caution - yellow triangle 167 | * Stop - red stop sign 168 | * Note - speech bubble with exclaimation mark 169 | 170 | ```yaml 171 | Type: Type 172 | Parameter Sets: (All) 173 | Aliases: 174 | Accepted values: Stop, Note, Caution, Plain 175 | 176 | Required: False 177 | Position: Named 178 | Default value: Plain 179 | Accept pipeline input: True (ByPropertyName, ByValue) 180 | Accept wildcard characters: False 181 | ``` 182 | 183 | ### CommonParameters 184 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. 185 | For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). 186 | 187 | ## INPUTS 188 | 189 | ### System.String 190 | ### System.Double 191 | ### PSmacOS.NativeBridge.MessageBox+Type 192 | ## OUTPUTS 193 | 194 | ### System.Object 195 | ## NOTES 196 | 197 | ## RELATED LINKS 198 | [Out-GridView]() 199 | -------------------------------------------------------------------------------- /psake.ps1: -------------------------------------------------------------------------------- 1 | # PSake makes variables declared here available in other scriptblocks 2 | # Init some things 3 | Properties { 4 | # Find the build folder based on build system 5 | $ProjectRoot = $ENV:BHProjectPath 6 | if (-not $ProjectRoot) 7 | { 8 | $ProjectRoot = $PSScriptRoot 9 | } 10 | 11 | $Timestamp = Get-Date -uformat "%Y%m%d-%H%M%S" 12 | $PSVersion = $PSVersionTable.PSVersion.Major 13 | $TestFile = "TestResults_PS$PSVersion`_$TimeStamp.xml" 14 | $lines = '----------------------------------------------------------------------' 15 | 16 | $Verbose = @{} 17 | if($ENV:BHCommitMessage -match "!verbose") 18 | { 19 | $Verbose = @{Verbose = $True} 20 | } 21 | } 22 | 23 | 24 | Task Default -Depends Init,Clean,Build,BuildDocs,Test,Deploy 25 | 26 | 27 | Task Init { 28 | $lines 29 | Set-Location $ProjectRoot 30 | "Build System Details:" 31 | Get-Item ENV:BH* 32 | "`n" 33 | } 34 | 35 | Task Clean -PreCondition { Test-Path $ENV:BHBuildOutput } { 36 | push-location $ENV:BHBuildOutput 37 | remove-item * -recurse -force 38 | Pop-Location 39 | 40 | push-location -Path $ProjectRoot/src/GridViewer 41 | remove-item build -recurse -force -ErrorAction SilentlyContinue 42 | pop-location 43 | 44 | push-location -Path $ProjectRoot/src/libpsmacosbridging 45 | remove-item build -recurse -force -ErrorAction SilentlyContinue 46 | pop-location 47 | 48 | push-location -Path $ProjectRoot/src 49 | dotnet clean 50 | if ($lastexitcode -ne 0) 51 | { 52 | throw "dotnet clean failed" 53 | } 54 | pop-location 55 | } 56 | 57 | Task CompileObjC { 58 | $lines 59 | 'Compiling Objective-C bridging code' 60 | push-location -Path "$ProjectRoot/src/GridViewer" 61 | if ($env:BHBuildSystem -eq 'Travis CI') { 62 | $configuration = "Release" 63 | } else { 64 | $Configuration = "Debug" 65 | } 66 | 67 | xcrun xcodebuild -alltargets -configuration $Configuration 68 | if ($lastexitcode -ne 0) 69 | { 70 | throw "xcrun xcodebuild failed" 71 | } 72 | Copy-Item -Path "./Build/$Configuration/GridViewer.app" -Destination $ENV:BHBuildOutput\PSmacOS\bin -Recurse -Force 73 | pop-location 74 | 75 | push-location -Path "$ProjectRoot/src/libpsmacosbridging" 76 | new-item -name "build" -ItemType "Directory" -Force -ErrorAction SilentlyContinue | Out-Null 77 | push-location build 78 | cmake .. 79 | if ($lastexitcode -ne 0) 80 | { 81 | throw "cmake failed" 82 | } 83 | 84 | make 85 | if ($lastexitcode -ne 0) 86 | { 87 | throw "make failed" 88 | } 89 | 90 | copy-item "./lib/*.dylib" $ENV:BHBuildOutput\PSmacOS\bin -Recurse -Force 91 | pop-location 92 | pop-location 93 | 94 | "`n" 95 | } 96 | 97 | Task CompileCSharp -Depends CompileObjC { 98 | $lines 99 | 'Compiling C#' 100 | push-location -Path "$ProjectRoot/src" 101 | dotnet build -o $ENV:BHBuildOutput\PSmacOS\bin 102 | if ($lastexitcode -ne 0) 103 | { 104 | throw "dotnet build failed" 105 | } 106 | pop-location 107 | "`n" 108 | } 109 | 110 | Task Build -Depends CompileCSharp { 111 | $lines 112 | 'Assembling Module' 113 | Copy-Item "$PSScriptRoot\PSmacOS\*" "$($ENV:BHBuildOutput)\PSmacOS" -Recurse -Force 114 | "`n" 115 | } 116 | 117 | Task BuildDocs -Depends Build { 118 | $lines 119 | 'Building documentation' 120 | New-ExternalHelp ".\docs" -OutputPath "$($ENV:BHBuildOutput)\PSmacOS\en-US" -Force 121 | "`n" 122 | } 123 | 124 | Task Test -Depends Build { 125 | $lines 126 | if ($env:BHBuildSystem -eq 'Travis CI' -or $env:BHBranchName -eq "master") { 127 | "Testing with PowerShell $PSVersion" 128 | 129 | # Gather test results. Store them in a variable and file 130 | $TestResults = Invoke-Pester -Path $ProjectRoot\Tests -PassThru -OutputFormat NUnitXml -OutputFile "$ProjectRoot\$TestFile" 131 | 132 | # In Appveyor? Upload our tests! #Abstract this into a function? 133 | If($ENV:BHBuildSystem -eq 'AppVeyor') 134 | { 135 | (New-Object 'System.Net.WebClient').UploadFile( 136 | "https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", 137 | "$ProjectRoot\$TestFile" ) 138 | } 139 | 140 | Remove-Item "$ProjectRoot\$TestFile" -Force -ErrorAction SilentlyContinue 141 | 142 | # Failed tests? 143 | # Need to tell psake or it will proceed to the deployment. Danger! 144 | if($TestResults.FailedCount -gt 0) 145 | { 146 | Write-Error "Failed '$($TestResults.FailedCount)' tests, build failed" 147 | } 148 | } else { 149 | "Skipping testing" 150 | } 151 | "`n" 152 | } 153 | 154 | Task BumpVersion { 155 | $lines 156 | 157 | Update-Metadata -Path $env:BHPSModuleManifest 158 | 159 | "`n" 160 | } 161 | 162 | Task Deploy -Depends Test,BuildDocs { 163 | $lines 164 | 165 | $Params = @{ 166 | Path = $ProjectRoot 167 | Force = $true 168 | Recurse = $false # We keep psdeploy artifacts, avoid deploying those : ) 169 | } 170 | 171 | Invoke-PSDeploy @Verbose @Params 172 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.com/charlieschmidt/PSmacOS.svg?branch=master)](https://travis-ci.com/charlieschmidt/PSmacOS) 2 | 3 | PSmacOS 4 | ======== 5 | 6 | PowerShell module containing convience or platform-specific cmdlets for macOS. 7 | 8 | ## Installation 9 | 10 | `Install-Module PSmacOS` to install from PSGallery 11 | 12 | ## Documentation 13 | 14 | Look in the [docs](docs/) folder. 15 | 16 | ## Cmdlet list 17 | 18 | * [Get-Clipboard](docs/Get-Clipboard.md) - get clipboard content as a string and return to the pipeline 19 | 20 | * [Set-Clipboard](docs/Set-Clipboard.md) - set clipboard content 21 | 22 | * [Show-MessageBox](docs/Show-MessageBox.md) - show a message/alert box 23 | 24 | ![Show-MessageBox Plain Type screenshot](/Resources/Screenshot-Show-MessageBox-Plain.png) 25 | ![Show-MessageBox Note Type screenshot](/Resources/Screenshot-Show-MessageBox-Note.png) 26 | ![Show-MessageBox Caution Type screenshot](/Resources/Screenshot-Show-MessageBox-Caution.png) 27 | ![Show-MessageBox Stop Type screenshot](/Resources/Screenshot-Show-MessageBox-Stop.png) 28 | 29 | 30 | * [Out-GridView](docs/Out-GridView.md) - show a searchable table of objects from the pipeline 31 | 32 | ![Out-GridView screenshot](/Resources/Screenshot-Out-GridView.png) 33 | 34 | ## Planned/Future 35 | 36 | * `Start-Process` or equivalent that knows about macOS app bundles [like the `open` command but powershelly] 37 | 38 | * `Invoke-AppleScript` - why not? 39 | -------------------------------------------------------------------------------- /src/Cmdlets/Clipboard.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | using System.Collections.Generic; 6 | using System.Collections; 7 | 8 | namespace PSmacOS.Cmdlets 9 | { 10 | [OutputType(typeof(string))] 11 | [Cmdlet(VerbsCommon.Get, "Clipboard")] 12 | [Alias("gcb")] 13 | public class GetClipboard : Cmdlet 14 | { 15 | protected override void BeginProcessing() 16 | { 17 | var clipboard = NativeBridge.Clipboard.Get(); 18 | WriteObject($"{clipboard}"); 19 | } 20 | } 21 | 22 | 23 | [Cmdlet(VerbsCommon.Set, "Clipboard")] 24 | [Alias("scb")] 25 | public class SetClipboard : Cmdlet 26 | { 27 | private List _contentList = new List(); 28 | 29 | [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] 30 | [AllowNull] 31 | [AllowEmptyCollection] 32 | [AllowEmptyString] 33 | public string[] Value { get; set; } 34 | 35 | protected override void BeginProcessing() 36 | { 37 | _contentList.Clear(); 38 | } 39 | 40 | protected override void ProcessRecord() 41 | { 42 | if (Value != null) 43 | { 44 | _contentList.AddRange(Value); 45 | } 46 | } 47 | 48 | protected override void EndProcessing() 49 | { 50 | if (_contentList != null) 51 | { 52 | var value = string.Join(Environment.NewLine, _contentList.ToArray(), 0, _contentList.Count); 53 | 54 | var ret = NativeBridge.Clipboard.Set(value); 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /src/Cmdlets/OutGridView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | using System.Collections.Generic; 6 | using System.Collections; 7 | using System.Management.Automation.Internal; 8 | using System.Management.Automation.Runspaces; 9 | 10 | using Microsoft.PowerShell.Commands.Internal.Format; 11 | 12 | // https://github.com/PowerShell/PowerShell/blob/a3786158ca51cd65388743f900b69ec9e253c3d9/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs 13 | 14 | namespace PSmacOS.Cmdlets 15 | { 16 | /// 17 | /// Enum for SelectionMode parameter. 18 | /// 19 | public enum OutputModeOption 20 | { 21 | /// 22 | /// None is the default and it means OK and Cancel will not be present 23 | /// and no objects will be written to the pipeline. 24 | /// The selectionMode of the actual list will still be multiple. 25 | /// 26 | None, 27 | /// 28 | /// Allow selection of one single item to be written to the pipeline. 29 | /// 30 | Single, 31 | /// 32 | ///Allow select of multiple items to be written to the pipeline. 33 | /// 34 | Multiple 35 | } 36 | 37 | [Cmdlet(VerbsData.Out, "GridView",DefaultParameterSetName = "PassThru")] 38 | [Alias("ogv")] 39 | public class OutGridView : PSCmdlet 40 | { 41 | //private TypeInfoDataBase _typeInfoDataBase; 42 | //private PSPropertyExpressionFactory _expressionFactory; 43 | 44 | /// 45 | /// This parameter specifies the current pipeline object. 46 | /// 47 | [Parameter(ValueFromPipeline = true)] 48 | public PSObject InputObject { get; set; } = AutomationNull.Value; 49 | 50 | /// 51 | /// Gets/sets the title of the Out-GridView window. 52 | /// 53 | [Parameter] 54 | [ValidateNotNullOrEmpty] 55 | public string Title { get; set; } 56 | 57 | /// 58 | /// Get or sets a value indicating whether the cmdlet should wait for the window to be closed. 59 | /// 60 | [Parameter(ParameterSetName = "Wait")] 61 | public SwitchParameter Wait { get; set; } 62 | 63 | /// 64 | /// Get or sets a value indicating whether the selected items should be written to the pipeline 65 | /// and if it should be possible to select multiple or single list items. 66 | /// 67 | [Parameter(ParameterSetName = "OutputMode")] 68 | public OutputModeOption OutputMode { set; get; } 69 | 70 | /// 71 | /// Gets or sets a value indicating whether the selected items should be written to the pipeline. 72 | /// Setting this to true is the same as setting the OutputMode to Multiple. 73 | /// 74 | [Parameter(ParameterSetName = "PassThru")] 75 | public SwitchParameter PassThru 76 | { 77 | set { this.OutputMode = value.IsPresent ? OutputModeOption.Multiple : OutputModeOption.None; } 78 | get { return OutputMode == OutputModeOption.Multiple ? new SwitchParameter(true) : new SwitchParameter(false); } 79 | } 80 | 81 | 82 | /// 83 | /// Provides a one-time, pre-processing functionality for the cmdlet. 84 | /// 85 | protected override void BeginProcessing() 86 | { 87 | // Set up the ExpressionFactory 88 | //_expressionFactory = new PSPropertyExpressionFactory(); 89 | 90 | // If the value of the Title parameter is valid, use it as a window's title. 91 | if (this.Title != null) 92 | { 93 | NativeBridge.GridView.Start(this.Title, OutputMode, this); 94 | } 95 | else 96 | { 97 | // Using the command line as a title. 98 | NativeBridge.GridView.Start(this.MyInvocation.Line, OutputMode, this); 99 | } 100 | 101 | // Load the Type info database. 102 | //_typeInfoDataBase = this.Context.FormatDBManager.GetTypeInfoDataBase(); 103 | } 104 | 105 | 106 | 107 | /// 108 | /// Provides a record-by-record processing functionality for the cmdlet. 109 | /// 110 | protected override void ProcessRecord() 111 | { 112 | if (InputObject == null || InputObject == AutomationNull.Value) 113 | { 114 | return; 115 | } 116 | 117 | IDictionary dictionary = InputObject.BaseObject as IDictionary; 118 | if (dictionary != null) 119 | { 120 | // Dictionaries should be enumerated through because the pipeline does not enumerate through them. 121 | foreach (DictionaryEntry entry in dictionary) 122 | { 123 | ProcessObject(PSObjectHelper.AsPSObject(entry)); 124 | } 125 | } 126 | else 127 | { 128 | ProcessObject(InputObject); 129 | } 130 | } 131 | 132 | /// 133 | /// StopProcessing is called close the window when Ctrl+C in the command prompt. 134 | /// 135 | protected override void StopProcessing() 136 | { 137 | if (this.Wait || this.OutputMode != OutputModeOption.None) 138 | { 139 | NativeBridge.GridView.Close(); 140 | } 141 | } 142 | 143 | /// 144 | /// Blocks depending on the wait and selected. 145 | /// 146 | protected override void EndProcessing() 147 | { 148 | base.EndProcessing(); 149 | 150 | if (NativeBridge.GridView.IsClosed()) 151 | { 152 | return; 153 | } 154 | 155 | // If -Wait is used or outputMode is not None we have to wait for the window to be closed 156 | // The pipeline will be blocked while we don't return 157 | if (this.Wait || this.OutputMode != OutputModeOption.None) 158 | { 159 | NativeBridge.GridView.WaitForExit(); 160 | } 161 | 162 | // Output selected items to pipeline. 163 | List selectedItems = NativeBridge.GridView.GetSelectedItems(); 164 | if (this.OutputMode != OutputModeOption.None && selectedItems != null) 165 | { 166 | foreach (PSObject selectedItem in selectedItems) 167 | { 168 | if (selectedItem == null) 169 | { 170 | continue; 171 | } 172 | /* 173 | PSPropertyInfo originalObjectProperty = selectedItem.Properties[OutWindowProxy.OriginalObjectPropertyName]; 174 | if (originalObjectProperty == null) 175 | { 176 | return; 177 | } 178 | */ 179 | this.WriteObject(selectedItem, false); 180 | } 181 | } 182 | } 183 | 184 | private const string DataNotQualifiedForGridView = "DataNotQualifiedForGridView"; 185 | /// 186 | /// Execute formatting on a single object. 187 | /// 188 | /// object to process 189 | private void ProcessObject(PSObject input) 190 | { 191 | // Make sure the OGV window is not closed. 192 | if (NativeBridge.GridView.IsClosed()) 193 | { 194 | /* 195 | LocalPipeline pipeline = (LocalPipeline)this.Context.CurrentRunspace.GetCurrentlyRunningPipeline(); 196 | 197 | if (pipeline != null && !pipeline.IsStopping) 198 | { 199 | // Stop the pipeline cleanly. 200 | pipeline.StopAsync(); 201 | } 202 | */ 203 | return; 204 | } 205 | 206 | Object baseObject = input.BaseObject; 207 | 208 | // Throw a terminating error for types that are not supported. 209 | if (baseObject is ScriptBlock || 210 | baseObject is SwitchParameter || 211 | baseObject is PSReference || 212 | baseObject is PSObject) 213 | { 214 | ErrorRecord error = new ErrorRecord( 215 | new FormatException("The data format is not supported by Out-GridView."), 216 | DataNotQualifiedForGridView, 217 | ErrorCategory.InvalidType, 218 | null); 219 | 220 | this.ThrowTerminatingError(error); 221 | } 222 | 223 | /* 224 | if (DefaultScalarTypes.IsTypeInList(input.TypeNames) || 225 | OutOfBandFormatViewManager.IsPropertyLessObject(input)) 226 | { 227 | WriteVerbose("is scalar"); 228 | } else { 229 | WriteVerbose("is not scalar"); 230 | } 231 | */ 232 | 233 | NativeBridge.GridView.AddRecord(InputObject); 234 | 235 | /* 236 | if (_gridHeader == null) 237 | { 238 | // Columns have not been added yet; Start the main window and add columns. 239 | _windowProxy.ShowWindow(); 240 | _gridHeader = GridHeader.ConstructGridHeader(input, this); 241 | } 242 | else 243 | { 244 | _gridHeader.ProcessInputObject(input); 245 | } 246 | */ 247 | 248 | /* 249 | // Some thread synchronization needed. 250 | Exception exception = _windowProxy.GetLastException(); 251 | if (exception != null) 252 | { 253 | ErrorRecord error = new ErrorRecord( 254 | exception, 255 | "ManagementListInvocationException", 256 | ErrorCategory.OperationStopped, 257 | null); 258 | 259 | this.ThrowTerminatingError(error); 260 | } 261 | */ 262 | } 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /src/Cmdlets/ShowMessageBox.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | using System.Collections.Generic; 6 | using System.Collections; 7 | 8 | namespace PSmacOS.Cmdlets 9 | { 10 | [Cmdlet(VerbsCommon.Show, "MessageBox",DefaultParameterSetName = "SwitchButtons")] 11 | public class ShowMessageBox : PSCmdlet 12 | { 13 | [Parameter(Mandatory = false, ParameterSetName = "SwitchButtons")] 14 | [Parameter(Mandatory = false, ParameterSetName = "AnyButtons")] 15 | public string Title { get; set; } = string.Empty; 16 | 17 | [Parameter(Mandatory = true, ParameterSetName = "SwitchButtons")] 18 | [Parameter(Mandatory = true, ParameterSetName = "AnyButtons")] 19 | [ValidateNotNullOrEmpty()] 20 | public string Message { get; set; } 21 | 22 | [Parameter(Mandatory = false, ParameterSetName = "SwitchButtons")] 23 | [Parameter(Mandatory = false, ParameterSetName = "AnyButtons")] 24 | public double Timeout { get; set; } = 0; 25 | 26 | [Parameter(Mandatory = false, ParameterSetName = "SwitchButtons")] 27 | [Parameter(Mandatory = false, ParameterSetName = "AnyButtons")] 28 | public NativeBridge.MessageBox.Type Type { get; set; } = NativeBridge.MessageBox.Type.Plain; 29 | 30 | 31 | [Parameter(Mandatory = false, ParameterSetName = "AnyButtons")] 32 | public string ButtonOneLabel { get; set; } = "Ok"; 33 | 34 | [Parameter(Mandatory = false, ParameterSetName = "AnyButtons")] 35 | public string ButtonTwoLabel { get; set; } = null; 36 | 37 | [Parameter(Mandatory = false, ParameterSetName = "AnyButtons")] 38 | public string ButtonThreeLabel { get; set; } = null; 39 | 40 | [Parameter(Mandatory = false, ParameterSetName = "SwitchButtons")] 41 | [ValidateSet("AbortRetryIgnore","OK", "OKCancel", "RetryCancel", "YesNo", "YesNoCancel")] 42 | public string Buttons { get; set; } = "Ok"; 43 | 44 | protected override void ProcessRecord() 45 | { 46 | if (ParameterSetName == "SwitchButtons") { 47 | switch (Buttons.ToLowerInvariant()) { 48 | case "abortretryignore": 49 | ButtonOneLabel = "Abort"; 50 | ButtonTwoLabel = "Retry"; 51 | ButtonThreeLabel = "Ignore"; 52 | break; 53 | case "ok": 54 | ButtonOneLabel = "OK"; 55 | break; 56 | case "okcancel": 57 | ButtonOneLabel = "OK"; 58 | ButtonTwoLabel = "Cancel"; 59 | break; 60 | case "retrycancel": 61 | ButtonOneLabel = "Retry"; 62 | ButtonTwoLabel = "Cancel"; 63 | break; 64 | case "yesno": 65 | ButtonOneLabel = "Yes"; 66 | ButtonTwoLabel = "No"; 67 | break; 68 | case "yesnocancel": 69 | ButtonOneLabel = "Yes"; 70 | ButtonTwoLabel = "No"; 71 | ButtonThreeLabel = "Cancel"; 72 | break; 73 | } 74 | } 75 | 76 | var buttonPressed = NativeBridge.MessageBox.Show(Timeout, Type, Title, Message, ButtonOneLabel, ButtonTwoLabel, ButtonThreeLabel); 77 | switch (buttonPressed) { 78 | case 0: 79 | case 1: 80 | case 2: 81 | WriteObject(buttonPressed+1); 82 | break; 83 | case 3: // timeout 84 | WriteObject(0); 85 | break; 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/GridViewer/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /src/GridViewer/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /src/GridViewer/GVApplicationDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // GVApplicationDelegate.h 3 | // GridViewer 4 | // 5 | // Created by Charlie Schmidt on 10/1/18. 6 | // Copyright © 2018 Charlie Schmidt. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GVApplicationDelegate : NSObject 12 | 13 | @property (strong,nonatomic) NSString *title; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /src/GridViewer/GVApplicationDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // GVApplicationDelegate.m 3 | // GridViewer 4 | // 5 | // Created by Charlie Schmidt on 10/1/18. 6 | // Copyright © 2018 Charlie Schmidt. All rights reserved. 7 | // 8 | 9 | #import "GVApplicationDelegate.h" 10 | #import "GVObjectTableViewController.h" 11 | #import "GVWindowController.h" 12 | 13 | @interface GVApplicationDelegate () 14 | 15 | @end 16 | 17 | @implementation GVApplicationDelegate 18 | 19 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 20 | NSWindow *mainWindow = [NSApplication sharedApplication].mainWindow; 21 | 22 | NSArray *args = [[NSProcessInfo processInfo] arguments]; 23 | //NSLog(@"args: %@",args); 24 | //NSLog(@"arg count: %lu",(unsigned long)args.count); 25 | //NSLog(@"arg: '%@'",[args objectAtIndex:1]); 26 | 27 | if (args.count == 2) { 28 | [mainWindow setTitle:[args objectAtIndex:1]]; 29 | } else { 30 | [mainWindow setTitle:@"GridView"]; 31 | } 32 | 33 | [mainWindow orderFrontRegardless]; 34 | [mainWindow makeKeyAndOrderFront:nil]; 35 | 36 | } 37 | 38 | - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)application { 39 | return YES; 40 | } 41 | 42 | - (void)applicationWillTerminate:(NSNotification *)aNotification { 43 | 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /src/GridViewer/GVObjectTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GVObjectTableViewController.h 3 | // GridViewer 4 | // 5 | // Created by Charlie Schmidt on 10/1/18. 6 | // Copyright © 2018 Charlie Schmidt. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | 13 | @interface GVObjectTableViewController : NSViewController 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /src/GridViewer/GVObjectTableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GVObjectTableViewController.m 3 | // GridViewer 4 | // 5 | // Created by Charlie Schmidt on 10/1/18. 6 | // Copyright © 2018 Charlie Schmidt. All rights reserved. 7 | // 8 | 9 | // line-by-line NSStreamDelegate inspired/copied from https://github.com/AlexMoffat/line-by-line-ios-file-reader 10 | 11 | #import "GVObjectTableViewController.h" 12 | 13 | 14 | @implementation NSDictionary (PSObject) 15 | 16 | // a lot of the rest of the code is much easier to deal with if we make the object (stored as a dictionary) have lowercase keys. we can't ensure that all the powershell objects 17 | // coming in on the pipe will all have their property names in the same string case 18 | // this is an extension method to convert a dictionary to one with lowercase keys 19 | - (NSDictionary *)dictionaryWithLowerCaseKeys { 20 | NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:0]; 21 | NSString *key; 22 | 23 | for (key in self) { 24 | id val = [self objectForKey:key]; 25 | 26 | [result setObject:val forKey:[key lowercaseString]]; 27 | } 28 | 29 | return result; 30 | } 31 | 32 | @end 33 | 34 | @interface GVObjectTableViewController () 35 | 36 | // the objects from the pipeline 37 | @property (strong) IBOutlet NSArrayController *objects; 38 | 39 | @property (strong) NSString *totalCountStringValue; 40 | @property (strong) NSString *selectedCountStringValue; 41 | @property (strong) NSString *filteredCountStringValue; 42 | 43 | @property (weak) IBOutlet NSSearchFieldCell *searchField; 44 | @property (weak) IBOutlet NSTableView *tableView; 45 | 46 | - (IBAction)closeButtonClicked:(id)sender; 47 | - (IBAction)okButtonClicked:(id)sender; 48 | 49 | // keep track of (and update as more come in over the pipeline) the objc Class being used to store each key/title/column/property 50 | @property (strong, nonatomic) NSMutableDictionary *keyClasses; 51 | 52 | // Buffer to hold any unprocessed string data after each loop. 53 | @property (strong, nonatomic) NSString *stringBuffer; 54 | 55 | @end 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | @implementation GVObjectTableViewController 64 | 65 | @synthesize stringBuffer; 66 | @synthesize objects; 67 | @synthesize keyClasses; 68 | 69 | - (void)controlTextDidChange:(NSNotification *)obj { 70 | //NSLog(@"in control text did change"); 71 | // the search field has changed 72 | if (self.keyClasses.allKeys && [self.keyClasses.allKeys count] > 0 && [self.searchField.stringValue isEqualToString:@""] == false) { 73 | // it has text 74 | 75 | //NSLog(@"looking for %@",self.searchField.stringValue); 76 | 77 | // build a predicate of () or () or () where each element is a comparison one of the object's properties 78 | NSMutableArray *subPredicates = [NSMutableArray array]; 79 | 80 | for (NSString *key in self.keyClasses.allKeys) { 81 | // foreach key, add a () clause 82 | NSPredicate *subPredicate = nil; 83 | //NSLog(@"key = %@",key); 84 | Class klass = [keyClasses objectForKey:key]; 85 | //NSLog(@"value = %@",klass); 86 | 87 | if ([klass isSubclassOfClass:[NSString class]]) { 88 | // if it's a string, normal comparison 89 | subPredicate = [NSPredicate predicateWithFormat:@"(%K != nil AND %K CONTAINS[cd] %@)", key, key, self.searchField.stringValue]; 90 | } else if ([klass isSubclassOfClass:[NSNumber class]]) { 91 | // if it's a number, check if null and then compare to stringvalue 92 | subPredicate = [NSPredicate predicateWithFormat:@"(%K != nil AND %K.stringValue CONTAINS[cd] %@)", key, key, self.searchField.stringValue]; 93 | } else { 94 | //NSLog(@"No predicate is appropriate"); 95 | } 96 | 97 | if (subPredicate != nil) { 98 | [subPredicates addObject:subPredicate]; 99 | } 100 | } 101 | 102 | // create master predicate of all subpredicates OR'd together 103 | NSPredicate *filter = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates]; 104 | [self.objects setFilterPredicate:filter]; 105 | 106 | [self.tableView reloadData]; 107 | } else { 108 | // clear predicates 109 | [self.objects setFilterPredicate:nil]; 110 | [self.tableView reloadData]; 111 | } 112 | 113 | [self updateInfoLabels]; 114 | //NSLog(@"in control text was changed"); 115 | } 116 | 117 | 118 | - (IBAction)closeButtonClicked:(id)sender { 119 | [self.view.window close]; 120 | } 121 | 122 | - (IBAction)okButtonClicked:(id)sender { 123 | for (id obj in [self.objects.arrangedObjects objectsAtIndexes:[self.tableView selectedRowIndexes]]) { 124 | NSString *s = [[NSString alloc] initWithFormat:@"%@\n",[obj objectForKey:@"__gridviewer_psobject_index"]]; 125 | [[NSFileHandle fileHandleWithStandardOutput] writeData: [s dataUsingEncoding: NSUTF8StringEncoding]]; 126 | } 127 | [self.view.window close]; 128 | } 129 | 130 | unsigned int const TRY_TO_READ = 1024*10; 131 | uint8_t _buffer[TRY_TO_READ]; 132 | 133 | - (void)readDataFromStream:(NSStream *)theStream { 134 | NSInteger length = [(NSInputStream *)theStream read:_buffer maxLength:TRY_TO_READ]; 135 | if (length) { 136 | //NSLog(@"read %ld\n",(long)length); 137 | if (self.stringBuffer) { 138 | // Some data left from the last time this method was called so 139 | // append the new data. 140 | self.stringBuffer = [self.stringBuffer stringByAppendingString:[[NSString alloc] initWithBytes:_buffer length:length encoding:NSUTF8StringEncoding]]; 141 | } else { 142 | // No data left over from last time. 143 | self.stringBuffer = [[NSString alloc] initWithBytes:_buffer length:length encoding:NSUTF8StringEncoding]; 144 | } 145 | // Split on newlines. 146 | NSArray *lines = [self.stringBuffer componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\r\n"]]; 147 | // Lines are processed in arrears, that is each time round the loop we send 148 | // to the lineProcessor the line read on the previous time round. This is because 149 | // we might not get a complete last line. However, if the whole stringBuffer ends 150 | // with the newlines then we get an empty string and so know we read a complete 151 | // line. Any remaining data is stored in stringBuffer and used on the next loop 152 | // or sent when the stream is closed. 153 | NSString *lineToProcess = nil; 154 | for (NSString *line in lines) { 155 | if (lineToProcess) { 156 | [self processLine:lineToProcess]; 157 | // Use an empty string here so that files 158 | // that end with a newline have a final empty 159 | // line just like if reading with stringWithContentsOfFile 160 | // and then splitting. 161 | lineToProcess = @""; 162 | } 163 | if (![line isEqualToString:@""]) { 164 | lineToProcess = line; 165 | } 166 | } 167 | // Leave any remaining data in the buffer. 168 | self.stringBuffer = lineToProcess; 169 | } 170 | } 171 | 172 | - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)streamEvent { 173 | if (streamEvent & NSStreamEventHasBytesAvailable) { 174 | [self readDataFromStream:stream]; 175 | 176 | //NSLog(@"stdinStream had bytes available %@",[dateFormatter stringFromDate:[NSDate date]]); 177 | } 178 | 179 | if (streamEvent & NSStreamEventErrorOccurred) { 180 | //! FIXME - consider probably closing the window or.. bailing out somehow here? 181 | return; 182 | } 183 | 184 | if (streamEvent & NSStreamEventEndEncountered) { 185 | [stream close]; 186 | [stream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 187 | // Treat anything left in stringBuffer as the remaining line. 188 | if (self.stringBuffer) { 189 | [self processLine:self.stringBuffer]; 190 | self.stringBuffer = nil; 191 | } 192 | 193 | 194 | //NSLog(@"stdinStream finished %@",[dateFormatter stringFromDate:[NSDate date]]); 195 | } 196 | } 197 | 198 | - (void)viewDidLoad { 199 | [super viewDidLoad]; 200 | 201 | // store the key -> class 202 | keyClasses = [[NSMutableDictionary alloc] init]; 203 | 204 | // read from stdin in the 'background' 205 | NSStream *stdinStream = [[NSInputStream alloc] initWithFileAtPath:@"/dev/stdin"]; 206 | [stdinStream setDelegate:self]; 207 | [stdinStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 208 | [stdinStream open]; 209 | 210 | /* 211 | [self processLine:@"{\"a\":\"the thing\",\"b\":\"the other\"}"]; 212 | [self processLine:@"{\"a\":\"2the thing\",\"b\":\"2the other\"}"]; 213 | [self processLine:@"{\"a\":\"3the thing\",\"b\":\"3the other\"}"]; 214 | */ 215 | } 216 | 217 | unsigned int linesRead = 0; 218 | // process an object from the pipeline 219 | - (void)processLine:(NSString*)json { 220 | if (json != nil && [json isEqualToString:@""] == false) { 221 | // parse the json 222 | NSData *objectData = [json dataUsingEncoding:NSUTF8StringEncoding]; 223 | NSError *jsonError; 224 | NSDictionary *objectWithTitles = [NSJSONSerialization JSONObjectWithData:objectData 225 | options:0 226 | error:&jsonError]; 227 | NSDictionary *objectWithKeys = [objectWithTitles dictionaryWithLowerCaseKeys]; 228 | 229 | // foreach property/title on the object 230 | for (NSString *title in objectWithTitles) { 231 | // make the key - lowercase, because we can't be sure they'll all come in the same string case over the pipeline 232 | NSString *key = [title lowercaseString]; 233 | 234 | // find the column we've added alrady 235 | NSTableColumn *column = [self.tableView tableColumnWithIdentifier:key]; 236 | 237 | if (column == nil) { 238 | // if no column, add one 239 | NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:key]; 240 | column.title = title; 241 | 242 | NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:column.identifier ascending:YES selector:@selector(compare:)]; 243 | [column setSortDescriptorPrototype:sortDescriptor]; 244 | 245 | 246 | NSMutableDictionary *bindingOptions = [NSMutableDictionary dictionary]; 247 | [bindingOptions setObject:@"" 248 | forKey:NSNullPlaceholderBindingOption]; 249 | 250 | NSString *keyPath = [NSString stringWithFormat:@"arrangedObjects.%@",key]; 251 | [column bind:NSValueBinding 252 | toObject:self.objects 253 | withKeyPath:keyPath 254 | options:bindingOptions]; 255 | 256 | [self.tableView addTableColumn:column]; 257 | } 258 | 259 | // find the class for this property 260 | Class klass = [keyClasses objectForKey:key]; 261 | if (klass == nil) { 262 | // if we havn't seen this key before, let's see if it has a type 263 | id val = [objectWithKeys objectForKey:key]; 264 | 265 | if (val != (id)[NSNull null]) { 266 | // it does have a type, add that to the cache so we can use it on display later 267 | klass = [val class]; // val will have a class, because its not null per above 268 | [keyClasses setObject:klass forKey:key]; 269 | } 270 | } 271 | 272 | } 273 | 274 | [objectWithKeys setValue:[[NSNumber alloc] initWithInt:linesRead++] forKey:@"__gridviewer_psobject_index"]; 275 | 276 | // add the object (with lowercase keys) to the controller 277 | [self.objects addObject:objectWithKeys]; 278 | self.totalCountStringValue = [NSString stringWithFormat:@"%ld",[self.objects.arrangedObjects count]]; 279 | 280 | [self updateInfoLabels]; 281 | } 282 | } 283 | 284 | 285 | 286 | - (void)updateInfoLabels { 287 | self.selectedCountStringValue = [NSString stringWithFormat:@"%ld",[self.tableView selectedRowIndexes].count]; 288 | 289 | self.filteredCountStringValue = [NSString stringWithFormat:@"%ld",[self.objects.arrangedObjects count]]; 290 | } 291 | 292 | - (void)tableViewSelectionDidChange:(NSNotification *)notification 293 | { 294 | [self updateInfoLabels]; 295 | } 296 | 297 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { 298 | return [self.objects.arrangedObjects count]; 299 | } 300 | 301 | - (void)tableView:(NSTableView *)tableView sortDescriptorsDidChange:(NSArray *)sortDescriptors 302 | { 303 | [self.objects setSortDescriptors:sortDescriptors]; 304 | [tableView reloadData]; 305 | } 306 | 307 | - (NSView *)tableView:(NSTableView *)tableView 308 | viewForTableColumn:(NSTableColumn *)tableColumn 309 | row:(NSInteger)row { 310 | id originalValue = [[self.objects.arrangedObjects objectAtIndex:row] valueForKey:tableColumn.identifier]; 311 | NSString *newValue; 312 | 313 | if (originalValue == (id)[NSNull null] || originalValue == nil) { 314 | // if the original value is null, return an empty string for display 315 | newValue = @""; 316 | } else { 317 | // find the best way to display the value 318 | Class klass = [keyClasses objectForKey:tableColumn.identifier]; 319 | 320 | if ([klass isSubclassOfClass:[NSString class]]) { 321 | //if a string, display that 322 | newValue = originalValue; 323 | } else if ([klass isSubclassOfClass:[NSNumber class]]) { 324 | // if a number, format and display that 325 | NSString *numberStr = [NSNumberFormatter localizedStringFromNumber:originalValue numberStyle:NSNumberFormatterDecimalStyle]; 326 | newValue = numberStr; 327 | } else { 328 | // ? 329 | newValue = @""; 330 | } 331 | } 332 | NSTableCellView *cellView = [tableView makeViewWithIdentifier:@"DefaultCellView" owner:self]; 333 | // [cellView.textField bind:NSValueBinding toObject:] 334 | cellView.textField.stringValue = newValue; 335 | 336 | return cellView; 337 | } 338 | 339 | 340 | @end 341 | 342 | 343 | -------------------------------------------------------------------------------- /src/GridViewer/GVWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GVWindowController.h 3 | // GridViewer 4 | // 5 | // Created by Charlie Schmidt on 1/23/19. 6 | // Copyright © 2019 Charlie Schmidt. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface GVWindowController : NSWindowController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /src/GridViewer/GVWindowController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GVWindowController.m 3 | // GridViewer 4 | // 5 | // Created by Charlie Schmidt on 1/23/19. 6 | // Copyright © 2019 Charlie Schmidt. All rights reserved. 7 | // 8 | 9 | #import "GVWindowController.h" 10 | #import "GVApplicationDelegate.h" 11 | 12 | @interface GVWindowController () 13 | 14 | @end 15 | 16 | @implementation GVWindowController 17 | 18 | - (void)windowDidLoad { 19 | [super windowDidLoad]; 20 | 21 | // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /src/GridViewer/GridIcon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlieschmidt/PSmacOS/09f3b8d0af3eb1239ceee5208372a358f5334181/src/GridViewer/GridIcon.icns -------------------------------------------------------------------------------- /src/GridViewer/GridViewer.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | CB213ECA2175AF50002020E7 /* ps_black_64.svg in Resources */ = {isa = PBXBuildFile; fileRef = CB213EC92175AF4F002020E7 /* ps_black_64.svg */; }; 11 | CB78E65121F951F000CF99D0 /* GVWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = CB78E65021F951F000CF99D0 /* GVWindowController.m */; }; 12 | CBD2B96A2161E7790012F9B6 /* GVApplicationDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CBD2B9692161E7790012F9B6 /* GVApplicationDelegate.m */; }; 13 | CBD2B96C2161E77B0012F9B6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CBD2B96B2161E77B0012F9B6 /* Assets.xcassets */; }; 14 | CBD2B9722161E77B0012F9B6 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = CBD2B9712161E77B0012F9B6 /* main.m */; }; 15 | CBD2B9832161FAD60012F9B6 /* GVObjectTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CBD2B9812161FAD60012F9B6 /* GVObjectTableViewController.m */; }; 16 | CBD2B98A21630BB40012F9B6 /* Storyboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CBD2B98921630BB40012F9B6 /* Storyboard.storyboard */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | CB213EC92175AF4F002020E7 /* ps_black_64.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = ps_black_64.svg; sourceTree = ""; }; 21 | CB213ECB2175B083002020E7 /* GridIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = GridIcon.icns; path = /Users/charlie/Projects/PSmacOS/src/GridViewer/GridIcon.icns; sourceTree = ""; }; 22 | CB78E64F21F951F000CF99D0 /* GVWindowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GVWindowController.h; sourceTree = ""; }; 23 | CB78E65021F951F000CF99D0 /* GVWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GVWindowController.m; sourceTree = ""; }; 24 | CBD2B9652161E7790012F9B6 /* GridViewer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GridViewer.app; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | CBD2B9682161E7790012F9B6 /* GVApplicationDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GVApplicationDelegate.h; sourceTree = ""; }; 26 | CBD2B9692161E7790012F9B6 /* GVApplicationDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GVApplicationDelegate.m; sourceTree = ""; }; 27 | CBD2B96B2161E77B0012F9B6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 28 | CBD2B9702161E77B0012F9B6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 29 | CBD2B9712161E77B0012F9B6 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 30 | CBD2B9802161FAD60012F9B6 /* GVObjectTableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GVObjectTableViewController.h; sourceTree = ""; }; 31 | CBD2B9812161FAD60012F9B6 /* GVObjectTableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GVObjectTableViewController.m; sourceTree = ""; }; 32 | CBD2B98921630BB40012F9B6 /* Storyboard.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Storyboard.storyboard; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | CBD2B9622161E7790012F9B6 /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | CBD2B95C2161E7790012F9B6 = { 47 | isa = PBXGroup; 48 | children = ( 49 | CBD2B96B2161E77B0012F9B6 /* Assets.xcassets */, 50 | CB213ECB2175B083002020E7 /* GridIcon.icns */, 51 | CBD2B9682161E7790012F9B6 /* GVApplicationDelegate.h */, 52 | CBD2B9692161E7790012F9B6 /* GVApplicationDelegate.m */, 53 | CBD2B9802161FAD60012F9B6 /* GVObjectTableViewController.h */, 54 | CBD2B9812161FAD60012F9B6 /* GVObjectTableViewController.m */, 55 | CB78E64F21F951F000CF99D0 /* GVWindowController.h */, 56 | CB78E65021F951F000CF99D0 /* GVWindowController.m */, 57 | CBD2B9702161E77B0012F9B6 /* Info.plist */, 58 | CBD2B9712161E77B0012F9B6 /* main.m */, 59 | CBD2B9662161E7790012F9B6 /* Products */, 60 | CB213EC92175AF4F002020E7 /* ps_black_64.svg */, 61 | CBD2B98921630BB40012F9B6 /* Storyboard.storyboard */, 62 | ); 63 | sourceTree = ""; 64 | }; 65 | CBD2B9662161E7790012F9B6 /* Products */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | CBD2B9652161E7790012F9B6 /* GridViewer.app */, 69 | ); 70 | name = Products; 71 | sourceTree = ""; 72 | }; 73 | /* End PBXGroup section */ 74 | 75 | /* Begin PBXNativeTarget section */ 76 | CBD2B9642161E7790012F9B6 /* GridViewer */ = { 77 | isa = PBXNativeTarget; 78 | buildConfigurationList = CBD2B9762161E77B0012F9B6 /* Build configuration list for PBXNativeTarget "GridViewer" */; 79 | buildPhases = ( 80 | CBD2B9612161E7790012F9B6 /* Sources */, 81 | CBD2B9622161E7790012F9B6 /* Frameworks */, 82 | CBD2B9632161E7790012F9B6 /* Resources */, 83 | ); 84 | buildRules = ( 85 | ); 86 | dependencies = ( 87 | ); 88 | name = GridViewer; 89 | productName = GridViewer; 90 | productReference = CBD2B9652161E7790012F9B6 /* GridViewer.app */; 91 | productType = "com.apple.product-type.application"; 92 | }; 93 | /* End PBXNativeTarget section */ 94 | 95 | /* Begin PBXProject section */ 96 | CBD2B95D2161E7790012F9B6 /* Project object */ = { 97 | isa = PBXProject; 98 | attributes = { 99 | LastUpgradeCheck = 1000; 100 | ORGANIZATIONNAME = "Charlie Schmidt"; 101 | TargetAttributes = { 102 | CBD2B9642161E7790012F9B6 = { 103 | CreatedOnToolsVersion = 10.0; 104 | }; 105 | }; 106 | }; 107 | buildConfigurationList = CBD2B9602161E7790012F9B6 /* Build configuration list for PBXProject "GridViewer" */; 108 | compatibilityVersion = "Xcode 9.3"; 109 | developmentRegion = en; 110 | hasScannedForEncodings = 0; 111 | knownRegions = ( 112 | en, 113 | Base, 114 | ); 115 | mainGroup = CBD2B95C2161E7790012F9B6; 116 | productRefGroup = CBD2B9662161E7790012F9B6 /* Products */; 117 | projectDirPath = ""; 118 | projectRoot = ""; 119 | targets = ( 120 | CBD2B9642161E7790012F9B6 /* GridViewer */, 121 | ); 122 | }; 123 | /* End PBXProject section */ 124 | 125 | /* Begin PBXResourcesBuildPhase section */ 126 | CBD2B9632161E7790012F9B6 /* Resources */ = { 127 | isa = PBXResourcesBuildPhase; 128 | buildActionMask = 2147483647; 129 | files = ( 130 | CBD2B98A21630BB40012F9B6 /* Storyboard.storyboard in Resources */, 131 | CB213ECA2175AF50002020E7 /* ps_black_64.svg in Resources */, 132 | CBD2B96C2161E77B0012F9B6 /* Assets.xcassets in Resources */, 133 | ); 134 | runOnlyForDeploymentPostprocessing = 0; 135 | }; 136 | /* End PBXResourcesBuildPhase section */ 137 | 138 | /* Begin PBXSourcesBuildPhase section */ 139 | CBD2B9612161E7790012F9B6 /* Sources */ = { 140 | isa = PBXSourcesBuildPhase; 141 | buildActionMask = 2147483647; 142 | files = ( 143 | CB78E65121F951F000CF99D0 /* GVWindowController.m in Sources */, 144 | CBD2B9722161E77B0012F9B6 /* main.m in Sources */, 145 | CBD2B9832161FAD60012F9B6 /* GVObjectTableViewController.m in Sources */, 146 | CBD2B96A2161E7790012F9B6 /* GVApplicationDelegate.m in Sources */, 147 | ); 148 | runOnlyForDeploymentPostprocessing = 0; 149 | }; 150 | /* End PBXSourcesBuildPhase section */ 151 | 152 | /* Begin XCBuildConfiguration section */ 153 | CBD2B9742161E77B0012F9B6 /* Debug */ = { 154 | isa = XCBuildConfiguration; 155 | buildSettings = { 156 | ALWAYS_SEARCH_USER_PATHS = NO; 157 | CLANG_ANALYZER_NONNULL = YES; 158 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 159 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 160 | CLANG_CXX_LIBRARY = "libc++"; 161 | CLANG_ENABLE_MODULES = YES; 162 | CLANG_ENABLE_OBJC_ARC = YES; 163 | CLANG_ENABLE_OBJC_WEAK = YES; 164 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 165 | CLANG_WARN_BOOL_CONVERSION = YES; 166 | CLANG_WARN_COMMA = YES; 167 | CLANG_WARN_CONSTANT_CONVERSION = YES; 168 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 169 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 170 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 171 | CLANG_WARN_EMPTY_BODY = YES; 172 | CLANG_WARN_ENUM_CONVERSION = YES; 173 | CLANG_WARN_INFINITE_RECURSION = YES; 174 | CLANG_WARN_INT_CONVERSION = YES; 175 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 176 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 177 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 178 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 179 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 180 | CLANG_WARN_STRICT_PROTOTYPES = YES; 181 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 182 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 183 | CLANG_WARN_UNREACHABLE_CODE = YES; 184 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 185 | CODE_SIGN_IDENTITY = "Mac Developer"; 186 | COPY_PHASE_STRIP = NO; 187 | DEBUG_INFORMATION_FORMAT = dwarf; 188 | ENABLE_STRICT_OBJC_MSGSEND = YES; 189 | ENABLE_TESTABILITY = YES; 190 | GCC_C_LANGUAGE_STANDARD = gnu11; 191 | GCC_DYNAMIC_NO_PIC = NO; 192 | GCC_NO_COMMON_BLOCKS = YES; 193 | GCC_OPTIMIZATION_LEVEL = 0; 194 | GCC_PREPROCESSOR_DEFINITIONS = ( 195 | "DEBUG=1", 196 | "$(inherited)", 197 | ); 198 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 199 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 200 | GCC_WARN_UNDECLARED_SELECTOR = YES; 201 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 202 | GCC_WARN_UNUSED_FUNCTION = YES; 203 | GCC_WARN_UNUSED_VARIABLE = YES; 204 | MACOSX_DEPLOYMENT_TARGET = 10.12; 205 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 206 | MTL_FAST_MATH = YES; 207 | ONLY_ACTIVE_ARCH = YES; 208 | SDKROOT = macosx; 209 | }; 210 | name = Debug; 211 | }; 212 | CBD2B9752161E77B0012F9B6 /* Release */ = { 213 | isa = XCBuildConfiguration; 214 | buildSettings = { 215 | ALWAYS_SEARCH_USER_PATHS = NO; 216 | CLANG_ANALYZER_NONNULL = YES; 217 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 218 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 219 | CLANG_CXX_LIBRARY = "libc++"; 220 | CLANG_ENABLE_MODULES = YES; 221 | CLANG_ENABLE_OBJC_ARC = YES; 222 | CLANG_ENABLE_OBJC_WEAK = YES; 223 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 224 | CLANG_WARN_BOOL_CONVERSION = YES; 225 | CLANG_WARN_COMMA = YES; 226 | CLANG_WARN_CONSTANT_CONVERSION = YES; 227 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 228 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 229 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 230 | CLANG_WARN_EMPTY_BODY = YES; 231 | CLANG_WARN_ENUM_CONVERSION = YES; 232 | CLANG_WARN_INFINITE_RECURSION = YES; 233 | CLANG_WARN_INT_CONVERSION = YES; 234 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 235 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 236 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 237 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 238 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 239 | CLANG_WARN_STRICT_PROTOTYPES = YES; 240 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 241 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 242 | CLANG_WARN_UNREACHABLE_CODE = YES; 243 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 244 | CODE_SIGN_IDENTITY = "Mac Developer"; 245 | COPY_PHASE_STRIP = NO; 246 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 247 | ENABLE_NS_ASSERTIONS = NO; 248 | ENABLE_STRICT_OBJC_MSGSEND = YES; 249 | GCC_C_LANGUAGE_STANDARD = gnu11; 250 | GCC_NO_COMMON_BLOCKS = YES; 251 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 252 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 253 | GCC_WARN_UNDECLARED_SELECTOR = YES; 254 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 255 | GCC_WARN_UNUSED_FUNCTION = YES; 256 | GCC_WARN_UNUSED_VARIABLE = YES; 257 | MACOSX_DEPLOYMENT_TARGET = 10.12; 258 | MTL_ENABLE_DEBUG_INFO = NO; 259 | MTL_FAST_MATH = YES; 260 | SDKROOT = macosx; 261 | }; 262 | name = Release; 263 | }; 264 | CBD2B9772161E77B0012F9B6 /* Debug */ = { 265 | isa = XCBuildConfiguration; 266 | buildSettings = { 267 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 268 | CODE_SIGN_IDENTITY = "-"; 269 | CODE_SIGN_STYLE = Automatic; 270 | COMBINE_HIDPI_IMAGES = YES; 271 | DEVELOPMENT_TEAM = 359Z4D5QD2; 272 | INFOPLIST_FILE = Info.plist; 273 | LD_RUNPATH_SEARCH_PATHS = ( 274 | "$(inherited)", 275 | "@executable_path/../Frameworks", 276 | ); 277 | MACOSX_DEPLOYMENT_TARGET = 10.12; 278 | PRODUCT_BUNDLE_IDENTIFIER = com.charlieschmidt.GridViewer; 279 | PRODUCT_NAME = "$(TARGET_NAME)"; 280 | }; 281 | name = Debug; 282 | }; 283 | CBD2B9782161E77B0012F9B6 /* Release */ = { 284 | isa = XCBuildConfiguration; 285 | buildSettings = { 286 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 287 | CODE_SIGN_IDENTITY = "-"; 288 | CODE_SIGN_STYLE = Automatic; 289 | COMBINE_HIDPI_IMAGES = YES; 290 | DEVELOPMENT_TEAM = 359Z4D5QD2; 291 | INFOPLIST_FILE = Info.plist; 292 | LD_RUNPATH_SEARCH_PATHS = ( 293 | "$(inherited)", 294 | "@executable_path/../Frameworks", 295 | ); 296 | PRODUCT_BUNDLE_IDENTIFIER = com.charlieschmidt.GridViewer; 297 | PRODUCT_NAME = "$(TARGET_NAME)"; 298 | }; 299 | name = Release; 300 | }; 301 | /* End XCBuildConfiguration section */ 302 | 303 | /* Begin XCConfigurationList section */ 304 | CBD2B9602161E7790012F9B6 /* Build configuration list for PBXProject "GridViewer" */ = { 305 | isa = XCConfigurationList; 306 | buildConfigurations = ( 307 | CBD2B9742161E77B0012F9B6 /* Debug */, 308 | CBD2B9752161E77B0012F9B6 /* Release */, 309 | ); 310 | defaultConfigurationIsVisible = 0; 311 | defaultConfigurationName = Release; 312 | }; 313 | CBD2B9762161E77B0012F9B6 /* Build configuration list for PBXNativeTarget "GridViewer" */ = { 314 | isa = XCConfigurationList; 315 | buildConfigurations = ( 316 | CBD2B9772161E77B0012F9B6 /* Debug */, 317 | CBD2B9782161E77B0012F9B6 /* Release */, 318 | ); 319 | defaultConfigurationIsVisible = 0; 320 | defaultConfigurationName = Release; 321 | }; 322 | /* End XCConfigurationList section */ 323 | }; 324 | rootObject = CBD2B95D2161E7790012F9B6 /* Project object */; 325 | } 326 | -------------------------------------------------------------------------------- /src/GridViewer/GridViewer.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/GridViewer/GridViewer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/GridViewer/GridViewer.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/GridViewer/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | GridIcon.icns 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2018 Charlie Schmidt. All rights reserved. 27 | NSMainStoryboardFile 28 | Storyboard 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/GridViewer/Storyboard.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 784 | 788 | 789 | 790 | 791 | 792 | 793 | 807 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | -------------------------------------------------------------------------------- /src/GridViewer/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // GridViewer 4 | // 5 | // Created by Charlie Schmidt on 10/1/18. 6 | // Copyright © 2018 Charlie Schmidt. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | #import "GVApplicationDelegate.h" 13 | 14 | int main(int argc, const char * argv[]) { 15 | GVApplicationDelegate *del = [[GVApplicationDelegate alloc] init]; 16 | 17 | [NSApplication sharedApplication]; 18 | [NSApp setDelegate:del]; 19 | /* 20 | NSFileHandle *input = [NSFileHandle fileHandleWithStandardInput]; 21 | NSData *inputData = [NSData dataWithData:[input availableData]]; 22 | NSString *inputString = [[NSString alloc] 23 | initWithData:inputData encoding:NSUTF8StringEncoding]; 24 | 25 | NSArray *lines = [inputString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\r\n"]]; 26 | 27 | for (NSString *line in lines) { 28 | NSLog(@"got line: '%@'",line); 29 | } 30 | NSLog(@"and still starting app"); 31 | */ 32 | return NSApplicationMain(argc, argv); 33 | } 34 | -------------------------------------------------------------------------------- /src/GridViewer/ps_black_64.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/MarshalExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace PSmacOS 7 | { 8 | public static class MarshalExtensions 9 | { 10 | public static IntPtr NativeUtf8FromString(string managedString) 11 | { 12 | if (managedString != null) 13 | { 14 | int len = Encoding.UTF8.GetByteCount(managedString); 15 | byte[] buffer = new byte[len + 1]; 16 | Encoding.UTF8.GetBytes(managedString, 0, managedString.Length, buffer, 0); 17 | IntPtr nativeUtf8 = Marshal.AllocHGlobal(buffer.Length); 18 | Marshal.Copy(buffer, 0, nativeUtf8, buffer.Length); 19 | return nativeUtf8; 20 | } else { 21 | return IntPtr.Zero; 22 | } 23 | } 24 | 25 | public static string StringFromNativeUtf8(IntPtr nativeUtf8) 26 | { 27 | if (nativeUtf8 != IntPtr.Zero) 28 | { 29 | int len = 0; 30 | while (Marshal.ReadByte(nativeUtf8, len) != 0) ++len; 31 | byte[] buffer = new byte[len]; 32 | Marshal.Copy(nativeUtf8, buffer, 0, buffer.Length); 33 | return Encoding.UTF8.GetString(buffer); 34 | } else { 35 | return null; 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/NativeBridge/Clipboard.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Reflection; 7 | using System.Diagnostics; 8 | using System.IO; 9 | using System.Collections; 10 | using System.Collections.Generic; 11 | 12 | namespace PSmacOS.NativeBridge 13 | { 14 | public class Clipboard 15 | { 16 | [DllImport("./libpsmacosbridging")] 17 | internal extern static IntPtr getClipboard(); 18 | 19 | [DllImport("./libpsmacosbridging")] 20 | internal extern static bool setClipboard(IntPtr clipboardManagedString); 21 | 22 | [DllImport("./libpsmacosbridging")] 23 | internal extern static void freeString(IntPtr managedString); 24 | 25 | public static string Get() 26 | { 27 | IntPtr clipboardManagedString = getClipboard(); 28 | var clipboardString = MarshalExtensions.StringFromNativeUtf8(clipboardManagedString); 29 | freeString(clipboardManagedString); 30 | return clipboardString; 31 | } 32 | 33 | public static bool Set(string value) 34 | { 35 | var clipboardManagedString = MarshalExtensions.NativeUtf8FromString(value); 36 | var ret = setClipboard(clipboardManagedString); 37 | return ret; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/NativeBridge/GridView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Reflection; 7 | using System.Diagnostics; 8 | using System.IO; 9 | using System.Collections; 10 | using System.Collections.Generic; 11 | using PSmacOS.Cmdlets; 12 | 13 | namespace PSmacOS.NativeBridge 14 | { 15 | public class GridView 16 | { 17 | private static Process _gridViewerProcess = null; 18 | 19 | private static List objects = new List(); 20 | 21 | private static List selectedIndexes = new List(); 22 | 23 | public static void Start(string title, OutputModeOption outputMode, PSCmdlet cmdlet) 24 | { 25 | _gridViewerProcess = new Process(); 26 | 27 | _gridViewerProcess.StartInfo.FileName = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Contents", "MacOS", "GridViewer"); 28 | _gridViewerProcess.StartInfo.Arguments = string.Format("\"{0}\"", title.Replace("\"", "\\\"")); 29 | 30 | _gridViewerProcess.StartInfo.CreateNoWindow = true; 31 | _gridViewerProcess.StartInfo.UseShellExecute = false; 32 | 33 | _gridViewerProcess.StartInfo.RedirectStandardInput = true; 34 | _gridViewerProcess.StartInfo.RedirectStandardOutput = true; 35 | _gridViewerProcess.StartInfo.RedirectStandardError = true; 36 | 37 | // Just Console.WriteLine it. 38 | _gridViewerProcess.OutputDataReceived += (sender, data) => 39 | { 40 | if (!string.IsNullOrWhiteSpace(data.Data)) 41 | { 42 | selectedIndexes.Add(Convert.ToInt32(data.Data)); 43 | } 44 | }; 45 | _gridViewerProcess.ErrorDataReceived += (sender, data) => 46 | { 47 | Console.WriteLine(data.Data); 48 | }; 49 | 50 | try 51 | { 52 | _gridViewerProcess.Start(); 53 | _gridViewerProcess.BeginOutputReadLine(); 54 | _gridViewerProcess.BeginErrorReadLine(); 55 | } 56 | catch (Exception ex) 57 | { 58 | cmdlet.ThrowTerminatingError(new ErrorRecord(ex, "GridViewException", ErrorCategory.OperationStopped, null)); 59 | } 60 | } 61 | 62 | public static void AddRecord(PSObject obj) 63 | { 64 | objects.Add(obj); 65 | 66 | var recordJson = PSObjectHelper.ToJson(obj); 67 | _gridViewerProcess.StandardInput.WriteLine(recordJson); 68 | } 69 | 70 | public static List GetSelectedItems() { 71 | var selectedObjects = new List(); 72 | foreach (var index in selectedIndexes) { 73 | selectedObjects.Add(objects[index]); 74 | } 75 | return selectedObjects; 76 | } 77 | 78 | public static void Close() 79 | { 80 | _gridViewerProcess.Close(); 81 | } 82 | 83 | public static bool IsClosed() 84 | { 85 | if (_gridViewerProcess == null || _gridViewerProcess.HasExited) 86 | { 87 | return true; 88 | } 89 | else { 90 | return false; 91 | } 92 | } 93 | 94 | public static void WaitForExit() 95 | { 96 | _gridViewerProcess.WaitForExit(); 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /src/NativeBridge/MessageBox.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Reflection; 7 | using System.Diagnostics; 8 | using System.IO; 9 | using System.Collections; 10 | using System.Collections.Generic; 11 | 12 | namespace PSmacOS.NativeBridge 13 | { 14 | 15 | 16 | public class MessageBox 17 | { 18 | public enum Type 19 | { 20 | Stop = 0, 21 | Note = 1, 22 | Caution = 2, 23 | Plain = 3 24 | } 25 | 26 | [DllImport("./libpsmacosbridging")] 27 | internal extern static ulong showMessageBox(double timeoutSeconds, ulong type, IntPtr title, IntPtr message, IntPtr buttonOneLabel, IntPtr buttonTwoLabel, IntPtr buttonThreeLabel); 28 | 29 | public static ulong Show(double timeoutSeconds, MessageBox.Type type, string title, string message, string buttonOneLabel, string buttonTwoLabel, string buttonThreeLabel) 30 | { 31 | var titleManagedString = MarshalExtensions.NativeUtf8FromString(title); 32 | var messageManagedString = MarshalExtensions.NativeUtf8FromString(message); 33 | var buttonOneLabelManagedString = MarshalExtensions.NativeUtf8FromString(buttonOneLabel); 34 | var buttonTwoLabelManagedString = MarshalExtensions.NativeUtf8FromString(buttonTwoLabel); 35 | var buttonThreeLabelManagedString = MarshalExtensions.NativeUtf8FromString(buttonThreeLabel); 36 | 37 | ulong response = showMessageBox(timeoutSeconds, Convert.ToUInt64(type), titleManagedString, messageManagedString, buttonOneLabelManagedString, buttonTwoLabelManagedString, buttonThreeLabelManagedString); 38 | 39 | return response; 40 | } 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /src/PSObjectHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Collections.Generic; 5 | using System.Management.Automation; 6 | using System.Collections; 7 | using System.Reflection; 8 | using System.Text; 9 | using System.Globalization; 10 | using System.Management.Automation.Internal; 11 | using Newtonsoft.Json; 12 | using Newtonsoft.Json.Converters; 13 | 14 | namespace PSmacOS 15 | { 16 | public static class PSObjectHelper 17 | { 18 | /// 19 | /// Exception used for Stopping. 20 | /// 21 | private class StoppingException : System.Exception { } 22 | 23 | public static string ToJson(PSObject obj) 24 | { 25 | 26 | object preprocessedObject = null; 27 | try 28 | { 29 | preprocessedObject = ProcessValue(obj, 0); 30 | } 31 | catch (StoppingException) 32 | { 33 | return null; 34 | } 35 | JsonSerializerSettings jsonSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.None, MaxDepth = 1 }; 36 | jsonSettings.Converters.Add(new StringEnumConverter()); 37 | 38 | string output = JsonConvert.SerializeObject(preprocessedObject, jsonSettings); 39 | return output; 40 | } 41 | 42 | /// 43 | /// Return an alternate representation of the specified object that serializes the same JSON, except 44 | /// that properties that cannot be evaluated are treated as having the value null. 45 | /// Primitive types are returned verbatim. Aggregate types are processed recursively. 46 | /// 47 | /// The object to be processed 48 | /// The current depth into the object graph 49 | /// An object suitable for serializing to JSON 50 | private static object ProcessValue(object obj, int depth) 51 | { 52 | PSObject pso = obj as PSObject; 53 | 54 | if (pso != null) 55 | obj = pso.BaseObject; 56 | 57 | Object rv = obj; 58 | bool isPurePSObj = false; 59 | bool isCustomObj = false; 60 | 61 | if (obj == null 62 | || DBNull.Value.Equals(obj) 63 | || obj is string 64 | || obj is char 65 | || obj is bool 66 | || obj is DateTime 67 | || obj is DateTimeOffset 68 | || obj is Guid 69 | || obj is Uri 70 | || obj is double 71 | || obj is float 72 | || obj is decimal) 73 | { 74 | rv = obj; 75 | } 76 | else if (obj is Newtonsoft.Json.Linq.JObject jObject) 77 | { 78 | rv = jObject.ToObject>(); 79 | } 80 | else 81 | { 82 | TypeInfo t = obj.GetType().GetTypeInfo(); 83 | 84 | if (t.IsPrimitive) 85 | { 86 | rv = obj; 87 | } 88 | else if (t.IsEnum) 89 | { 90 | // Win8:378368 Enums based on System.Int64 or System.UInt64 are not JSON-serializable 91 | // because JavaScript does not support the necessary precision. 92 | Type enumUnderlyingType = Enum.GetUnderlyingType(obj.GetType()); 93 | if (enumUnderlyingType.Equals(typeof(Int64)) || enumUnderlyingType.Equals(typeof(UInt64))) 94 | { 95 | rv = obj.ToString(); 96 | } 97 | else 98 | { 99 | rv = obj; 100 | } 101 | } 102 | else 103 | { 104 | if (depth > 0) 105 | { 106 | if (pso != null) //&& pso.immediateBaseObjectIsEmpty) 107 | { 108 | // The obj is a pure PSObject, we convert the original PSObject to a string, 109 | // instead of its base object in this case 110 | rv = LanguagePrimitives.ConvertTo(pso, typeof(string), 111 | CultureInfo.InvariantCulture); 112 | isPurePSObj = true; 113 | } 114 | else 115 | { 116 | rv = LanguagePrimitives.ConvertTo(obj, typeof(String), 117 | CultureInfo.InvariantCulture); 118 | } 119 | } 120 | else 121 | { 122 | IDictionary dict = obj as IDictionary; 123 | if (dict != null) 124 | { 125 | rv = ProcessDictionary(dict, depth); 126 | } 127 | else 128 | { 129 | IEnumerable enumerable = obj as IEnumerable; 130 | if (enumerable != null) 131 | { 132 | rv = ProcessEnumerable(enumerable, depth); 133 | } 134 | else 135 | { 136 | rv = ProcessCustomObject(obj, depth); 137 | isCustomObj = true; 138 | } 139 | } 140 | } 141 | } 142 | } 143 | 144 | rv = AddPsProperties(pso, rv, depth, isPurePSObj, isCustomObj); 145 | 146 | return rv; 147 | } 148 | 149 | /// 150 | /// Add to a base object any properties that might have been added to an object (via PSObject) through the Add-Member cmdlet. 151 | /// 152 | /// The containing PSObject, or null if the base object was not contained in a PSObject 153 | /// The base object that might have been decorated with additional properties 154 | /// The current depth into the object graph 155 | /// the processed object is a pure PSObject 156 | /// the processed object is a custom object 157 | /// 158 | /// The original base object if no additional properties had been added, 159 | /// otherwise a dictionary containing the value of the original base object in the "value" key 160 | /// as well as the names and values of an additional properties. 161 | /// 162 | private static object AddPsProperties(object psobj, object obj, int depth, bool isPurePSObj, bool isCustomObj) 163 | { 164 | PSObject pso = psobj as PSObject; 165 | 166 | if (pso == null) 167 | return obj; 168 | 169 | // when isPurePSObj is true, the obj is guaranteed to be a string converted by LanguagePrimitives 170 | if (isPurePSObj) 171 | return obj; 172 | 173 | bool wasDictionary = true; 174 | IDictionary dict = obj as IDictionary; 175 | 176 | if (dict == null) 177 | { 178 | wasDictionary = false; 179 | dict = new Dictionary(); 180 | dict.Add("value", obj); 181 | } 182 | 183 | AppendPsProperties(pso, dict, depth, isCustomObj); 184 | 185 | if (wasDictionary == false && dict.Count == 1) 186 | return obj; 187 | 188 | return dict; 189 | } 190 | 191 | /// 192 | /// Append to a dictionary any properties that might have been added to an object (via PSObject) through the Add-Member cmdlet. 193 | /// If the passed in object is a custom object (not a simple object, not a dictionary, not a list, get processed in ProcessCustomObject method), 194 | /// we also take Adapted properties into account. Otherwise, we only consider the Extended properties. 195 | /// When the object is a pure PSObject, it also gets processed in "ProcessCustomObject" before reaching this method, so we will 196 | /// iterate both extended and adapted properties for it. Since it's a pure PSObject, there will be no adapted properties. 197 | /// 198 | /// The containing PSObject, or null if the base object was not contained in a PSObject 199 | /// The dictionary to which any additional properties will be appended 200 | /// The current depth into the object graph 201 | /// The processed object is a custom object 202 | private static void AppendPsProperties(PSObject psobj, IDictionary receiver, int depth, bool isCustomObject) 203 | { 204 | // serialize only Extended and Adapted properties.. 205 | PSMemberInfoCollection srcPropertiesToSearch = psobj.Properties; 206 | 207 | foreach (PSPropertyInfo prop in srcPropertiesToSearch) 208 | { 209 | object value = null; 210 | try 211 | { 212 | value = prop.Value; 213 | } 214 | catch (Exception) 215 | { 216 | } 217 | 218 | if (!receiver.Contains(prop.Name)) 219 | { 220 | receiver[prop.Name] = ProcessValue(value, depth + 1); 221 | } 222 | } 223 | } 224 | 225 | /// 226 | /// Return an alternate representation of the specified dictionary that serializes the same JSON, except 227 | /// that any contained properties that cannot be evaluated are treated as having the value null. 228 | /// 229 | /// 230 | /// 231 | /// 232 | private static object ProcessDictionary(IDictionary dict, int depth) 233 | { 234 | Dictionary result = new Dictionary(dict.Count); 235 | 236 | foreach (DictionaryEntry entry in dict) 237 | { 238 | //Console.WriteLine($"dictionary: {entry.Key} = {entry.Value}"); 239 | string name = entry.Key as string; 240 | if (name == null) 241 | { 242 | // use the error string that matches the message from JavaScriptSerializer 243 | /* 244 | var exception = 245 | new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, 246 | WebCmdletStrings.NonStringKeyInDictionary, 247 | dict.GetType().FullName)); 248 | */ 249 | //ThrowTerminatingError(new ErrorRecord(exception, "NonStringKeyInDictionary", ErrorCategory.InvalidOperation, dict)); 250 | 251 | throw new System.Exception("nonStringKeyInDictionary"); 252 | } 253 | 254 | result.Add(name, ProcessValue(entry.Value, depth + 1)); 255 | } 256 | 257 | return result; 258 | } 259 | 260 | /// 261 | /// Return an alternate representation of the specified collection that serializes the same JSON, except 262 | /// that any contained properties that cannot be evaluated are treated as having the value null. 263 | /// 264 | /// 265 | /// 266 | /// 267 | private static object ProcessEnumerable(IEnumerable enumerable, int depth) 268 | { 269 | List result = new List(); 270 | 271 | foreach (object o in enumerable) 272 | { 273 | result.Add(ProcessValue(o, depth + 1)); 274 | } 275 | 276 | return result; 277 | } 278 | 279 | /// 280 | /// Return an alternate representation of the specified aggregate object that serializes the same JSON, except 281 | /// that any contained properties that cannot be evaluated are treated as having the value null. 282 | /// 283 | /// The result is a dictionary in which all public fields and public gettable properties of the original object 284 | /// are represented. If any exception occurs while retrieving the value of a field or property, that entity 285 | /// is included in the output dictionary with a value of null. 286 | /// 287 | /// 288 | /// 289 | /// 290 | private static object ProcessCustomObject(object o, int depth) 291 | { 292 | Dictionary result = new Dictionary(); 293 | Type t = o.GetType(); 294 | 295 | foreach (FieldInfo info in t.GetFields(BindingFlags.Public | BindingFlags.Instance)) 296 | { 297 | if (!info.IsDefined(typeof(T), true)) 298 | { 299 | object value; 300 | try 301 | { 302 | value = info.GetValue(o); 303 | } 304 | catch (Exception) 305 | { 306 | value = null; 307 | } 308 | 309 | result.Add(info.Name, ProcessValue(value, depth + 1)); 310 | } 311 | } 312 | 313 | foreach (PropertyInfo info2 in t.GetProperties(BindingFlags.Public | BindingFlags.Instance)) 314 | { 315 | if (!info2.IsDefined(typeof(T), true)) 316 | { 317 | MethodInfo getMethod = info2.GetGetMethod(); 318 | if ((getMethod != null) && (getMethod.GetParameters().Length <= 0)) 319 | { 320 | object value; 321 | try 322 | { 323 | value = getMethod.Invoke(o, new object[0]); 324 | } 325 | catch (Exception) 326 | { 327 | value = null; 328 | } 329 | 330 | result.Add(info2.Name, ProcessValue(value, depth + 1)); 331 | } 332 | } 333 | } 334 | return result; 335 | } 336 | 337 | private static readonly PSObject s_emptyPSObject = new PSObject(string.Empty); 338 | 339 | internal static PSObject AsPSObject(object obj) 340 | { 341 | return (obj == null) ? s_emptyPSObject : PSObject.AsPSObject(obj); 342 | } 343 | } 344 | } -------------------------------------------------------------------------------- /src/PSmacOS.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/PSmacOS.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PSmacOS", "PSmacOS.csproj", "{F1A83A9F-C85E-44C5-90CA-EFBA99B4B585}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Debug|x86 = Debug|x86 13 | Release|Any CPU = Release|Any CPU 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {F1A83A9F-C85E-44C5-90CA-EFBA99B4B585}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {F1A83A9F-C85E-44C5-90CA-EFBA99B4B585}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {F1A83A9F-C85E-44C5-90CA-EFBA99B4B585}.Debug|x64.ActiveCfg = Debug|x64 24 | {F1A83A9F-C85E-44C5-90CA-EFBA99B4B585}.Debug|x64.Build.0 = Debug|x64 25 | {F1A83A9F-C85E-44C5-90CA-EFBA99B4B585}.Debug|x86.ActiveCfg = Debug|x86 26 | {F1A83A9F-C85E-44C5-90CA-EFBA99B4B585}.Debug|x86.Build.0 = Debug|x86 27 | {F1A83A9F-C85E-44C5-90CA-EFBA99B4B585}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {F1A83A9F-C85E-44C5-90CA-EFBA99B4B585}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {F1A83A9F-C85E-44C5-90CA-EFBA99B4B585}.Release|x64.ActiveCfg = Release|x64 30 | {F1A83A9F-C85E-44C5-90CA-EFBA99B4B585}.Release|x64.Build.0 = Release|x64 31 | {F1A83A9F-C85E-44C5-90CA-EFBA99B4B585}.Release|x86.ActiveCfg = Release|x86 32 | {F1A83A9F-C85E-44C5-90CA-EFBA99B4B585}.Release|x86.Build.0 = Release|x86 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /src/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.0" 4 | } 5 | } -------------------------------------------------------------------------------- /src/libpsmacosbridging/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # libpsmacosbridging cmake configuration file 2 | 3 | cmake_minimum_required(VERSION 2.8) 4 | set(CMAKE_MACOSX_RPATH 1) 5 | project(libpsmacosbridging) 6 | 7 | if ( ${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR} ) 8 | message( FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there. You may need to remove CMakeCache.txt." ) 9 | endif() 10 | 11 | set(GCC_COMPILE_FLAGS "-std=c99 -Wall -pedantic -g -fobjc-arc") 12 | 13 | execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion 14 | OUTPUT_VARIABLE GCC_VERSION) 15 | if (GCC_VERSION VERSION_GREATER 4.9 OR GCC_VERSION VERSION_EQUAL 4.9) 16 | set(GCC_COMPILE_FLAGS "${GCC_COMPILE_FLAGS} -fdiagnostics-color=auto") 17 | endif() 18 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GCC_COMPILE_FLAGS}") 19 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -pedantic -g") 20 | 21 | #set_source_files_properties(*.m PROPERTIES COMPILE_FLAGS "-x objective-c") 22 | 23 | set(LIBPSMACOSBRIDGING_PRIVATE_LIBS ${LIBPSMACOSBRIDGING_PRIVATE_LIBS} "-framework Cocoa") 24 | 25 | # Source files 26 | set(OBJC_HEADERS 27 | libpsmacosbridging.h 28 | ) 29 | 30 | set(OBJC_SOURCE 31 | bridge_memory.m 32 | bridge_messagebox.m 33 | bridge_pasteboard.m 34 | ) 35 | 36 | # Set the output folders 37 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 38 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 39 | 40 | # Make the library 41 | add_library(psmacosbridging SHARED ${OBJC_HEADERS} ${OBJC_SOURCE}) 42 | set_target_properties(psmacosbridging PROPERTIES SOVERSION 1) 43 | target_link_libraries(psmacosbridging LINK_PRIVATE ${LIBPSMACOSBRIDGING_PRIVATE_LIBS}) 44 | 45 | # Install options 46 | install(TARGETS psmacosbridging 47 | LIBRARY DESTINATION lib 48 | ARCHIVE DESTINATION lib) 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/libpsmacosbridging/bridge_memory.m: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void freeString(char *string) { 5 | if (string != NULL) { 6 | free(string); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/libpsmacosbridging/bridge_messagebox.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | unsigned long showMessageBox(double timeoutSeconds, unsigned long type, const char *title, const char *message, const char *buttonOneLabel, const char *buttonTwoLabel, const char *buttonThreeLabel) 6 | { 7 | CFStringRef messageStringRef = CFStringCreateWithCString(NULL, message, strlen(message)); 8 | CFStringRef titleStringRef = CFStringCreateWithCString(NULL, title, strlen(title)); 9 | 10 | CFStringRef buttonOneLabelStringRef = CFStringCreateWithCString(NULL, buttonOneLabel, strlen(buttonOneLabel)); 11 | 12 | CFStringRef buttonTwoLabelStringRef = NULL; 13 | if (buttonTwoLabel != NULL) { 14 | buttonTwoLabelStringRef = CFStringCreateWithCString(NULL, buttonTwoLabel, strlen(buttonTwoLabel)); 15 | } 16 | 17 | CFStringRef buttonThreeLabelStringRef = NULL; 18 | if (buttonThreeLabel != NULL) { 19 | buttonThreeLabelStringRef = CFStringCreateWithCString(NULL, buttonThreeLabel, strlen(buttonThreeLabel)); 20 | } 21 | 22 | CFOptionFlags responseFlags; 23 | CFUserNotificationDisplayAlert(timeoutSeconds, type, NULL, NULL, NULL, titleStringRef, messageStringRef, buttonOneLabelStringRef, buttonTwoLabelStringRef, buttonThreeLabelStringRef, &responseFlags); 24 | return responseFlags; 25 | } 26 | -------------------------------------------------------------------------------- /src/libpsmacosbridging/bridge_pasteboard.m: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | char* getClipboard() { 14 | // get reference to clipboard 15 | NSPasteboard *pb = [NSPasteboard generalPasteboard]; 16 | 17 | NSString *clipboardNSString = [pb stringForType:NSPasteboardTypeString]; 18 | if (clipboardNSString == nil) { 19 | return NULL; 20 | } 21 | 22 | const char *clipboardString; 23 | char *ret; 24 | size_t len; 25 | 26 | 27 | clipboardString = [clipboardNSString UTF8String]; 28 | len = strlen(clipboardString); 29 | ret = malloc(len + 1); 30 | if (ret != NULL) { 31 | memcpy(ret, clipboardString, len); 32 | ret[len] = '\0'; 33 | } 34 | 35 | return ret; 36 | } 37 | 38 | 39 | bool setClipboard(const char *valueString) 40 | { 41 | // get reference to clipboard 42 | NSPasteboard *pb = [NSPasteboard generalPasteboard]; 43 | 44 | // convert char* to utf'd nsstring 45 | NSString *valueNSString; 46 | valueNSString = [[NSString alloc] initWithBytes:valueString length:strlen(valueString) encoding:NSUTF8StringEncoding]; 47 | 48 | // prepare clipboard for change 49 | [pb declareTypes:[NSArray arrayWithObjects:NSPasteboardTypeString,nil] owner:nil]; 50 | 51 | // change clipboard 52 | bool ret = [pb setString:valueNSString forType:NSPasteboardTypeString]; 53 | 54 | // free resources 55 | // [valueNSString release]; 56 | 57 | return ret; 58 | } 59 | 60 | -------------------------------------------------------------------------------- /src/libpsmacosbridging/libpsmacosbridging.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _LIBPSMACOSBRIDGING_H_ 3 | #define _LIBPSMACOSBRIDGING_H_ 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | // bridge_pasteboard.m 10 | char * getClipboard(); 11 | bool setClipboard(const char *valueString); 12 | 13 | // bridge_messagebox.m 14 | unsigned long showMessageBox(double timeoutSeconds, unsigned long type, const char *title, const char *message, const char *buttonOneLabel, const char *buttonTwoLabel, const char *buttonThreeLabel); 15 | 16 | // bridge_memory.m 17 | void freeString(char *string); 18 | 19 | #ifdef __cplusplus 20 | } 21 | #endif 22 | 23 | #endif -------------------------------------------------------------------------------- /src/nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | --------------------------------------------------------------------------------