diff --git a/.github/workflows/purge-cloudflare-cache.yml b/.github/workflows/purge-cloudflare-cache.yml deleted file mode 100644 index a2d288faf..000000000 --- a/.github/workflows/purge-cloudflare-cache.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: Purge CloudFlare Cache - -on: - page_build: - -jobs: - purge_cache: - runs-on: ubuntu-latest - steps: - - name: purge - uses: jakejarvis/cloudflare-purge-action@master - env: - CLOUDFLARE_ZONE: ${{ secrets.CLOUDFLARE_ZONE }} - CLOUDFLARE_TOKEN: ${{ secrets.CLOUDFLARE_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 9a92223ed..000000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,163 +0,0 @@ -env: - IS_RELEASE_BRANCH: ${{ startsWith(github.ref, 'refs/heads/release/') }} - BRANCH_NAME: ${{ github.ref_name }} - -name: Build and Deploy - -on: - workflow_dispatch: - push: - branches: - - 'release/1.*' - paths-ignore: - - '.github/**' - - '.gitignore' - - '*.md' - - 'LICENSE' - - 'build-and-deploy.ps1' - -jobs: - build: - runs-on: windows-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Setup MSBuild - uses: microsoft/setup-msbuild@v2 - - - name: Set up .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: '7.x' - - - name: Restore NuGet packages - run: msbuild src/Greenshot.sln /p:Configuration=Release /restore /t:PrepareForBuild - env: - Box13_ClientId: ${{ secrets.Box13_ClientId }} - Box13_ClientSecret: ${{ secrets.Box13_ClientSecret }} - DropBox13_ClientId: ${{ secrets.DropBox13_ClientId }} - DropBox13_ClientSecret: ${{ secrets.DropBox13_ClientSecret }} - Flickr_ClientId: ${{ secrets.Flickr_ClientId }} - Flickr_ClientSecret: ${{ secrets.Flickr_ClientSecret }} - Imgur13_ClientId: ${{ secrets.Imgur13_ClientId }} - Imgur13_ClientSecret: ${{ secrets.Imgur13_ClientSecret }} - Photobucket_ClientId: ${{ secrets.Photobucket_ClientId }} - Photobucket_ClientSecret: ${{ secrets.Photobucket_ClientSecret }} - Picasa_ClientId: ${{ secrets.Picasa_ClientId }} - Picasa_ClientSecret: ${{ secrets.Picasa_ClientSecret }} - - - name: Build and package - run: msbuild src/Greenshot.sln /p:Configuration=Release /t:Rebuild /v:normal - env: - Box13_ClientId: ${{ secrets.Box13_ClientId }} - Box13_ClientSecret: ${{ secrets.Box13_ClientSecret }} - DropBox13_ClientId: ${{ secrets.DropBox13_ClientId }} - DropBox13_ClientSecret: ${{ secrets.DropBox13_ClientSecret }} - Flickr_ClientId: ${{ secrets.Flickr_ClientId }} - Flickr_ClientSecret: ${{ secrets.Flickr_ClientSecret }} - Imgur13_ClientId: ${{ secrets.Imgur13_ClientId }} - Imgur13_ClientSecret: ${{ secrets.Imgur13_ClientSecret }} - Photobucket_ClientId: ${{ secrets.Photobucket_ClientId }} - Photobucket_ClientSecret: ${{ secrets.Photobucket_ClientSecret }} - Picasa_ClientId: ${{ secrets.Picasa_ClientId }} - Picasa_ClientSecret: ${{ secrets.Picasa_ClientSecret }} - - - name: Copy Files - run: | - mkdir -p ${{ github.workspace }}/artifacts - cp installer/Greenshot-INSTALLER-*.exe ${{ github.workspace }}/artifacts/ - - - name: Upload Artifact - uses: actions/upload-artifact@v4 - with: - name: drop - path: ${{ github.workspace }}/artifacts - - deploy: - runs-on: windows-latest - needs: build - - steps: - - - name: Checkout repository - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Download build artifacts - uses: actions/download-artifact@v4 - with: - name: drop # Name of the artifact uploaded in previous steps - path: drop # Local folder where artifacts are downloaded - - - name: Extract version from file name - if: env.IS_RELEASE_BRANCH == 'true' - id: version_from_filename - run: | - $file = Get-ChildItem drop -Filter "Greenshot-INSTALLER-*.exe" | Select-Object -First 1 - if (-not $file) { - throw "No matching file found in 'drop' directory." - } - if ($file.Name -match "Greenshot-INSTALLER-([\d\.]+)(.*)\.exe") { - echo "version=$($matches[1])" >> $Env:GITHUB_OUTPUT - } else { - throw "Version number could not be extracted from file name: $($file.Name)" - } - shell: pwsh - - - name: Set version from sanitized branch name - if: env.IS_RELEASE_BRANCH != 'true' - id: version_from_branchname - run: | - $branch = "${{ github.ref }}" -replace '^refs/heads/', '' - $sanitized = $branch -replace '[^a-zA-Z0-9._-]', '_' - echo "version=$sanitized" >> $Env:GITHUB_OUTPUT - shell: pwsh - - - name: Set version info - id: version_info - run: | - echo "version=${{ steps.version_from_filename.outputs.version || steps.version_from_branchname.outputs.version }}" >> $GITHUB_OUTPUT - shell: bash - - - - name: Create tag - if: env.IS_RELEASE_BRANCH == 'true' - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git tag -a "v${{ steps.version_info.outputs.version }}" -m "v${{ steps.version_info.outputs.version }}" - git push origin "v${{ steps.version_info.outputs.version }}" - - - name: Rename installer for non-release branch - if: env.IS_RELEASE_BRANCH != 'true' - run: | - branch="${BRANCH_NAME:-${GITHUB_REF#refs/heads/}}" - sanitized=$(echo "$branch" | sed 's/[^a-zA-Z0-9._-]/_/g') - mv drop/Greenshot-INSTALLER-*.exe "drop/Greenshot-INSTALLER-${sanitized}.exe" - shell: bash - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - name: "Greenshot ${{ steps.version_info.outputs.version }} (continuous build)" - tag_name: ${{ env.IS_RELEASE_BRANCH == 'true' && format('v{0}', steps.version_info.outputs.version) || env.BRANCH_NAME }} - files: drop/*.exe - generate_release_notes: true - draft: ${{ env.IS_RELEASE_BRANCH != 'true' }} - prerelease: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Trigger GitHub Pages rebuild - shell: bash - run: | - curl -X POST \ - -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ - -H "Accept: application/vnd.github+json" \ - https://api.github.com/repos/${{ github.repository }}/pages/builds - diff --git a/.github/workflows/update-gh-pages.yml b/.github/workflows/update-gh-pages.yml deleted file mode 100644 index ba45b70d1..000000000 --- a/.github/workflows/update-gh-pages.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Update GitHub Pages - -on: - workflow_dispatch: - release: - types: [published, unpublished, created, edited, deleted, prereleased, released] - -jobs: - update-gh-pages: - runs-on: ubuntu-latest - steps: - - name: Trigger GitHub Pages rebuild - shell: bash - run: | - curl -X POST \ - -H "Authorization: Bearer ${{ secrets.GH_PAGES_TOKEN }}" \ - -H "Accept: application/vnd.github+json" \ - https://api.github.com/repos/${{ github.repository }}/pages/builds diff --git a/.gitignore b/.gitignore index 7e96d5501..3afd2d7ab 100644 --- a/.gitignore +++ b/.gitignore @@ -214,6 +214,4 @@ ModelManifest.xml *.credentials.cs # Rider files -.idea - -/installer/Greenshot-INSTALLER-*.exe +.idea \ No newline at end of file diff --git a/README.md b/README.md index a3e9696c7..1f47983e3 100644 --- a/README.md +++ b/README.md @@ -21,25 +21,4 @@ Being easy to understand and configurable, Greenshot is an efficient tool for pr About this repository --------------------- -This is the development branch is for Greenshot 1.3, which has been first released on 2025-07-14. - -Releases --------- - -You can find a list of all releases (stable and unstable) in the [Github releases](https://github.com/greenshot/greenshot/releases) or in the [version history on our website](https://getgreenshot.org/version-history/). -The [downloads page on our website](https://getgreenshot.org/downloads/) always links to the latest stable release. - -Trademark and Logo Usage Policy -------------------------------- - -The Greenshot logo and trademark are the property of the Greenshot development team. Unauthorized use of the logo and trademark is generally prohibited. However, we allow the use of the Greenshot name and logo in the following contexts: - -* In blog posts, articles, or reviews that discuss or promote the Greenshot, provided that the usage is fair and does not imply endorsement by Greenshot. -* In educational materials or presentations that accurately represent the project. - -Please refrain from using the Greenshot logo and trademark in any promotional materials, products, or in a manner that may cause confusion or imply endorsement without prior written permission. - -If you have any questions or wish to seek permission for other uses, please contact us. - -Thank you for your understanding and cooperation. - +This repository is for Greenshot 1.3, currently in development, but is the next planned release diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 324758e42..000000000 --- a/SECURITY.md +++ /dev/null @@ -1,5 +0,0 @@ -# Security Policy - -## Reporting a Vulnerability - -If you think you found a security issue in Greenshot, please report it responsibly [in our security section](https://github.com/greenshot/greenshot/security). We try to look into it as soon as possible - please give us some time for reaction, though. diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 000000000..558d40760 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,106 @@ +# .NET Desktop +# Build and run tests for .NET Desktop or Windows classic desktop solutions. +# Add steps that publish symbols, save build artifacts, and more: +# https://docs.microsoft.com/azure/devops/pipelines/apps/windows/dot-net + +trigger: + batch: true + branches: + include: + - 'release/1.*' + exclude: + - 'develop' + +stages: +- stage: Build + jobs: + - job: Build + variables: + - group: 'Plug-in Credentials' + - name: solution + value: 'src/Greenshot.sln' + - name: buildPlatform + value: 'Any CPU' + - name: buildConfiguration + value: 'Release' + + pool: + vmImage: 'Windows-latest' + + steps: + - task: MSBuild@1 + displayName: Restore nuget packages and generate credential templates + inputs: + solution: '$(solution)' + platform: $(buildPlatform) + configuration: $(buildConfiguration) + msbuildArguments: '/restore /t:PrepareForBuild' + + - task: MSBuild@1 + displayName: Build and package + inputs: + solution: '$(solution)' + platform: $(buildPlatform) + configuration: $(buildConfiguration) + env: + Box13_ClientId: $(Box13_ClientId) + Box13_ClientSecret: $(Box13_ClientSecret) + DropBox13_ClientId: $(DropBox13_ClientId) + DropBox13_ClientSecret: $(DropBox13_ClientSecret) + Flickr_ClientId: $(Flickr_ClientId) + Flickr_ClientSecret: $(Flickr_ClientSecret) + Imgur13_ClientId: $(Imgur13_ClientId) + Imgur13_ClientSecret: $(Imgur13_ClientSecret) + Photobucket_ClientId: $(Photobucket_ClientId) + Photobucket_ClientSecret: $(Photobucket_ClientSecret) + Picasa_ClientId: $(Picasa_ClientId) + Picasa_ClientSecret: $(Picasa_ClientSecret) + + - task: CopyFiles@2 + displayName: 'Copy Files to: $(build.artifactstagingdirectory)' + inputs: + SourceFolder: '$(Build.SourcesDirectory)\installer' + Contents: Greenshot-INSTALLER-*.exe + TargetFolder: '$(build.artifactstagingdirectory)' + flattenFolders: true + + - task: PublishBuildArtifacts@1 + displayName: 'Publish Artifact: drop' + inputs: + PathtoPublish: '$(build.artifactstagingdirectory)' + +- stage: Deploy + jobs: + - deployment: GitHub_Release + pool: + vmImage: 'Windows-latest' + + environment: 'GitHub Release' + strategy: + # default deployment strategy + runOnce: + deploy: + steps: + - download: current + artifact: drop + + # Create a GitHub release + - task: GitHubRelease@0 + inputs: + gitHubConnection: GitHub Release + repositoryName: '$(Build.Repository.Name)' + action: 'create' # Options: create, edit, delete + target: '$(Build.SourceVersion)' # Required when action == Create || Action == Edit + tagSource: 'manual' # Required when action == Create# Options: auto, manual + tag: 'v$(Build.BuildNumber)' # Required when action == Edit || Action == Delete || TagSource == Manual + title: Greenshot $(Build.BuildNumber) unstable # Optional + #releaseNotesSource: 'file' # Optional. Options: file, input + #releaseNotesFile: # Optional + #releaseNotes: # Optional + assets: '$(Pipeline.Workspace)/drop/*.exe' + #assetUploadMode: 'delete' # Optional. Options: delete, replace + isDraft: true # Optional + isPreRelease: true # Optional + addChangeLog: true # Optional + #compareWith: 'lastFullRelease' # Required when addChangeLog == True. Options: lastFullRelease, lastRelease, lastReleaseByTag + #releaseTag: # Required when compareWith == LastReleaseByTag diff --git a/build-and-deploy.ps1 b/build-and-deploy.ps1 deleted file mode 100644 index 7645259c5..000000000 --- a/build-and-deploy.ps1 +++ /dev/null @@ -1,149 +0,0 @@ -# USAGE -# * Enable script execution in Powershell: 'Set-ExecutionPolicy RemoteSigned' -# * Create a GitHub personal access token (PAT) for greenshot repository -# * user must be owner of the repository -# * token needs read and write permissions ""for Contents"" and ""Pages"" -# * Execute the script and paste your token - -# Prompt the user to securely input the Github token -$SecureToken = Read-Host "Please enter your GitHub personal access token" -AsSecureString -$ReleaseToken = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureToken)) - -# Variables -$RepoPath = "." # Replace with your local repo path -$ArtifactsPath = "$RepoPath\artifacts" -$SolutionFile = "$RepoPath\src\Greenshot.sln" - -# Step 0: Update Local Repository -git pull - -# Step 1: Restore NuGet Packages -Write-Host "Restoring NuGet packages..." -msbuild "$SolutionFile" /p:Configuration=Release /restore /t:PrepareForBuild -if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to restore NuGet packages." - exit $LASTEXITCODE -} - -# Step 2: Build and Package -Write-Host "Building and packaging the solution..." -msbuild "$SolutionFile" /p:Configuration=Release /t:Rebuild /v:normal -if ($LASTEXITCODE -ne 0) { - Write-Error "Build failed." - exit $LASTEXITCODE -} - -# Step 3: Copy Installer Files -Write-Host "Copying installer files..." -if (-not (Test-Path $ArtifactsPath)) { - New-Item -ItemType Directory -Force -Path $ArtifactsPath -} -Copy-Item "$RepoPath\installer\Greenshot-INSTALLER-*.exe" -Destination $ArtifactsPath -Force -if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to copy installer files." - exit $LASTEXITCODE -} - -# Step 4: Extract Version from File Name -Write-Host "Extracting version from installer file name..." -$InstallerFile = Get-ChildItem $ArtifactsPath -Filter "Greenshot-INSTALLER-*.exe" | Select-Object -Last 1 -if (-not $InstallerFile) { - Write-Error "No matching installer file found in '$ArtifactsPath'." - exit 1 -} - -if ($InstallerFile.Name -match "Greenshot-INSTALLER-([\d\.]+).*\.exe") { - $Version = $matches[1] - Write-Host "Extracted version: $Version" -} else { - Write-Error "Version number could not be extracted from file name: $($InstallerFile.Name)" - exit 1 -} - -# Step 5: Create Git Tag -Write-Host "Creating Git tag..." -cd $RepoPath -#git config user.name "local-script" -#git config user.email "local-script@example.com" -git tag -a "v$Version" -m "v$Version" -git push origin "v$Version" -if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to create Git tag." - exit $LASTEXITCODE -} - -# Step 6: Create GitHub Release -Write-Host "Creating GitHub release..." -$Headers = @{ - Authorization = "Bearer $ReleaseToken" - Accept = "application/vnd.github+json" -} -$ReleaseData = @{ - tag_name = "v$Version" - name = "Greenshot $Version unstable" - body = "Pre-release of Greenshot $Version." - draft = $true - prerelease = $true - generate_release_notes = $true -} -$ReleaseResponse = Invoke-RestMethod ` - -Uri "https://api.github.com/repos/greenshot/greenshot/releases" ` - -Method POST ` - -Headers $Headers ` - -Body (ConvertTo-Json $ReleaseData -Depth 10) - -if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to create GitHub release." - exit $LASTEXITCODE -} - -Write-Host "Release created successfully." - -# Get the release ID from the response -$ReleaseId = $ReleaseResponse.id -Write-Host "Release ID: $ReleaseId" - -# Step 7: Upload .exe File to Release -Write-Host "Uploading .exe file to GitHub release..." -$ExeFilePath = "$ArtifactsPath\$($InstallerFile.Name)" -if (-Not (Test-Path $ExeFilePath)) { - Write-Error "Built .exe file not found: $ExeFilePath" - exit 1 -} - -# GitHub API for uploading release assets -$UploadUrl = $ReleaseResponse.upload_url -replace "{.*}", "" - -# Upload the file -$FileHeaders = @{ - Authorization = "Bearer $ReleaseToken" - ContentType = "application/octet-stream" -} -$FileName = [System.IO.Path]::GetFileName($ExeFilePath) - -Invoke-RestMethod ` - -Uri "$($UploadUrl)?name=$FileName" ` - -Method POST ` - -Headers $FileHeaders ` - -InFile $ExeFilePath ` - -ContentType "application/octet-stream" - -if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to upload .exe file to release." - exit $LASTEXITCODE -} - -Write-Host "File uploaded successfully: $FileName" - -# Step 7: Trigger GitHub Pages Rebuild -#Write-Host "Triggering GitHub Pages rebuild..." -#Invoke-RestMethod ` -# -Uri "https://api.github.com/repos/greenshot/greenshot/pages/builds" ` -# -Method POST ` -# -Headers $Headers -#if ($LASTEXITCODE -ne 0) { -# Write-Error "Failed to trigger GitHub Pages rebuild." -# exit $LASTEXITCODE -#} -# -#Write-Host "GitHub Pages rebuild triggered successfully." \ No newline at end of file diff --git a/installer/additional_files/readme.txt b/installer/additional_files/readme.txt index bdb64e2c4..1215789b6 100644 --- a/installer/additional_files/readme.txt +++ b/installer/additional_files/readme.txt @@ -7,29 +7,7 @@ CHANGE LOG: All details to our tickets can be found here: https://greenshot.atlassian.net -# Greenshot 1.3.xxx - -Bugs fixed: -* greenshot.ini: Exclude Plugins and Include Plugins setting broken [#648](https://github.com/greenshot/greenshot/issues/648) [#642](https://github.com/greenshot/greenshot/issues/642) thanks to @Christian-Schulz for providing the fix - -Features added: - -# Greenshot 1.3.296 - -Bugs fixed -* Fix Administrative installation via user interface [#546](https://github.com/greenshot/greenshot/issues/546) [#611](https://github.com/greenshot/greenshot/issues/611) [#598](https://github.com/greenshot/greenshot/issues/598) - -Features added: -* Installer: Allow Choice between All-Users (Administrative) and Current-User Installation [#625](https://github.com/greenshot/greenshot/pull/625) - -# Greenshot 1.3.292 - -Bugs fixed: -* Fix Administrative installation via command line using /ALLUSERS [#601](https://github.com/greenshot/greenshot/issues/601) [#619](https://github.com/greenshot/greenshot/issues/619) - -# Greenshot 1.3.290 - -Note: the version information for the first 1.3 release is outdated/incomplete. Due to the long timespan and large amount of changes between 1.2 and 1.3 we lost track. Sorry. +# Release notes for Greenshot 1.3 Greenshot 1.3 is the first Greenshot which targets .NET 4.7.2 which just by doing to solves some general issues in the area of Internet Explorer capturing, TLS communication and some other minor issues. @@ -668,5 +646,3 @@ Features added: * when clicking two overlapping elements, the one created later gets selected [ 1725175 ] * created textboxes can now be edited with a doubleclick [ 1704408 ] * selected font is now stored in the application config file [ 1704411 ] - - diff --git a/installer/innosetup/setup.iss b/installer/innosetup/setup.iss index fa358ee7b..23788205e 100644 --- a/installer/innosetup/setup.iss +++ b/installer/innosetup/setup.iss @@ -7,7 +7,6 @@ #define BinDir "bin\Release\net472" #define ReleaseDir "..\..\src\Greenshot\bin\Release\net472" #define PluginDir "..\..\src\Greenshot\bin\Release\net472\Plugins" -#define CertumThumbprint GetEnv('CertumThumbprint') ; Include the scripts to install .NET Framework ; See https://www.codeproject.com/KB/install/dotnetfx_innosetup_instal.aspx @@ -83,34 +82,33 @@ Source: {#LanguagesDir}\*zh-CN*; Excludes: "*installer*,*website*"; DestDir: {ap Source: {#LanguagesDir}\*zh-TW*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\zhTW; Flags: overwritereadonly ignoreversion replacesameversion; ;Office Plugin -Source: {#PluginDir}\Greenshot.Plugin.Office\Greenshot.Plugin.Office.dll; DestDir: {app}\Plugins\Office; Components: plugins\office; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#PluginDir}\Greenshot.Plugin.Office\*.dll; DestDir: {app}\Plugins\Office; Components: plugins\office; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; ;JIRA Plugin -Source: {#PluginDir}\Greenshot.Plugin.Jira\*Jira*.dll; DestDir: {app}\Plugins\Jira; Components: plugins\jira; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#PluginDir}\Greenshot.Plugin.Jira\*.dll; DestDir: {app}\Plugins\Jira; Components: plugins\jira; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; Source: {#BaseDir}\Greenshot.Plugin.Jira\Languages\language_jira*.xml; DestDir: {app}\Languages\Plugins\Jira; Components: plugins\jira; Flags: overwritereadonly ignoreversion replacesameversion; ;Imgur Plugin -Source: {#PluginDir}\Greenshot.Plugin.Imgur\Greenshot.Plugin.Imgur.dll; DestDir: {app}\Plugins\Imgur; Components: plugins\imgur; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#PluginDir}\Greenshot.Plugin.Imgur\*.dll; DestDir: {app}\Plugins\Imgur; Components: plugins\imgur; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; Source: {#BaseDir}\Greenshot.Plugin.Imgur\Languages\language_imgur*.xml; DestDir: {app}\Languages\Plugins\Imgur; Components: plugins\imgur; Flags: overwritereadonly ignoreversion replacesameversion; ;Box Plugin -Source: {#PluginDir}\Greenshot.Plugin.Box\Greenshot.Plugin.Box.dll; DestDir: {app}\Plugins\Box; Components: plugins\box; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#PluginDir}\Greenshot.Plugin.Box\*.dll; DestDir: {app}\Plugins\Box; Components: plugins\box; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; Source: {#BaseDir}\Greenshot.Plugin.Box\Languages\language_box*.xml; DestDir: {app}\Languages\Plugins\Box; Components: plugins\box; Flags: overwritereadonly ignoreversion replacesameversion; ;DropBox Plugin -Source: {#PluginDir}\Greenshot.Plugin.DropBox\Greenshot.Plugin.DropBox.dll; DestDir: {app}\Plugins\DropBox; Components: plugins\dropbox; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#PluginDir}\Greenshot.Plugin.DropBox\*.dll; DestDir: {app}\Plugins\DropBox; Components: plugins\dropbox; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; Source: {#BaseDir}\Greenshot.Plugin.DropBox\Languages\language_dropbox*.xml; DestDir: {app}\Languages\Plugins\DropBox; Components: plugins\dropbox; Flags: overwritereadonly ignoreversion replacesameversion; ;Flickr Plugin -Source: {#PluginDir}\Greenshot.Plugin.Flickr\Greenshot.Plugin.Flickr.dll; DestDir: {app}\Plugins\Flickr; Components: plugins\flickr; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#PluginDir}\Greenshot.Plugin.Flickr\*.dll; DestDir: {app}\Plugins\Flickr; Components: plugins\flickr; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; Source: {#BaseDir}\Greenshot.Plugin.Flickr\Languages\language_flickr*.xml; DestDir: {app}\Languages\Plugins\Flickr; Components: plugins\flickr; Flags: overwritereadonly ignoreversion replacesameversion; ;Photobucket Plugin -Source: {#PluginDir}\Greenshot.Plugin.Photobucket\Greenshot.Plugin.Photobucket.dll; DestDir: {app}\Plugins\Photobucket; Components: plugins\photobucket; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#PluginDir}\Greenshot.Plugin.Photobucket\*.dll; DestDir: {app}\Plugins\Photobucket; Components: plugins\photobucket; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; Source: {#BaseDir}\Greenshot.Plugin.Photobucket\Languages\language_photo*.xml; DestDir: {app}\Languages\Plugins\Photobucket; Components: plugins\photobucket; Flags: overwritereadonly ignoreversion replacesameversion; ;Confluence Plugin -Source: {#PluginDir}\Greenshot.Plugin.Confluence\Greenshot.Plugin.Confluence.dll; DestDir: {app}\Plugins\Confluence; Components: plugins\confluence; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#PluginDir}\Greenshot.Plugin.Confluence\*.dll; DestDir: {app}\Plugins\Confluence; Components: plugins\confluence; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; Source: {#BaseDir}\Greenshot.Plugin.Confluence\Languages\language_confluence*.xml; DestDir: {app}\Languages\Plugins\Confluence; Components: plugins\confluence; Flags: overwritereadonly ignoreversion replacesameversion; ;ExternalCommand Plugin -Source: {#PluginDir}\Greenshot.Plugin.ExternalCommand\Greenshot.Plugin.ExternalCommand.dll; DestDir: {app}\Plugins\ExternalCommand; Components: plugins\externalcommand; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#PluginDir}\Greenshot.Plugin.ExternalCommand\*.dll; DestDir: {app}\Plugins\ExternalCommand; Components: plugins\externalcommand; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; Source: {#BaseDir}\Greenshot.Plugin.ExternalCommand\Languages\language_externalcommand*.xml; DestDir: {app}\Languages\Plugins\ExternalCommand; Components: plugins\externalcommand; Flags: overwritereadonly ignoreversion replacesameversion; ;Win 10 Plugin -Source: {#PluginDir}\Greenshot.Plugin.Win10\Greenshot.Plugin.Win10.dll; DestDir: {app}\Plugins\Win10; Components: plugins\win10; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; -Source: {#PluginDir}\Greenshot.Plugin.Win10\Microsoft.Toolkit.Uwp.Notifications.dll; DestDir: {app}\Plugins\Win10; Components: plugins\win10; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#PluginDir}\Greenshot.Plugin.Win10\*.dll; DestDir: {app}\Plugins\Win10; Components: plugins\win10; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; [Setup] ; changes associations is used when the installer installs new extensions, it clears the explorer icon cache @@ -127,27 +125,21 @@ AppVersion={#Version} ArchitecturesInstallIn64BitMode=x64 Compression=lzma2/ultra64 SolidCompression=yes -DefaultDirName={autopf}\{#ExeName} +DefaultDirName={code:DefDirRoot}\{#ExeName} DefaultGroupName={#ExeName} InfoBeforeFile=..\additional_files\readme.txt LicenseFile=..\additional_files\license.txt LanguageDetectionMethod=uilanguage MinVersion=6.1sp1 +OutputBaseFilename={#ExeName}-INSTALLER-{#Version}-UNSTABLE OutputDir=..\ -; user may choose between all-users vs. current-user installation in a dialog or by using the /ALLUSERS flag (on the command line) -; in registry section, HKA will take care of the appropriate root key (HKLM vs. HKCU), see https://jrsoftware.org/ishelp/index.php?topic=admininstallmode -PrivilegesRequiredOverridesAllowed=dialog -; admin privileges not required, unless user chooses all-users installation -; the installer will ask for elevation if needed PrivilegesRequired=lowest SetupIconFile=..\..\src\Greenshot\icons\applicationIcon\icon.ico -#if CertumThumbprint != "" - OutputBaseFilename={#ExeName}-INSTALLER-{#Version}-UNSTABLE - SignTool=SignTool sign /sha1 "{#CertumThumbprint}" /tr http://time.certum.pl /td sha256 /fd sha256 /v $f - SignedUninstaller=yes -#else - OutputBaseFilename={#ExeName}-INSTALLER-{#Version}-UNSTABLE-UNSIGNED -#endif +; Create a SHA1 signature +; SignTool=SignTool sign /debug /fd sha1 /tr https://time.certum.pl /td sha1 $f +; Append a SHA256 to the previous SHA1 signature (this is what as does) +; SignTool=SignTool sign /debug /as /fd sha256 /tr https://time.certum.pl /td sha256 $f +; SignedUninstaller=yes UninstallDisplayIcon={app}\{#ExeName}.exe Uninstallable=true VersionInfoCompany={#ExeName} @@ -159,7 +151,6 @@ VersionInfoVersion={#Version} WizardImageFile=installer-large.bmp ; Reference a bitmap, max size 55x58 WizardSmallImageFile=installer-small.bmp - [Registry] ; Delete all startup entries, so we don't have leftover values Root: HKCU; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; @@ -178,16 +169,22 @@ Root: HKLM; Subkey: Software\Classes\.greenshot; ValueType: none; ValueName: {#E Root: HKLM; Subkey: Software\Classes\Greenshot; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; ; Create the startup entries if requested to do so -Root: HKA; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: string; ValueName: {#ExeName}; ValueData: """{app}\{#ExeName}.exe"""; Flags: uninsdeletevalue noerror; Tasks: startup +; HKEY_LOCAL_USER - for current user only +Root: HKCU; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: string; ValueName: {#ExeName}; ValueData: """{app}\{#ExeName}.exe"""; Permissions: users-modify; Flags: uninsdeletevalue noerror; Tasks: startup; Check: IsRegularUser +; HKEY_LOCAL_MACHINE - for all users when admin +Root: HKLM; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: string; ValueName: {#ExeName}; ValueData: """{app}\{#ExeName}.exe"""; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Tasks: startup; Check: not IsRegularUser ; Register our own filetype for all users -Root: HKA; Subkey: Software\Classes\.greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot"; Flags: uninsdeletevalue noerror -Root: HKA; Subkey: Software\Classes\Greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot File"; Flags: uninsdeletevalue noerror -Root: HKA; Subkey: Software\Classes\Greenshot\DefaultIcon; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE,0"""; Flags: uninsdeletevalue noerror -Root: HKA; Subkey: Software\Classes\Greenshot\shell\open\command; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE"" --openfile ""%1"""; Flags: uninsdeletevalue noerror - -; Disable the default PRTSCR Snipping Tool in Windows 11 -Root: HKCU; Subkey: Control Panel\Keyboard; ValueType: dword; ValueName: "PrintScreenKeyForSnippingEnabled"; ValueData: "0"; Flags: uninsdeletevalue; Check: ShouldDisableSnippingTool +; HKEY_LOCAL_USER - for current user only +Root: HKCU; Subkey: Software\Classes\.greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot"; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser +Root: HKCU; Subkey: Software\Classes\Greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot File"; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser +Root: HKCU; Subkey: Software\Classes\Greenshot\DefaultIcon; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE,0"""; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser +Root: HKCU; Subkey: Software\Classes\Greenshot\shell\open\command; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE"" --openfile ""%1"""; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser +; HKEY_LOCAL_MACHINE - for all users when admin +Root: HKLM; Subkey: Software\Classes\.greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot"; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser +Root: HKLM; Subkey: Software\Classes\Greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot File"; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser +Root: HKLM; Subkey: Software\Classes\Greenshot\DefaultIcon; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE,0"""; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser +Root: HKLM; Subkey: Software\Classes\Greenshot\shell\open\command; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE"" --openfile ""%1"""; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser [Icons] Name: {group}\{#ExeName}; Filename: {app}\{#ExeName}.exe; WorkingDir: {app}; AppUserModelID: "{#ExeName}" @@ -271,7 +268,6 @@ en.win10=Windows 10 plug-in en.UninstallIconDescription=Uninstall en.ShowLicense=Show license en.ShowReadme=Show Readme -en.disablewin11snippingtool=Disable Win11 default PrtScr snipping tool de.confluence=Confluence Plug-in de.default=Standard installation @@ -284,7 +280,6 @@ de.optimize=Optimierung der Leistung, kann etwas dauern. de.startgreenshot={#ExeName} starten de.startup={#ExeName} starten wenn Windows hochfährt de.win10=Windows 10 Plug-in -de.disablewin11snippingtool=Deaktiviere das Standard Windows 11 Snipping Tool auf "Druck" es.confluence=Extensión para Confluence es.default=${default} @@ -486,7 +481,6 @@ Name: "compact"; Description: "{code:CompactInstall}" Name: "custom"; Description: "{code:CustomInstall}"; Flags: iscustom [Components] -Name: "disablesnippingtool"; Description: {cm:disablewin11snippingtool}; Flags: disablenouninstallwarning; Types: default full custom; Check: IsWindows11OrNewer() Name: "greenshot"; Description: "Greenshot"; Types: default full compact custom; Flags: fixed ;Name: "plugins\networkimport"; Description: "Network Import Plugin"; Types: full Name: "plugins\box"; Description: {cm:box}; Types: full custom; Flags: disablenouninstallwarning @@ -536,8 +530,23 @@ Name: "languages\ukUA"; Description: {cm:ukUA}; Types: full custom; Flags: disab Name: "languages\viVN"; Description: {cm:viVN}; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('e') Name: "languages\zhCN"; Description: {cm:zhCN}; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('a') Name: "languages\zhTW"; Description: {cm:zhTW}; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('9') - [Code] +// Do we have a regular user trying to install this? +function IsRegularUser(): Boolean; +begin + Result := not (IsAdmin or IsAdminInstallMode); +end; + +// The following code is used to select the installation path, this is localappdata if non poweruser +function DefDirRoot(Param: String): String; +begin + if IsRegularUser then + Result := ExpandConstant('{localappdata}') + else + Result := ExpandConstant('{pf}') +end; + + function FullInstall(Param : String) : String; begin result := SetupMessage(msgFullInstallation); @@ -735,19 +744,6 @@ begin Result := IsWindowsVersionOrNewer(10, 0); end; -function IsWindows11OrNewer: Boolean; -var - WindowsVersion: TWindowsVersion; -begin - GetWindowsVersionEx(WindowsVersion); - Result := (WindowsVersion.Major >= 10) and (WindowsVersion.Build >= 22000); -end; - -function ShouldDisableSnippingTool: Boolean; -begin - Result := IsComponentSelected('disablesnippingtool'); -end; - [Run] Filename: "{app}\{#ExeName}.exe"; Description: "{cm:startgreenshot}"; Parameters: "{code:GetParamsForGS}"; WorkingDir: "{app}"; Flags: nowait postinstall runasoriginaluser Filename: "https://getgreenshot.org/thank-you/?language={language}&version={#Version}"; Flags: shellexec runasoriginaluser diff --git a/nuget.config b/nuget.config deleted file mode 100644 index 554c2f634..000000000 --- a/nuget.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 131878749..b6533ddd7 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,13 +1,13 @@  - Copyright © Greenshot 2004-2022 + Copyright © Greenshot 2004-2021 Greenshot https://getgreenshot.org/favicon.ico https://github.com/greenshot/greenshot git https://github.com/greenshot/greenshot GPL-3.0-only - latest + 9 true true win10-x64;win10-x86;win-x64;win-x86 @@ -46,7 +46,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index caa6a3db3..f2ce406a1 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -1,65 +1,13 @@ - - - - - - - - - - (); - foreach (var line in InputLines) - { - string text = line.ItemSpec; - foreach (var token in Tokens) - { - string tokenName = token.ItemSpec; - // Skip if tokenName is null or empty - if (string.IsNullOrEmpty(tokenName)) - continue; - - string replacementValue = token.GetMetadata("ReplacementValue"); - if (!string.IsNullOrEmpty(replacementValue)) - { - string placeholder = "$"+ "{"+tokenName+"}"; // Token-Format wie $(Box13_ClientId) - text = text.Replace(placeholder, replacementValue); - } - } - output.Add(new Microsoft.Build.Utilities.TaskItem(text)); - } - OutputLines = output.ToArray(); - ]]> - - - - $(NuGetPackageRoot)msbuildtasks/1.5.0.235/tools/ + $(PkgMSBuildTasks)\tools\ - - + + - - - - - - - - - - - - + + - - - - - + \ No newline at end of file diff --git a/src/Greenshot.Base/Controls/GreenshotForm.cs b/src/Greenshot.Base/Controls/GreenshotForm.cs index 85bc6bd4d..eff79e975 100644 --- a/src/Greenshot.Base/Controls/GreenshotForm.cs +++ b/src/Greenshot.Base/Controls/GreenshotForm.cs @@ -102,20 +102,6 @@ namespace Greenshot.Base.Controls /// protected bool ToFront { get; set; } - protected GreenshotForm() - { - DpiChanged += (sender, dpiChangedEventArgs) => DpiChangedHandler(dpiChangedEventArgs.DeviceDpiOld, dpiChangedEventArgs.DeviceDpiNew); - } - - /// - /// This is the basic DpiChangedHandler responsible for all the DPI relative changes - /// - /// - /// - protected virtual void DpiChangedHandler(int oldDpi, int newDpi) - { - } - #if DEBUG /// /// Code to initialize the language etc during design time @@ -364,7 +350,7 @@ namespace Greenshot.Base.Controls { if (!Language.TryGetString(languageKey, out langString)) { - LOG.DebugFormat("Unknown language key '{0}' configured for control '{1}', this might be okay.", languageKey, applyTo.Name); + LOG.WarnFormat("Unknown language key '{0}' configured for control '{1}', this might be okay.", languageKey, applyTo.Name); return; } @@ -396,10 +382,10 @@ namespace Greenshot.Base.Controls protected void ApplyLanguage(Control applyTo) { - if (applyTo is not IGreenshotLanguageBindable languageBindable) + if (!(applyTo is IGreenshotLanguageBindable languageBindable)) { // check if it's a menu! - if (applyTo is not ToolStrip toolStrip) + if (!(applyTo is ToolStrip toolStrip)) { return; } @@ -416,14 +402,20 @@ namespace Greenshot.Base.Controls ApplyLanguage(applyTo, languageBindable.LanguageKey); // Repopulate the combox boxes - if (applyTo is not (IGreenshotConfigBindable configBindable and GreenshotComboBox comboxBox)) return; - if (string.IsNullOrEmpty(configBindable.SectionName) || string.IsNullOrEmpty(configBindable.PropertyName)) return; - IniSection section = IniConfig.GetIniSection(configBindable.SectionName); - if (section == null) return; - // Only update the language, so get the actual value and than repopulate - Enum currentValue = comboxBox.GetSelectedEnum(); - comboxBox.Populate(section.Values[configBindable.PropertyName].ValueType); - comboxBox.SetValue(currentValue); + if (applyTo is IGreenshotConfigBindable configBindable && applyTo is GreenshotComboBox comboxBox) + { + if (!string.IsNullOrEmpty(configBindable.SectionName) && !string.IsNullOrEmpty(configBindable.PropertyName)) + { + IniSection section = IniConfig.GetIniSection(configBindable.SectionName); + if (section != null) + { + // Only update the language, so get the actual value and than repopulate + Enum currentValue = comboxBox.GetSelectedEnum(); + comboxBox.Populate(section.Values[configBindable.PropertyName].ValueType); + comboxBox.SetValue(currentValue); + } + } + } } /// @@ -466,9 +458,10 @@ namespace Greenshot.Base.Controls continue; } - if (controlObject is not Control applyToControl) + if (!(controlObject is Control applyToControl)) { - if (controlObject is not ToolStripItem applyToItem) + ToolStripItem applyToItem = controlObject as ToolStripItem; + if (applyToItem == null) { LOG.DebugFormat("No Control or ToolStripItem: {0}", field.Name); continue; @@ -537,7 +530,7 @@ namespace Greenshot.Base.Controls /// /// Fill all GreenshotControls with the values from the configuration /// - private void FillFields() + protected void FillFields() { foreach (FieldInfo field in GetCachedFields(GetType())) { diff --git a/src/Greenshot.Base/Controls/SaveImageFileDialog.cs b/src/Greenshot.Base/Controls/SaveImageFileDialog.cs index 091152d64..3c9b2780e 100644 --- a/src/Greenshot.Base/Controls/SaveImageFileDialog.cs +++ b/src/Greenshot.Base/Controls/SaveImageFileDialog.cs @@ -120,7 +120,6 @@ namespace Greenshot.Base.Controls private void PrepareFilterOptions() { - // TODO: Change to the FileFormatHandlerRegistry to look for all the supported extensions OutputFormat[] supportedImageFormats = (OutputFormat[]) Enum.GetValues(typeof(OutputFormat)); _filterOptions = new FilterOption[supportedImageFormats.Length]; for (int i = 0; i < _filterOptions.Length; i++) @@ -167,7 +166,7 @@ namespace Greenshot.Base.Controls // if the filename contains a valid extension, which is the same like the selected filter item's extension, the filename is okay if (fn.EndsWith(Extension, StringComparison.CurrentCultureIgnoreCase)) return fn; // otherwise we just add the selected filter item's extension - return fn + "." + Extension; + else return fn + "." + Extension; } set { diff --git a/src/Greenshot.Base/Controls/ThumbnailForm.cs b/src/Greenshot.Base/Controls/ThumbnailForm.cs index d5b8c50e8..c8f22f1f1 100644 --- a/src/Greenshot.Base/Controls/ThumbnailForm.cs +++ b/src/Greenshot.Base/Controls/ThumbnailForm.cs @@ -22,15 +22,12 @@ using System; using System.Drawing; using System.Windows.Forms; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; -using Dapplo.Windows.DesktopWindowsManager; -using Dapplo.Windows.DesktopWindowsManager.Structs; -using Dapplo.Windows.User32; -using Dapplo.Windows.User32.Enums; using Greenshot.Base.Core; using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; +using Greenshot.Base.UnmanagedHelpers; +using Greenshot.Base.UnmanagedHelpers.Enums; +using Greenshot.Base.UnmanagedHelpers.Structs; namespace Greenshot.Base.Controls { @@ -73,7 +70,7 @@ namespace Greenshot.Base.Controls { if (_thumbnailHandle == IntPtr.Zero) return; - DwmApi.DwmUnregisterThumbnail(_thumbnailHandle); + DWM.DwmUnregisterThumbnail(_thumbnailHandle); _thumbnailHandle = IntPtr.Zero; } @@ -86,19 +83,19 @@ namespace Greenshot.Base.Controls { UnregisterThumbnail(); - DwmApi.DwmRegisterThumbnail(Handle, window.Handle, out _thumbnailHandle); + DWM.DwmRegisterThumbnail(Handle, window.Handle, out _thumbnailHandle); if (_thumbnailHandle == IntPtr.Zero) return; - var result = DwmApi.DwmQueryThumbnailSourceSize(_thumbnailHandle, out var sourceSize); + var result = DWM.DwmQueryThumbnailSourceSize(_thumbnailHandle, out var sourceSize); if (result.Failed()) { - DwmApi.DwmUnregisterThumbnail(_thumbnailHandle); + DWM.DwmUnregisterThumbnail(_thumbnailHandle); return; } if (sourceSize.IsEmpty) { - DwmApi.DwmUnregisterThumbnail(_thumbnailHandle); + DWM.DwmUnregisterThumbnail(_thumbnailHandle); return; } @@ -113,17 +110,17 @@ namespace Greenshot.Base.Controls Width = thumbnailWidth; Height = thumbnailHeight; // Prepare the displaying of the Thumbnail - var dwmThumbnailProperties = new DwmThumbnailProperties() + var dwmThumbnailProperties = new DWM_THUMBNAIL_PROPERTIES { Opacity = 255, Visible = true, SourceClientAreaOnly = false, - Destination = new NativeRect(0, 0, thumbnailWidth, thumbnailHeight) + Destination = new RECT(0, 0, thumbnailWidth, thumbnailHeight) }; - result = DwmApi.DwmUpdateThumbnailProperties(_thumbnailHandle, ref dwmThumbnailProperties); + result = DWM.DwmUpdateThumbnailProperties(_thumbnailHandle, ref dwmThumbnailProperties); if (result.Failed()) { - DwmApi.DwmUnregisterThumbnail(_thumbnailHandle); + DWM.DwmUnregisterThumbnail(_thumbnailHandle); return; } @@ -140,13 +137,13 @@ namespace Greenshot.Base.Controls // Make sure it's on "top"! if (parentControl != null) { - User32Api.SetWindowPos(Handle, parentControl.Handle, 0, 0, 0, 0, WindowPos.SWP_NOMOVE | WindowPos.SWP_NOSIZE | WindowPos.SWP_NOACTIVATE); + User32.SetWindowPos(Handle, parentControl.Handle, 0, 0, 0, 0, WindowPos.SWP_NOMOVE | WindowPos.SWP_NOSIZE | WindowPos.SWP_NOACTIVATE); } } public void AlignToControl(Control alignTo) { - var screenBounds = DisplayInfo.ScreenBounds; + var screenBounds = WindowCapture.GetScreenBounds(); if (screenBounds.Contains(alignTo.Left, alignTo.Top - Height)) { Location = new Point(alignTo.Left + (alignTo.Width / 2) - (Width / 2), alignTo.Top - Height); diff --git a/src/Greenshot.Base/Core/AbstractDestination.cs b/src/Greenshot.Base/Core/AbstractDestination.cs index c76574bab..96eaf46bc 100644 --- a/src/Greenshot.Base/Core/AbstractDestination.cs +++ b/src/Greenshot.Base/Core/AbstractDestination.cs @@ -24,18 +24,15 @@ using System.Collections.Generic; using System.Drawing; using System.Threading; using System.Windows.Forms; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; -using Dapplo.Windows.Dpi; -using Dapplo.Windows.User32; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; +using Greenshot.Base.UnmanagedHelpers; using log4net; namespace Greenshot.Base.Core { /// - /// The AbstractDestination is a default implementation of IDestination + /// Description of AbstractDestination. /// public abstract class AbstractDestination : IDestination { @@ -44,7 +41,7 @@ namespace Greenshot.Base.Core public virtual int CompareTo(object obj) { - if (obj is not IDestination other) + if (!(obj is IDestination other)) { return 1; } @@ -179,7 +176,7 @@ namespace Greenshot.Base.Core ExportInformation exportInformation = new ExportInformation(Designation, Language.GetString("settings_destination_picker")); var menu = new ContextMenuStrip { - ImageScalingSize = CoreConfig.IconSize, + ImageScalingSize = CoreConfig.ScaledIconSize, Tag = null, TopLevel = true }; @@ -187,10 +184,10 @@ namespace Greenshot.Base.Core menu.Opening += (sender, args) => { // find the DPI settings for the screen where this is going to land - var screenDpi = NativeDpiMethods.GetDpi(menu.Location); - var scaledIconSize = DpiCalculator.ScaleWithDpi(CoreConfig.IconSize, screenDpi); + var screenDpi = DpiHelper.GetDpi(menu.Location); + var scaledIconSize = DpiHelper.ScaleWithDpi(CoreConfig.IconSize, screenDpi); menu.SuspendLayout(); - var fontSize = DpiCalculator.ScaleWithDpi(12f, screenDpi); + var fontSize = DpiHelper.ScaleWithDpi(12f, screenDpi); menu.Font = new Font(FontFamily.GenericSansSerif, fontSize, FontStyle.Regular, GraphicsUnit.Pixel); menu.ImageScalingSize = scaledIconSize; menu.ResumeLayout(); @@ -307,7 +304,7 @@ namespace Greenshot.Base.Core ShowMenuAtCursor(menu); return exportInformation; } - + /// /// This method will show the supplied context menu at the mouse cursor, also makes sure it has focus and it's not visible in the taskbar. /// @@ -315,21 +312,21 @@ namespace Greenshot.Base.Core private static void ShowMenuAtCursor(ContextMenuStrip menu) { // find a suitable location - NativePoint location = Cursor.Position; - var menuRectangle = new NativeRect(location, menu.Size); + Point location = Cursor.Position; + Rectangle menuRectangle = new Rectangle(location, menu.Size); - menuRectangle = menuRectangle.Intersect(DisplayInfo.ScreenBounds); + menuRectangle.Intersect(WindowCapture.GetScreenBounds()); if (menuRectangle.Height < menu.Height) { - location = location.Offset(-40, -(menuRectangle.Height - menu.Height)); + location.Offset(-40, -(menuRectangle.Height - menu.Height)); } else { - location = location.Offset(-40, -10); + location.Offset(-40, -10); } // This prevents the problem that the context menu shows in the task-bar - User32Api.SetForegroundWindow(SimpleServiceProvider.Current.GetInstance().ContextMenuStrip.Handle); + User32.SetForegroundWindow(SimpleServiceProvider.Current.GetInstance().ContextMenuStrip.Handle); menu.Show(location); menu.Focus(); diff --git a/src/Greenshot.Base/Core/AnimationHelpers.cs b/src/Greenshot.Base/Core/AnimationHelpers.cs index fd693683f..8b58a1c34 100644 --- a/src/Greenshot.Base/Core/AnimationHelpers.cs +++ b/src/Greenshot.Base/Core/AnimationHelpers.cs @@ -22,7 +22,6 @@ using System; using System.Collections.Generic; using System.Drawing; -using Dapplo.Windows.Common.Structs; namespace Greenshot.Base.Core { @@ -281,19 +280,19 @@ namespace Greenshot.Base.Core /// /// Implementation of the RectangleAnimator /// - public class RectangleAnimator : AnimatorBase + public class RectangleAnimator : AnimatorBase { - public RectangleAnimator(NativeRect first, NativeRect last, int frames) + public RectangleAnimator(Rectangle first, Rectangle last, int frames) : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn) { } - public RectangleAnimator(NativeRect first, NativeRect last, int frames, EasingType easingType) + public RectangleAnimator(Rectangle first, Rectangle last, int frames, EasingType easingType) : base(first, last, frames, easingType, EasingMode.EaseIn) { } - public RectangleAnimator(NativeRect first, NativeRect last, int frames, EasingType easingType, EasingMode easingMode) + public RectangleAnimator(Rectangle first, Rectangle last, int frames, EasingType easingType, EasingMode easingMode) : base(first, last, frames, easingType, easingMode) { } @@ -301,8 +300,8 @@ namespace Greenshot.Base.Core /// /// Calculate the next frame object /// - /// NativeRect - public override NativeRect Next() + /// Rectangle + public override Rectangle Next() { if (!NextFrame) { @@ -318,7 +317,7 @@ namespace Greenshot.Base.Core double dh = Last.Height - First.Height; int width = First.Width + (int) (easingValue * dw); int height = First.Height + (int) (easingValue * dh); - Current = new NativeRect(x, y, width, height); + Current = new Rectangle(x, y, width, height); return Current; } @@ -327,19 +326,19 @@ namespace Greenshot.Base.Core /// /// Implementation of the PointAnimator /// - public class PointAnimator : AnimatorBase + public class PointAnimator : AnimatorBase { - public PointAnimator(NativePoint first, NativePoint last, int frames) + public PointAnimator(Point first, Point last, int frames) : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn) { } - public PointAnimator(NativePoint first, NativePoint last, int frames, EasingType easingType) + public PointAnimator(Point first, Point last, int frames, EasingType easingType) : base(first, last, frames, easingType, EasingMode.EaseIn) { } - public PointAnimator(NativePoint first, NativePoint last, int frames, EasingType easingType, EasingMode easingMode) + public PointAnimator(Point first, Point last, int frames, EasingType easingType, EasingMode easingMode) : base(first, last, frames, easingType, easingMode) { } @@ -348,7 +347,7 @@ namespace Greenshot.Base.Core /// Calculate the next frame value /// /// Point - public override NativePoint Next() + public override Point Next() { if (NextFrame) { @@ -358,7 +357,7 @@ namespace Greenshot.Base.Core int x = First.X + (int) (easingValue * dx); int y = First.Y + (int) (easingValue * dy); - Current = new NativePoint(x, y); + Current = new Point(x, y); } return Current; @@ -368,19 +367,19 @@ namespace Greenshot.Base.Core /// /// Implementation of the SizeAnimator /// - public class SizeAnimator : AnimatorBase + public class SizeAnimator : AnimatorBase { - public SizeAnimator(NativeSize first, NativeSize last, int frames) + public SizeAnimator(Size first, Size last, int frames) : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn) { } - public SizeAnimator(NativeSize first, NativeSize last, int frames, EasingType easingType) + public SizeAnimator(Size first, Size last, int frames, EasingType easingType) : base(first, last, frames, easingType, EasingMode.EaseIn) { } - public SizeAnimator(NativeSize first, NativeSize last, int frames, EasingType easingType, EasingMode easingMode) + public SizeAnimator(Size first, Size last, int frames, EasingType easingType, EasingMode easingMode) : base(first, last, frames, easingType, easingMode) { } @@ -389,7 +388,7 @@ namespace Greenshot.Base.Core /// Calculate the next frame values /// /// Size - public override NativeSize Next() + public override Size Next() { if (NextFrame) { @@ -398,7 +397,7 @@ namespace Greenshot.Base.Core double dh = Last.Height - First.Height; int width = First.Width + (int) (easingValue * dw); int height = First.Height + (int) (easingValue * dh); - Current = new NativeSize(width, height); + Current = new Size(width, height); } return Current; diff --git a/src/Greenshot.Base/Core/Capture.cs b/src/Greenshot.Base/Core/Capture.cs index 0be688727..347cd0067 100644 --- a/src/Greenshot.Base/Core/Capture.cs +++ b/src/Greenshot.Base/Core/Capture.cs @@ -22,9 +22,6 @@ using System; using System.Drawing; using System.Drawing.Imaging; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; -using Dapplo.Windows.User32; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Ocr; using log4net; @@ -39,18 +36,18 @@ namespace Greenshot.Base.Core { private static readonly ILog Log = LogManager.GetLogger(typeof(Capture)); - private NativeRect _screenBounds; + private Rectangle _screenBounds; /// /// Get/Set the screen bounds /// - public NativeRect ScreenBounds + public Rectangle ScreenBounds { get { - if (_screenBounds.IsEmpty) + if (_screenBounds == Rectangle.Empty) { - _screenBounds = DisplayInfo.ScreenBounds; + _screenBounds = WindowCapture.GetScreenBounds(); } return _screenBounds; @@ -127,23 +124,23 @@ namespace Greenshot.Base.Core /// public bool CursorVisible { get; set; } - private NativePoint _cursorLocation = NativePoint.Empty; + private Point _cursorLocation = Point.Empty; /// /// Get/Set the CursorLocation /// - public NativePoint CursorLocation + public Point CursorLocation { get => _cursorLocation; set => _cursorLocation = value; } - private NativePoint _location = NativePoint.Empty; + private Point _location = Point.Empty; /// /// Get/set the Location /// - public NativePoint Location + public Point Location { get => _location; set => _location = value; @@ -165,7 +162,7 @@ namespace Greenshot.Base.Core /// public Capture() { - _screenBounds = DisplayInfo.ScreenBounds; + _screenBounds = WindowCapture.GetScreenBounds(); _captureDetails = new CaptureDetails(); } @@ -217,8 +214,8 @@ namespace Greenshot.Base.Core /// /// Crops the capture to the specified rectangle (with Bitmap coordinates!) /// - /// NativeRect with bitmap coordinates - public bool Crop(NativeRect cropRectangle) + /// Rectangle with bitmap coordinates + public bool Crop(Rectangle cropRectangle) { Log.Debug("Cropping to: " + cropRectangle); if (!ImageHelper.Crop(ref _image, ref cropRectangle)) @@ -248,7 +245,7 @@ namespace Greenshot.Base.Core /// y coordinates to move the mouse public void MoveMouseLocation(int x, int y) { - _cursorLocation = _cursorLocation.Offset(x, y); + _cursorLocation.Offset(x, y); } // TODO: Enable when the elements are usable again. @@ -264,7 +261,7 @@ namespace Greenshot.Base.Core //private void MoveElements(List listOfElements, int x, int y) { // foreach(ICaptureElement childElement in listOfElements) { - // NativeRect bounds = childElement.Bounds; + // Rectangle bounds = childElement.Bounds; // bounds.Offset(x, y); // childElement.Bounds = bounds; // MoveElements(childElement.Children, x, y); diff --git a/src/Greenshot.Base/Core/CaptureHandler.cs b/src/Greenshot.Base/Core/CaptureHandler.cs index 74f58b555..f807666c7 100644 --- a/src/Greenshot.Base/Core/CaptureHandler.cs +++ b/src/Greenshot.Base/Core/CaptureHandler.cs @@ -20,16 +20,15 @@ */ using System.Drawing; -using Dapplo.Windows.Common.Structs; namespace Greenshot.Base.Core { /// /// This is the method signature which is used to capture a rectangle from the screen. /// - /// NativeRect + /// /// Captured Bitmap - public delegate Bitmap CaptureScreenRectangleHandler(NativeRect captureBounds); + public delegate Bitmap CaptureScreenRectangleHandler(Rectangle captureBounds); /// /// This is a hack to experiment with different screen capture routines diff --git a/src/Greenshot.Base/Core/ClipboardHelper.cs b/src/Greenshot.Base/Core/ClipboardHelper.cs index bfe13c121..a990beca3 100644 --- a/src/Greenshot.Base/Core/ClipboardHelper.cs +++ b/src/Greenshot.Base/Core/ClipboardHelper.cs @@ -30,17 +30,11 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Windows.Forms; -using Dapplo.Windows.Clipboard; -using Dapplo.Windows.Common.Structs; -using Dapplo.Windows.Gdi32.Enums; -using Dapplo.Windows.Gdi32.Structs; -using Dapplo.Windows.User32; using Greenshot.Base.Core.Enums; -using Greenshot.Base.Core.FileFormatHandlers; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; -using Greenshot.Base.Interfaces.Drawing; using Greenshot.Base.Interfaces.Plugin; +using Greenshot.Base.UnmanagedHelpers; using log4net; using HtmlDocument = HtmlAgilityPack.HtmlDocument; @@ -69,8 +63,7 @@ namespace Greenshot.Base.Core //private static readonly string FORMAT_HTML = "HTML Format"; // Template for the HTML Text on the clipboard - // see: https://msdn.microsoft.com/en-us/library/ms649015%28v=v - // s.85%29.aspx + // see: https://msdn.microsoft.com/en-us/library/ms649015%28v=vs.85%29.aspx // or: https://msdn.microsoft.com/en-us/library/Aa767917.aspx private const string HtmlClipboardString = @"Version:0.9 StartHTML:<<<<<<<1 @@ -119,12 +112,12 @@ EndSelection:<<<<<<<4 string owner = null; try { - IntPtr hWnd = ClipboardNative.CurrentOwner; + IntPtr hWnd = User32.GetClipboardOwner(); if (hWnd != IntPtr.Zero) { try { - User32Api.GetWindowThreadProcessId(hWnd, out var pid); + User32.GetWindowThreadProcessId(hWnd, out var pid); using Process me = Process.GetCurrentProcess(); using Process ownerProcess = Process.GetProcessById(pid); // Exclude myself @@ -146,7 +139,9 @@ EndSelection:<<<<<<<4 catch (Exception e) { Log.Warn("Non critical error: Couldn't get clipboard process, trying to use the title.", e); - owner = User32Api.GetText(hWnd); + var title = new StringBuilder(260, 260); + User32.GetWindowText(hWnd, title, title.Capacity); + owner = title.ToString(); } } } @@ -284,9 +279,6 @@ EndSelection:<<<<<<<4 { if (dataObject == null) return false; - IList formats = GetFormats(dataObject); - Log.DebugFormat("Found formats: {0}", string.Join(",", formats)); - if (dataObject.GetDataPresent(DataFormats.Bitmap) || dataObject.GetDataPresent(DataFormats.Dib) || dataObject.GetDataPresent(DataFormats.Tiff) @@ -306,15 +298,14 @@ EndSelection:<<<<<<<4 { return true; } - var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); - var supportedExtensions = fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadDrawableFromStream).ToList(); - foreach (var (stream, filename) in IterateClipboardContent(dataObject)) + + foreach (var fileData in IterateClipboardContent(dataObject)) { try { - var extension = Path.GetExtension(filename)?.ToLowerInvariant(); - if (supportedExtensions.Contains(extension)) + using (ImageHelper.FromStream(fileData)) { + // If we get here, there is an image return true; } } @@ -324,7 +315,7 @@ EndSelection:<<<<<<<4 } finally { - stream?.Dispose(); + fileData?.Dispose(); } } @@ -336,8 +327,7 @@ EndSelection:<<<<<<<4 var imageStream = clipboardContent as MemoryStream; if (IsValidStream(imageStream)) { - // TODO: How to check if we support "just a stream"? - using (ImageIO.FromStream(imageStream)) + using (ImageHelper.FromStream(imageStream)) { // If we get here, there is an image return true; @@ -383,10 +373,9 @@ EndSelection:<<<<<<<4 /// Iterate the clipboard content /// /// IDataObject - /// IEnumerable{(MemoryStream,string)} - private static IEnumerable<(MemoryStream stream,string filename)> IterateClipboardContent(IDataObject dataObject) + /// IEnumerable{MemoryStream} + private static IEnumerable IterateClipboardContent(IDataObject dataObject) { - if (dataObject == null) yield break; var fileDescriptors = AvailableFileDescriptors(dataObject); if (fileDescriptors == null) yield break; @@ -424,8 +413,8 @@ EndSelection:<<<<<<<4 /// /// IEnumerable{FileDescriptor} /// IDataObject - /// IEnumerable{(MemoryStream stream, string filename)} - private static IEnumerable<(MemoryStream stream, string filename)> IterateFileDescriptors(IEnumerable fileDescriptors, IDataObject dataObject) + /// IEnumerable{MemoryStream} + private static IEnumerable IterateFileDescriptors(IEnumerable fileDescriptors, IDataObject dataObject) { if (fileDescriptors == null) { @@ -456,7 +445,7 @@ EndSelection:<<<<<<<4 if (fileData?.Length > 0) { fileData.Position = 0; - yield return (fileData, fileDescriptor.FileName); + yield return fileData; } fileIndex++; @@ -500,12 +489,8 @@ EndSelection:<<<<<<<4 public static Image GetImage() { IDataObject clipboardData = GetDataObject(); - if (clipboardData == null) - { - return null; - } // Return the first image - foreach (var clipboardImage in GetImages(clipboardData)) + foreach (Image clipboardImage in GetImages(clipboardData)) { return clipboardImage; } @@ -518,151 +503,56 @@ EndSelection:<<<<<<<4 /// Returned images must be disposed by the calling code! /// /// - /// IEnumerable of Bitmap - public static IEnumerable GetImages(IDataObject dataObject) + /// IEnumerable of Image + public static IEnumerable GetImages(IDataObject dataObject) { // Get single image, this takes the "best" match - Bitmap singleImage = GetImage(dataObject); + Image singleImage = GetImage(dataObject); if (singleImage != null) { - Log.Info($"Got {singleImage.GetType()} from clipboard with size {singleImage.Size}"); + Log.InfoFormat("Got image from clipboard with size {0} and format {1}", singleImage.Size, singleImage.PixelFormat); yield return singleImage; - yield break; } - - var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); - var supportedExtensions = fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadDrawableFromStream).ToList(); - - foreach (var (stream, filename) in IterateClipboardContent(dataObject)) + else { - var extension = Path.GetExtension(filename)?.ToLowerInvariant(); - if (!supportedExtensions.Contains(extension)) + foreach (var fileData in IterateClipboardContent(dataObject)) { - continue; - } - - Bitmap bitmap = null; - - try - { - if (!fileFormatHandlers.TryLoadFromStream(stream, extension, out bitmap)) + Image image; + try { + image = ImageHelper.FromStream(fileData); + } + catch (Exception ex) + { + Log.Error("Couldn't read file contents", ex); continue; } - - } - catch (Exception ex) - { - Log.Error("Couldn't read file contents", ex); - continue; - } - finally - { - stream?.Dispose(); - } - // If we get here, there is an image - yield return bitmap; - } - - // check if files are supplied - foreach (string imageFile in GetImageFilenames(dataObject)) - { - var extension = Path.GetExtension(imageFile)?.ToLowerInvariant(); - if (!supportedExtensions.Contains(extension)) - { - continue; - } - - Bitmap bitmap = null; - using FileStream fileStream = new FileStream(imageFile, FileMode.Open, FileAccess.Read, FileShare.Read); - try - { - if (!fileFormatHandlers.TryLoadFromStream(fileStream, extension, out bitmap)) + finally { - continue; + fileData?.Dispose(); } - } - catch (Exception ex) - { - Log.Error("Couldn't read file contents", ex); - continue; - } - // If we get here, there is an image - yield return bitmap; - } - } - - /// - /// Get all images (multiple if file names are available) from the dataObject - /// Returned images must be disposed by the calling code! - /// - /// - /// IEnumerable of IDrawableContainer - public static IEnumerable GetDrawables(IDataObject dataObject) - { - // Get single image, this takes the "best" match - IDrawableContainer singleImage = GetDrawable(dataObject); - if (singleImage != null) - { - Log.InfoFormat($"Got {singleImage.GetType()} from clipboard with size {singleImage.Size}"); - yield return singleImage; - yield break; - } - var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); - var supportedExtensions = fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadDrawableFromStream).ToList(); - - foreach (var (stream, filename) in IterateClipboardContent(dataObject)) - { - var extension = Path.GetExtension(filename)?.ToLowerInvariant(); - if (!supportedExtensions.Contains(extension)) - { - continue; + // If we get here, there is an image + yield return image; } - IEnumerable drawableContainers; - try + // check if files are supplied + foreach (string imageFile in GetImageFilenames(dataObject)) { - drawableContainers = fileFormatHandlers.LoadDrawablesFromStream(stream, extension); - } - catch (Exception ex) - { - Log.Error("Couldn't read file contents", ex); - continue; - } - finally - { - stream?.Dispose(); - } - // If we get here, there is an image - foreach (var container in drawableContainers) - { - yield return container; - } - } + Image returnImage = null; + try + { + returnImage = ImageHelper.LoadImage(imageFile); + } + catch (Exception streamImageEx) + { + Log.Error("Problem retrieving Image from clipboard.", streamImageEx); + } - // check if files are supplied - foreach (string imageFile in GetImageFilenames(dataObject)) - { - var extension = Path.GetExtension(imageFile)?.ToLowerInvariant(); - if (!supportedExtensions.Contains(extension)) - { - continue; - } - using FileStream fileStream = new FileStream(imageFile, FileMode.Open, FileAccess.Read, FileShare.Read); - IEnumerable drawableContainers; - try - { - drawableContainers = fileFormatHandlers.LoadDrawablesFromStream(fileStream, extension); - } - catch (Exception ex) - { - Log.Error("Couldn't read file contents", ex); - continue; - } - // If we get here, there is an image - foreach (var container in drawableContainers) - { - yield return container; + if (returnImage != null) + { + Log.InfoFormat("Got image from clipboard with size {0} and format {1}", returnImage.Size, returnImage.PixelFormat); + yield return returnImage; + } } } } @@ -672,226 +562,201 @@ EndSelection:<<<<<<<4 /// /// /// Image or null - private static Bitmap GetImage(IDataObject dataObject) + private static Image GetImage(IDataObject dataObject) { - if (dataObject == null) return null; - - Bitmap returnImage = null; - IList formats = GetFormats(dataObject); - string[] retrieveFormats; - - // Found a weird bug, where PNG's from Outlook 2010 are clipped - // So I build some special logic to get the best format: - if (formats != null && formats.Contains(FORMAT_PNG_OFFICEART) && formats.Contains(DataFormats.Dib)) + Image returnImage = null; + if (dataObject != null) { - // Outlook ?? - Log.Info("Most likely the current clipboard contents come from Outlook, as this has a problem with PNG and others we place the DIB format to the front..."); - retrieveFormats = new[] - { - DataFormats.Dib, FORMAT_BITMAP, FORMAT_FILECONTENTS, FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF, - DataFormats.Tiff, FORMAT_GIF, FORMAT_HTML - }; - } - else - { - retrieveFormats = new[] - { - FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_17, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF, DataFormats.Tiff, DataFormats.Dib, FORMAT_BITMAP, - FORMAT_FILECONTENTS, FORMAT_GIF, FORMAT_HTML - }; - } + IList formats = GetFormats(dataObject); + string[] retrieveFormats; - foreach (string currentFormat in retrieveFormats) - { - if (formats != null && formats.Contains(currentFormat)) + // Found a weird bug, where PNG's from Outlook 2010 are clipped + // So I build some special logic to get the best format: + if (formats != null && formats.Contains(FORMAT_PNG_OFFICEART) && formats.Contains(DataFormats.Dib)) { - Log.InfoFormat("Found {0}, trying to retrieve.", currentFormat); - returnImage = GetImageForFormat(currentFormat, dataObject); - } - else - { - Log.DebugFormat("Couldn't find format {0}.", currentFormat); - } - - if (returnImage != null) - { - return returnImage; - } - } - - return null; - } - - /// - /// Get an IDrawableContainer from the IDataObject, don't check for FileDrop - /// - /// - /// Image or null - private static IDrawableContainer GetDrawable(IDataObject dataObject) - { - if (dataObject == null) return null; - - IDrawableContainer returnImage = null; - IList formats = GetFormats(dataObject); - string[] retrieveFormats; - - // Found a weird bug, where PNG's from Outlook 2010 are clipped - // So I build some special logic to get the best format: - if (formats != null && formats.Contains(FORMAT_PNG_OFFICEART) && formats.Contains(DataFormats.Dib)) - { - // Outlook ?? - Log.Info("Most likely the current clipboard contents come from Outlook, as this has a problem with PNG and others we place the DIB format to the front..."); - retrieveFormats = new[] - { - DataFormats.Dib, FORMAT_BITMAP, FORMAT_FILECONTENTS, FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF, - DataFormats.Tiff, FORMAT_GIF, FORMAT_HTML - }; - } - else - { - retrieveFormats = new[] - { - FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_17, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF, DataFormats.Tiff, DataFormats.Dib, FORMAT_BITMAP, - FORMAT_FILECONTENTS, FORMAT_GIF, FORMAT_HTML - }; - } - - foreach (string currentFormat in retrieveFormats) - { - if (formats != null && formats.Contains(currentFormat)) - { - Log.InfoFormat("Found {0}, trying to retrieve.", currentFormat); - returnImage = GetDrawableForFormat(currentFormat, dataObject); - } - else - { - Log.DebugFormat("Couldn't find format {0}.", currentFormat); - } - - if (returnImage != null) - { - return returnImage; - } - } - - return null; - } - - /// - /// Helper method to try to get an Bitmap in the specified format from the dataObject - /// the DIB reader should solve some issues - /// It also supports Format17/DibV5, by using the following information: https://stackoverflow.com/a/14335591 - /// - /// string with the format - /// IDataObject - /// Bitmap or null - private static Bitmap GetImageForFormat(string format, IDataObject dataObject) - { - Bitmap bitmap = null; - - if (format == FORMAT_HTML) - { - var textObject = ContentAsString(dataObject, FORMAT_HTML, Encoding.UTF8); - if (textObject != null) - { - var doc = new HtmlDocument(); - doc.LoadHtml(textObject); - var imgNodes = doc.DocumentNode.SelectNodes("//img"); - if (imgNodes != null) + // Outlook ?? + Log.Info("Most likely the current clipboard contents come from Outlook, as this has a problem with PNG and others we place the DIB format to the front..."); + retrieveFormats = new[] { - foreach (var imgNode in imgNodes) - { - var srcAttribute = imgNode.Attributes["src"]; - var imageUrl = srcAttribute.Value; - Log.Debug(imageUrl); - bitmap = NetworkHelper.DownloadImage(imageUrl); - if (bitmap != null) - { - return bitmap; - } - } - } - } - } - - object clipboardObject = GetFromDataObject(dataObject, format); - var imageStream = clipboardObject as MemoryStream; - if (!IsValidStream(imageStream)) - { - return clipboardObject as Bitmap; - } - var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); - - // From here, imageStream is a valid stream - if (fileFormatHandlers.TryLoadFromStream(imageStream, format, out bitmap)) - { - return bitmap; - } - return null; - } - - /// - /// Helper method to try to get an IDrawableContainer in the specified format from the dataObject - /// the DIB reader should solve some issues - /// It also supports Format17/DibV5, by using the following information: https://stackoverflow.com/a/14335591 - /// - /// string with the format - /// IDataObject - /// IDrawableContainer or null - private static IDrawableContainer GetDrawableForFormat(string format, IDataObject dataObject) - { - IDrawableContainer drawableContainer = null; - - if (format == FORMAT_HTML) - { - var textObject = ContentAsString(dataObject, FORMAT_HTML, Encoding.UTF8); - if (textObject != null) - { - var doc = new HtmlDocument(); - doc.LoadHtml(textObject); - var imgNodes = doc.DocumentNode.SelectNodes("//img"); - if (imgNodes != null) - { - foreach (var imgNode in imgNodes) - { - var srcAttribute = imgNode.Attributes["src"]; - var imageUrl = srcAttribute.Value; - Log.Debug(imageUrl); - drawableContainer = NetworkHelper.DownloadImageAsDrawableContainer(imageUrl); - if (drawableContainer != null) - { - return drawableContainer; - } - } - } - } - } - - object clipboardObject = GetFromDataObject(dataObject, format); - var imageStream = clipboardObject as MemoryStream; - if (!IsValidStream(imageStream)) - { - // TODO: add text based, like "HTML Format" support here... - // TODO: solve the issue that we do not have a factory for the ImageContainer - /*var image = clipboardObject as Image; - if (image != null) - { - return new ImageContainer(this) - { - Image = image, - Left = x, - Top = y + DataFormats.Dib, FORMAT_BITMAP, FORMAT_FILECONTENTS, FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF, + DataFormats.Tiff, FORMAT_GIF, FORMAT_HTML }; } - return clipboardObject as Image; -*/ - return null; + else + { + retrieveFormats = new[] + { + FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_17, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF, DataFormats.Tiff, DataFormats.Dib, FORMAT_BITMAP, + FORMAT_FILECONTENTS, FORMAT_GIF, FORMAT_HTML + }; + } + + foreach (string currentFormat in retrieveFormats) + { + if (formats != null && formats.Contains(currentFormat)) + { + Log.InfoFormat("Found {0}, trying to retrieve.", currentFormat); + returnImage = GetImageForFormat(currentFormat, dataObject); + } + else + { + Log.DebugFormat("Couldn't find format {0}.", currentFormat); + } + + if (returnImage != null) + { + return returnImage; + } + } } - // From here, imageStream is a valid stream - var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); + return null; + } - return fileFormatHandlers.LoadDrawablesFromStream(imageStream, format).FirstOrDefault(); + /// + /// Helper method to try to get an image in the specified format from the dataObject + /// the DIB reader should solve some issues + /// It also supports Format17/DibV5, by using the following information: https://stackoverflow.com/a/14335591 + /// + /// string with the format + /// IDataObject + /// Image or null + private static Image GetImageForFormat(string format, IDataObject dataObject) + { + if (format == FORMAT_HTML) + { + var textObject = ContentAsString(dataObject, FORMAT_HTML, Encoding.UTF8); + if (textObject != null) + { + var doc = new HtmlDocument(); + doc.LoadHtml(textObject); + var imgNodes = doc.DocumentNode.SelectNodes("//img"); + if (imgNodes != null) + { + foreach (var imgNode in imgNodes) + { + var srcAttribute = imgNode.Attributes["src"]; + var imageUrl = srcAttribute.Value; + Log.Debug(imageUrl); + var image = NetworkHelper.DownloadImage(imageUrl); + if (image != null) + { + return image; + } + } + } + } + } + + object clipboardObject = GetFromDataObject(dataObject, format); + var imageStream = clipboardObject as MemoryStream; + if (!IsValidStream(imageStream)) + { + // TODO: add "HTML Format" support here... + return clipboardObject as Image; + } + + if (CoreConfig.EnableSpecialDIBClipboardReader) + { + if (format == FORMAT_17 || format == DataFormats.Dib) + { + Log.Info("Found DIB stream, trying to process it."); + try + { + if (imageStream != null) + { + byte[] dibBuffer = new byte[imageStream.Length]; + _ = imageStream.Read(dibBuffer, 0, dibBuffer.Length); + var infoHeader = BinaryStructHelper.FromByteArray(dibBuffer); + if (!infoHeader.IsDibV5) + { + Log.InfoFormat("Using special DIB @@ -975,7 +840,7 @@ EndSelection:<<<<<<<4 { SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false); // Create the image which is going to be saved so we don't create it multiple times - disposeImage = ImageIO.CreateImageFromSurface(surface, outputSettings, out imageToSave); + disposeImage = ImageOutput.CreateImageFromSurface(surface, outputSettings, out imageToSave); try { // Create PNG stream @@ -984,7 +849,7 @@ EndSelection:<<<<<<<4 pngStream = new MemoryStream(); // PNG works for e.g. Powerpoint SurfaceOutputSettings pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false); - ImageIO.SaveToStream(imageToSave, null, pngStream, pngOutputSettings); + ImageOutput.SaveToStream(imageToSave, null, pngStream, pngOutputSettings); pngStream.Seek(0, SeekOrigin.Begin); // Set the PNG stream dataObject.SetData(FORMAT_PNG, false, pngStream); @@ -1001,18 +866,11 @@ EndSelection:<<<<<<<4 { // Create the stream for the clipboard dibStream = new MemoryStream(); - var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); + var dibBytes = ((Bitmap)imageToSave).ConvertToDib(); + dibStream.Write(dibBytes,0, dibBytes.Length); - if (!fileFormatHandlers.TrySaveToStream((Bitmap)imageToSave, dibStream, DataFormats.Dib)) - { - dibStream.Dispose(); - dibStream = null; - } - else - { - // Set the DIB to the clipboard DataObject - dataObject.SetData(DataFormats.Dib, false, dibStream); - } + // Set the DIB to the clipboard DataObject + dataObject.SetData(DataFormats.Dib, false, dibStream); } } catch (Exception dibEx) @@ -1029,17 +887,20 @@ EndSelection:<<<<<<<4 dibV5Stream = new MemoryStream(); // Create the BITMAPINFOHEADER - var header = BitmapV5Header.Create(imageToSave.Width, imageToSave.Height, 32); - // Make sure we have BI_BITFIELDS, this seems to be normal for Format17? - header.Compression = BitmapCompressionMethods.BI_BITFIELDS; + var header = new BITMAPINFOHEADERV5(imageToSave.Width, imageToSave.Height, 32) + { + // Make sure we have BI_BITFIELDS, this seems to be normal for Format17? + biCompression = BI_COMPRESSION.BI_BITFIELDS + }; // Create a byte[] to write byte[] headerBytes = BinaryStructHelper.ToByteArray(header); // Write the BITMAPINFOHEADER to the stream dibV5Stream.Write(headerBytes, 0, headerBytes.Length); // As we have specified BI_COMPRESSION.BI_BITFIELDS, the BitfieldColorMask needs to be added - // This also makes sure the default values are set BitfieldColorMask colorMask = new BitfieldColorMask(); + // Make sure the values are set + colorMask.InitValues(); // Create the byte[] from the struct byte[] colorMaskBytes = BinaryStructHelper.ToByteArray(colorMask); Array.Reverse(colorMaskBytes); @@ -1063,7 +924,7 @@ EndSelection:<<<<<<<4 // Set the HTML if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.HTML)) { - string tmpFile = ImageIO.SaveToTmpFile(surface, new SurfaceOutputSettings(OutputFormat.png, 100, false), null); + string tmpFile = ImageOutput.SaveToTmpFile(surface, new SurfaceOutputSettings(OutputFormat.png, 100, false), null); string html = GetHtmlString(surface, tmpFile); dataObject.SetText(html, TextDataFormat.Html); } @@ -1081,11 +942,11 @@ EndSelection:<<<<<<<4 // Check if we can use the previously used image if (imageToSave.PixelFormat != PixelFormat.Format8bppIndexed) { - ImageIO.SaveToStream(imageToSave, surface, tmpPngStream, pngOutputSettings); + ImageOutput.SaveToStream(imageToSave, surface, tmpPngStream, pngOutputSettings); } else { - ImageIO.SaveToStream(surface, tmpPngStream, pngOutputSettings); + ImageOutput.SaveToStream(surface, tmpPngStream, pngOutputSettings); } html = GetHtmlDataUrlString(surface, tmpPngStream); @@ -1130,7 +991,7 @@ EndSelection:<<<<<<<4 private static byte[] BitmapToByteArray(Bitmap bitmap) { // Lock the bitmap's bits. - var rect = new NativeRect(0, 0, bitmap.Width, bitmap.Height); + Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height); BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat); int absStride = Math.Abs(bmpData.Stride); @@ -1264,15 +1125,15 @@ EndSelection:<<<<<<<4 public static IEnumerable GetImageFilenames(IDataObject dataObject) { string[] dropFileNames = (string[])dataObject.GetData(DataFormats.FileDrop); - if (dropFileNames is not { Length: > 0 }) return Enumerable.Empty(); - var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); - - var supportedExtensions = fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadFromStream).ToList(); - return dropFileNames - .Where(filename => !string.IsNullOrEmpty(filename)) - .Where(Path.HasExtension) - .Where(filename => supportedExtensions.Contains(Path.GetExtension(filename))); + if (dropFileNames != null && dropFileNames.Length > 0) + { + return dropFileNames + .Where(filename => !string.IsNullOrEmpty(filename)) + .Where(Path.HasExtension) + .Where(filename => ImageHelper.StreamConverters.Keys.Contains(Path.GetExtension(filename).ToLowerInvariant().Substring(1))); + } + return Enumerable.Empty(); } /// diff --git a/src/Greenshot.Base/Core/CoreConfiguration.cs b/src/Greenshot.Base/Core/CoreConfiguration.cs index 3e085bc9b..ead32a464 100644 --- a/src/Greenshot.Base/Core/CoreConfiguration.cs +++ b/src/Greenshot.Base/Core/CoreConfiguration.cs @@ -24,9 +24,9 @@ using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.IO; +using System.Linq; using System.Reflection; using System.Windows.Forms; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; @@ -59,7 +59,7 @@ namespace Greenshot.Base.Core [IniProperty("IEHotkey", Description = "Hotkey for starting the IE capture", DefaultValue = "Shift + Ctrl + PrintScreen")] public string IEHotkey { get; set; } - [IniProperty("ClipboardHotkey", Description = "Hotkey for opening the clipboard contents into the editor", ExcludeIfNull = true)] + [IniProperty("ClipboardHotkey", Description = "Hotkey for opening the clipboard contents into the editor")] public string ClipboardHotkey { get; set; } [IniProperty("IsFirstLaunch", Description = "Is this the first time launch?", DefaultValue = "true")] @@ -322,17 +322,17 @@ namespace Greenshot.Base.Core public bool ProcessEXIFOrientation { get; set; } [IniProperty("LastCapturedRegion", Description = "The last used region, for reuse in the capture last region")] - public NativeRect LastCapturedRegion { get; set; } + public Rectangle LastCapturedRegion { get; set; } [IniProperty("Win10BorderCrop", Description = "The capture is cropped with these settings, e.g. when you don't want to color around it -1,-1"), DefaultValue("0,0")] - public NativeSize Win10BorderCrop { get; set; } + public Size Win10BorderCrop { get; set; } - private NativeSize _iconSize; + private Size _iconSize; [IniProperty("BaseIconSize", Description = "Defines the base size of the icons (e.g. for the buttons in the editor), default value 16,16 and it's scaled to the current DPI", DefaultValue = "16,16")] - public NativeSize IconSize + public Size IconSize { get { return _iconSize; } set @@ -369,11 +369,13 @@ namespace Greenshot.Base.Core } } } - - [IniProperty("WebRequestTimeout", Description = "The connect timeout value for web requests, these are seconds", DefaultValue = "100")] + + public Size ScaledIconSize => DpiHelper.ScaleWithCurrentDpi(_iconSize); + + [IniProperty("WebRequestTimeout", Description = "The connect timeout value for webrequets, these are seconds", DefaultValue = "100")] public int WebRequestTimeout { get; set; } - [IniProperty("WebRequestReadWriteTimeout", Description = "The read/write timeout value for web requests, these are seconds", DefaultValue = "100")] + [IniProperty("WebRequestReadWriteTimeout", Description = "The read/write timeout value for webrequets, these are seconds", DefaultValue = "100")] public int WebRequestReadWriteTimeout { get; set; } public bool UseLargeIcons => IconSize.Width >= 32 || IconSize.Height >= 32; @@ -388,64 +390,89 @@ namespace Greenshot.Base.Core return ExperimentalFeatures != null && ExperimentalFeatures.Contains(experimentalFeature); } - private string CreateOutputFilePath() - { - if (IniConfig.IsPortable) - { - string pafOutputFilePath = Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots"); - if (!Directory.Exists(pafOutputFilePath)) - { - try - { - Directory.CreateDirectory(pafOutputFilePath); - return pafOutputFilePath; - } - catch (Exception ex) - { - // Problem creating directory, fallback to Desktop - LOG.Warn(ex); - } - } - else - { - return pafOutputFilePath; - } - } - - return Environment.GetFolderPath(Environment.SpecialFolder.Desktop); - } - /// /// Supply values we can't put as defaults /// /// The property to return a default for /// object with the default value for the supplied property - public override object GetDefault(string property) => - property switch + public override object GetDefault(string property) + { + switch (property) { - nameof(ExcludePlugins) => new List(), - nameof(IncludePlugins) => new List(), - nameof(OutputFileAsFullpath) => IniConfig.IsPortable ? Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots\dummy.png") : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "dummy.png"), - nameof(OutputFilePath) => CreateOutputFilePath(), - nameof(DWMBackgroundColor) => Color.Transparent, - nameof(ActiveTitleFixes) => new List { - "Firefox", - "IE", - "Chrome" - }, - nameof(TitleFixMatcher) => new Dictionary { - { "Firefox", " - Mozilla Firefox.*" }, - { "IE", " - (Microsoft|Windows) Internet Explorer.*" }, - { "Chrome", " - Google Chrome.*" } - }, - nameof(TitleFixReplacer) => new Dictionary { - { "Firefox", string.Empty }, - { "IE", string.Empty }, - { "Chrome", string.Empty } - }, - _ => null - }; - + case nameof(ExcludePlugins): + case nameof(IncludePlugins): + return new List(); + case nameof(OutputFileAsFullpath): + if (IniConfig.IsPortable) + { + return Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots\dummy.png"); + } + + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "dummy.png"); + case nameof(OutputFilePath): + if (IniConfig.IsPortable) + { + string pafOutputFilePath = Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots"); + if (!Directory.Exists(pafOutputFilePath)) + { + try + { + Directory.CreateDirectory(pafOutputFilePath); + return pafOutputFilePath; + } + catch (Exception ex) + { + LOG.Warn(ex); + // Problem creating directory, fallback to Desktop + } + } + else + { + return pafOutputFilePath; + } + } + + return Environment.GetFolderPath(Environment.SpecialFolder.Desktop); + case nameof(DWMBackgroundColor): + return Color.Transparent; + case nameof(ActiveTitleFixes): + return new List + { + "Firefox", + "IE", + "Chrome" + }; + case nameof(TitleFixMatcher): + return new Dictionary + { + { + "Firefox", " - Mozilla Firefox.*" + }, + { + "IE", " - (Microsoft|Windows) Internet Explorer.*" + }, + { + "Chrome", " - Google Chrome.*" + } + }; + case nameof(TitleFixReplacer): + return new Dictionary + { + { + "Firefox", string.Empty + }, + { + "IE", string.Empty + }, + { + "Chrome", string.Empty + } + }; + } + + return null; + } + /// /// This method will be called before converting the property, making to possible to correct a certain value /// Can be used when migration is needed @@ -515,9 +542,8 @@ namespace Greenshot.Base.Core OutputFileAutoReduceColors = false; } - bool isUpgradeFrom12 = LastSaveWithVersion?.StartsWith("1.2") ?? false; // Fix for excessive feed checking - if (UpdateCheckInterval != 0 && UpdateCheckInterval <= 7 && isUpgradeFrom12) + if (UpdateCheckInterval != 0 && UpdateCheckInterval <= 7 && LastSaveWithVersion.StartsWith("1.2")) { UpdateCheckInterval = 14; } diff --git a/src/Greenshot.Base/Core/DibHelper.cs b/src/Greenshot.Base/Core/DibHelper.cs new file mode 100644 index 000000000..59af37414 --- /dev/null +++ b/src/Greenshot.Base/Core/DibHelper.cs @@ -0,0 +1,134 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; +using Greenshot.Base.UnmanagedHelpers; + +namespace Greenshot.Base.Core +{ + /// + /// Though Greenshot implements the specs for the DIB image format, + /// it seems to cause a lot of issues when using the clipboard. + /// There is some research done about the DIB on the clipboard, this code is based upon the information + /// here + /// + internal static class DibHelper + { + private const double DpiToPelsPerMeter = 39.3701; + + /// + /// Converts the Bitmap to a Device Independent Bitmap format of type BITFIELDS. + /// + /// Bitmap to convert to DIB + /// byte{} with the image converted to DIB + public static byte[] ConvertToDib(this Bitmap sourceBitmap) + { + if (sourceBitmap == null) throw new ArgumentNullException(nameof(sourceBitmap)); + + var area = new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height); + + // If the supplied format doesn't match 32bpp, we need to convert it first, and dispose the new bitmap afterwards + bool needsDisposal = false; + if (sourceBitmap.PixelFormat != PixelFormat.Format32bppArgb) + { + needsDisposal = true; + var clonedImage = ImageHelper.CreateEmptyLike(sourceBitmap, Color.Transparent, PixelFormat.Format32bppArgb); + using (var graphics = Graphics.FromImage(clonedImage)) + { + graphics.DrawImage(sourceBitmap, area); + } + sourceBitmap = clonedImage; + } + + // All the pixels take this many bytes: + var bitmapSize = 4 * sourceBitmap.Width * sourceBitmap.Height; + // The bitmap info hear takes this many bytes: + var bitmapInfoHeaderSize = Marshal.SizeOf(typeof(BITMAPINFOHEADER)); + // The bitmap info size is the header + 3 RGBQUADs + var bitmapInfoSize = bitmapInfoHeaderSize + 3 * Marshal.SizeOf(typeof(RGBQUAD)); + + // Create a byte [] to contain the complete DIB (with .NET 5 and upwards, we could write the pixels directly to a stream) + var fullBmpBytes = new byte[bitmapInfoSize + bitmapSize]; + // Get a span for this, this simplifies the code a bit + var fullBmpSpan = fullBmpBytes.AsSpan(); + // Cast the span to be of type BITMAPINFOHEADER so we can assign values + // TODO: in .NET 6 we could do a AsRef, and even write to a stream directly + var bitmapInfoHeader = MemoryMarshal.Cast(fullBmpSpan); + + // Fill up the bitmap info header + bitmapInfoHeader[0].biSize = (uint)bitmapInfoHeaderSize; + bitmapInfoHeader[0].biWidth = sourceBitmap.Width; + bitmapInfoHeader[0].biHeight = sourceBitmap.Height; + bitmapInfoHeader[0].biPlanes = 1; + bitmapInfoHeader[0].biBitCount = 32; + bitmapInfoHeader[0].biCompression = BI_COMPRESSION.BI_BITFIELDS; + bitmapInfoHeader[0].biSizeImage = (uint)bitmapSize; + bitmapInfoHeader[0].biXPelsPerMeter = (int)(sourceBitmap.HorizontalResolution * DpiToPelsPerMeter); + bitmapInfoHeader[0].biYPelsPerMeter = (int)(sourceBitmap.VerticalResolution * DpiToPelsPerMeter); + + // Specify the color masks applied to the Int32 pixel value to get the R, G and B values. + var rgbQuads = MemoryMarshal.Cast(fullBmpSpan.Slice(Marshal.SizeOf(typeof(BITMAPINFOHEADER)))); + rgbQuads[0].rgbRed = 255; + rgbQuads[1].rgbGreen = 255; + rgbQuads[2].rgbBlue = 255; + + // Now copy the lines, in reverse (bmp is upside down) to the byte array + var sourceBitmapData = sourceBitmap.LockBits(area, ImageLockMode.ReadOnly, sourceBitmap.PixelFormat); + try + { + // Get a span for the real bitmap bytes, which starts after the bitmapinfo (header + 3xRGBQuad) + var bitmapSpan = fullBmpSpan.Slice(bitmapInfoSize); + // Make sure we also have a span to copy from, by taking the pointer from the locked bitmap + Span bitmapSourceSpan; + unsafe + { + bitmapSourceSpan = new Span(sourceBitmapData.Scan0.ToPointer(), sourceBitmapData.Stride * sourceBitmapData.Height); + } + + // Loop over all the bitmap lines + for (int destinationY = 0; destinationY < sourceBitmap.Height; destinationY++) + { + // Calculate the y coordinate for the bottom up. (flipping the image) + var sourceY = (sourceBitmap.Height - 1) - destinationY; + // Make a Span for the source bitmap pixels + var sourceLine = bitmapSourceSpan.Slice(sourceBitmapData.Stride * sourceY, 4 * sourceBitmap.Width); + // Make a Span for the destination dib pixels + var destinationLine = bitmapSpan.Slice(destinationY * 4 * sourceBitmap.Width); + sourceLine.CopyTo(destinationLine); + } + } + finally + { + sourceBitmap.UnlockBits(sourceBitmapData); + } + + // If we created a new bitmap, we need to dispose this + if (needsDisposal) + { + sourceBitmap.Dispose(); + } + return fullBmpBytes; + } + } +} diff --git a/src/Greenshot.Base/Core/DpiHelper.cs b/src/Greenshot.Base/Core/DpiHelper.cs new file mode 100644 index 000000000..d3c7e61b6 --- /dev/null +++ b/src/Greenshot.Base/Core/DpiHelper.cs @@ -0,0 +1,203 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using Greenshot.Base.Core.Enums; +using Greenshot.Base.UnmanagedHelpers; +using Greenshot.Base.UnmanagedHelpers.Enums; +using Greenshot.Base.UnmanagedHelpers.Structs; + +namespace Greenshot.Base.Core +{ + /// + /// This handles DPI changes see + /// Writing DPI-Aware Desktop and Win32 Applications + /// + public static class DpiHelper + { + /// + /// This is the default DPI for the screen + /// + public const uint DefaultScreenDpi = 96; + + /// + /// Retrieve the current DPI for the UI element which is related to this DpiHandler + /// + public static uint Dpi { get; private set; } = WindowsVersion.IsWindows10OrLater ? GetDpiForSystem() : DefaultScreenDpi; + + /// + /// Calculate a DPI scale factor + /// + /// uint + /// double + public static float DpiScaleFactor(uint dpi) + { + if (dpi == 0) + { + dpi = Dpi; + } + + return (float) dpi / DefaultScreenDpi; + } + + /// + /// Scale the supplied number according to the supplied dpi + /// + /// double with e.g. the width 16 for 16x16 images + /// current dpi, normal is 96. + /// A function which can modify the scale factor + /// double with the scaled number + public static float ScaleWithDpi(float someNumber, uint dpi, Func scaleModifier = null) + { + var dpiScaleFactor = DpiScaleFactor(dpi); + if (scaleModifier != null) + { + dpiScaleFactor = scaleModifier(dpiScaleFactor); + } + + return dpiScaleFactor * someNumber; + } + + /// + /// Scale the supplied Size according to the supplied dpi + /// + /// Size to resize + /// current dpi, normal is 96. + /// A function which can modify the scale factor + /// NativeSize scaled + public static Size ScaleWithDpi(Size size, uint dpi, Func scaleModifier = null) + { + var dpiScaleFactor = DpiScaleFactor(dpi); + if (scaleModifier != null) + { + dpiScaleFactor = scaleModifier(dpiScaleFactor); + } + + return new Size((int) (dpiScaleFactor * size.Width), (int) (dpiScaleFactor * size.Height)); + } + + /// + /// Scale the supplied NativeSize to the current dpi + /// + /// NativeSize to scale + /// A function which can modify the scale factor + /// NativeSize scaled + public static Size ScaleWithCurrentDpi(Size size, Func scaleModifier = null) + { + return ScaleWithDpi(size, Dpi, scaleModifier); + } + + /// + /// Return the DPI for the screen which the location is located on + /// + /// POINT + /// uint + public static uint GetDpi(POINT location) + { + if (!WindowsVersion.IsWindows81OrLater) + { + return DefaultScreenDpi; + } + RECT rect = new RECT(location.X, location.Y, 1, 1); + IntPtr hMonitor = User32.MonitorFromRect(ref rect, User32.MONITOR_DEFAULTTONEAREST); + var result = GetDpiForMonitor(hMonitor, MonitorDpiType.EffectiveDpi, out var dpiX, out var dpiY); + if (result.Succeeded()) + { + return dpiX; + } + + return DefaultScreenDpi; + } + + + /// + /// Retrieve the DPI value for the supplied window handle + /// + /// IntPtr + /// dpi value + public static uint GetDpi(IntPtr hWnd) + { + if (!User32.IsWindow(hWnd)) + { + return DefaultScreenDpi; + } + + // Use the easiest method, but this only works for Windows 10 + if (WindowsVersion.IsWindows10OrLater) + { + return GetDpiForWindow(hWnd); + } + + // Use the second easiest method, but this only works for Windows 8.1 or later + if (WindowsVersion.IsWindows81OrLater) + { + var hMonitor = User32.MonitorFromWindow(hWnd, MonitorFrom.DefaultToNearest); + // ReSharper disable once UnusedVariable + var result = GetDpiForMonitor(hMonitor, MonitorDpiType.EffectiveDpi, out var dpiX, out var dpiY); + if (result.Succeeded()) + { + return dpiX; + } + } + + // Fallback to the global DPI settings + using var hdc = SafeWindowDcHandle.FromWindow(hWnd); + if (hdc == null) + { + return DefaultScreenDpi; + } + + return (uint) GDI32.GetDeviceCaps(hdc, DeviceCaps.LOGPIXELSX); + } + + /// + /// See more at GetDpiForWindow function + /// Returns the dots per inch (dpi) value for the associated window. + /// + /// IntPtr + /// uint with dpi + [DllImport("user32.dll")] + private static extern uint GetDpiForWindow(IntPtr hWnd); + + /// + /// See + /// GetDpiForMonitor function + /// Queries the dots per inch (dpi) of a display. + /// + /// IntPtr + /// MonitorDpiType + /// out int for the horizontal dpi + /// out int for the vertical dpi + /// true if all okay + [DllImport("shcore.dll", SetLastError = true)] + private static extern HResult GetDpiForMonitor(IntPtr hMonitor, MonitorDpiType dpiType, out uint dpiX, out uint dpiY); + + /// + /// See GetDpiForSystem function + /// Returns the system DPI. + /// + /// uint with the system DPI + [DllImport("user32.dll")] + private static extern uint GetDpiForSystem(); + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/Core/EffectConverter.cs b/src/Greenshot.Base/Core/EffectConverter.cs index e5742b803..40cb1f5ae 100644 --- a/src/Greenshot.Base/Core/EffectConverter.cs +++ b/src/Greenshot.Base/Core/EffectConverter.cs @@ -3,8 +3,6 @@ using System.ComponentModel; using System.Drawing; using System.Globalization; using System.Text; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Effects; namespace Greenshot.Base.Core @@ -138,16 +136,16 @@ namespace Greenshot.Base.Core break; case "ShadowOffset": - NativePoint shadowOffset = new NativePoint(); + Point shadowOffset = new Point(); string[] coordinates = pair[1].Split(','); if (int.TryParse(coordinates[0], out var shadowOffsetX)) { - shadowOffset = shadowOffset.ChangeX(shadowOffsetX); + shadowOffset.X = shadowOffsetX; } if (int.TryParse(coordinates[1], out var shadowOffsetY)) { - shadowOffset = shadowOffset.ChangeY(shadowOffsetY); + shadowOffset.Y = shadowOffsetY; } effect.ShadowOffset = shadowOffset; diff --git a/src/Greenshot.Base/Core/Enums/HResult.cs b/src/Greenshot.Base/Core/Enums/HResult.cs new file mode 100644 index 000000000..a971c8801 --- /dev/null +++ b/src/Greenshot.Base/Core/Enums/HResult.cs @@ -0,0 +1,33 @@ +// Greenshot - a free and open source screenshot tool +// Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom +// +// For more information see: https://getgreenshot.org/ +// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 1 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System.Diagnostics.CodeAnalysis; + +namespace Greenshot.Base.Core.Enums +{ + /// + /// The HRESULT represents Windows error codes + /// See wikipedia + /// + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum HResult + { + S_OK = 0, + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/Core/Enums/MonitorDpiType.cs b/src/Greenshot.Base/Core/Enums/MonitorDpiType.cs new file mode 100644 index 000000000..e434f5bb3 --- /dev/null +++ b/src/Greenshot.Base/Core/Enums/MonitorDpiType.cs @@ -0,0 +1,41 @@ +// Copyright (c) Dapplo and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace Greenshot.Base.Core.Enums +{ + /// + /// See + /// + /// MONITOR_DPI_TYPE + /// enumeration + /// + /// + [Flags] + public enum MonitorDpiType + { + /// + /// The effective DPI. + /// This value should be used when determining the correct scale factor for scaling UI elements. + /// This incorporates the scale factor set by the user for this specific display. + /// + EffectiveDpi = 0, + + /// + /// The angular DPI. + /// This DPI ensures rendering at a compliant angular resolution on the screen. + /// This does not include the scale factor set by the user for this specific display + /// + AngularDpi = 1, + + /// + /// The raw DPI. + /// This value is the linear DPI of the screen as measured on the screen itself. + /// Use this value when you want to read the pixel density and not the recommended scaling setting. + /// This does not include the scale factor set by the user for this specific display and is not guaranteed to be a + /// supported DPI value. + /// + RawDpi = 2 + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/Core/Enums/MonitorFrom.cs b/src/Greenshot.Base/Core/Enums/MonitorFrom.cs new file mode 100644 index 000000000..f9d9fcc2b --- /dev/null +++ b/src/Greenshot.Base/Core/Enums/MonitorFrom.cs @@ -0,0 +1,31 @@ +// Copyright (c) Dapplo and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace Greenshot.Base.Core.Enums +{ + /// + /// Flags for the MonitorFromRect / MonitorFromWindow "flags" field + /// see MonitorFromRect function + /// or see MonitorFromWindow function + /// + [Flags] + public enum MonitorFrom : uint + { + /// + /// Returns a handle to the display monitor that is nearest to the rectangle. + /// + DefaultToNearest = 0, + + /// + /// Returns NULL. (why??) + /// + DefaultToNull = 1, + + /// + /// Returns a handle to the primary display monitor. + /// + DefaultToPrimary = 2 + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/Core/Enums/OutputFormat.cs b/src/Greenshot.Base/Core/Enums/OutputFormat.cs index 6e2353eb8..64d8a614b 100644 --- a/src/Greenshot.Base/Core/Enums/OutputFormat.cs +++ b/src/Greenshot.Base/Core/Enums/OutputFormat.cs @@ -31,7 +31,6 @@ namespace Greenshot.Base.Core.Enums jpg, png, tiff, - jxr, greenshot, ico } diff --git a/src/Greenshot.Base/Core/Enums/PrintWindowFlags.cs b/src/Greenshot.Base/Core/Enums/PrintWindowFlags.cs new file mode 100644 index 000000000..6d18cdbe6 --- /dev/null +++ b/src/Greenshot.Base/Core/Enums/PrintWindowFlags.cs @@ -0,0 +1,38 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; + +namespace Greenshot.Base.Core.Enums +{ + [Flags] + public enum PrintWindowFlags : uint + { + /// Render the entire window. + PW_ENTIREWINDOW = 0, + + /// Only the client area of the window is copied to hdcBlt. By default, the entire window is copied. + PW_CLIENTONLY = 1, + + /// Undocumented + PW_RENDERFULLCONTENT = 0x00000002, + } +} diff --git a/src/Greenshot.Base/Core/EnvironmentInfo.cs b/src/Greenshot.Base/Core/EnvironmentInfo.cs index 30811fb69..b1e907912 100644 --- a/src/Greenshot.Base/Core/EnvironmentInfo.cs +++ b/src/Greenshot.Base/Core/EnvironmentInfo.cs @@ -23,12 +23,8 @@ using System; using System.Reflection; using System.Runtime.InteropServices; using System.Text; -using Dapplo.Windows.Kernel32; -using Dapplo.Windows.Kernel32.Enums; -using Dapplo.Windows.Kernel32.Structs; -using Dapplo.Windows.User32; -using Dapplo.Windows.Common.Extensions; using Greenshot.Base.IniFile; +using Greenshot.Base.UnmanagedHelpers; using Microsoft.Win32; namespace Greenshot.Base.Core @@ -167,7 +163,7 @@ namespace Greenshot.Base.Core } // Get some important information for fixing GDI related Problems - environment.AppendFormat("GDI object count: {0}", User32Api.GetGuiResourcesGdiCount()); + environment.AppendFormat("GDI object count: {0}", User32.GetGuiResourcesGDICount()); if (newline) { environment.AppendLine(); @@ -177,7 +173,7 @@ namespace Greenshot.Base.Core environment.Append(", "); } - environment.AppendFormat("User object count: {0}", User32Api.GetGuiResourcesUserCount()); + environment.AppendFormat("User object count: {0}", User32.GetGuiResourcesUserCount()); } else { @@ -295,33 +291,33 @@ namespace Greenshot.Base.Core string edition = string.Empty; OperatingSystem osVersion = Environment.OSVersion; - var osVersionInfo = OsVersionInfoEx.Create(); + OSVERSIONINFOEX osVersionInfo = OSVERSIONINFOEX.Create(); - if (Kernel32Api.GetVersionEx(ref osVersionInfo)) + if (GetVersionEx(ref osVersionInfo)) { int majorVersion = osVersion.Version.Major; int minorVersion = osVersion.Version.Minor; - var productType = osVersionInfo.ProductType; - var suiteMask = osVersionInfo.SuiteMask; + byte productType = osVersionInfo.ProductType; + ushort suiteMask = osVersionInfo.SuiteMask; if (majorVersion == 4) { - if (productType == WindowsProductTypes.VER_NT_WORKSTATION) + if (productType == VER_NT_WORKSTATION) { // Windows NT 4.0 Workstation edition = "Workstation"; } - else if (productType == WindowsProductTypes.VER_NT_SERVER) + else if (productType == VER_NT_SERVER) { - edition = (suiteMask & WindowsSuites.Enterprise) != 0 ? "Enterprise Server" : "Standard Server"; + edition = (suiteMask & VER_SUITE_ENTERPRISE) != 0 ? "Enterprise Server" : "Standard Server"; } } else if (majorVersion == 5) { - if (productType == WindowsProductTypes.VER_NT_WORKSTATION) + if (productType == VER_NT_WORKSTATION) { - if ((suiteMask & WindowsSuites.Personal) != 0) + if ((suiteMask & VER_SUITE_PERSONAL) != 0) { // Windows XP Home Edition edition = "Home"; @@ -332,16 +328,16 @@ namespace Greenshot.Base.Core edition = "Professional"; } } - else if (productType == WindowsProductTypes.VER_NT_SERVER) + else if (productType == VER_NT_SERVER) { if (minorVersion == 0) { - if ((suiteMask & WindowsSuites.DataCenter) != 0) + if ((suiteMask & VER_SUITE_DATACENTER) != 0) { // Windows 2000 Datacenter Server edition = "Datacenter Server"; } - else if ((suiteMask & WindowsSuites.Enterprise) != 0) + else if ((suiteMask & VER_SUITE_ENTERPRISE) != 0) { // Windows 2000 Advanced Server edition = "Advanced Server"; @@ -354,17 +350,17 @@ namespace Greenshot.Base.Core } else { - if ((suiteMask & WindowsSuites.DataCenter) != 0) + if ((suiteMask & VER_SUITE_DATACENTER) != 0) { // Windows Server 2003 Datacenter Edition edition = "Datacenter"; } - else if ((suiteMask & WindowsSuites.Enterprise) != 0) + else if ((suiteMask & VER_SUITE_ENTERPRISE) != 0) { // Windows Server 2003 Enterprise Edition edition = "Enterprise"; } - else if ((suiteMask & WindowsSuites.Blade) != 0) + else if ((suiteMask & VER_SUITE_BLADE) != 0) { // Windows Server 2003 Web Edition edition = "Web Edition"; @@ -380,9 +376,122 @@ namespace Greenshot.Base.Core else if (majorVersion == 6) { - if (Kernel32Api.GetProductInfo(majorVersion, minorVersion, osVersionInfo.ServicePackMajor, osVersionInfo.ServicePackMinor, out var windowsProduct)) + if (GetProductInfo(majorVersion, minorVersion, osVersionInfo.ServicePackMajor, osVersionInfo.ServicePackMinor, out var ed)) { - edition = windowsProduct.GetEnumDescription(); + switch (ed) + { + case PRODUCT_BUSINESS: + edition = "Business"; + break; + case PRODUCT_BUSINESS_N: + edition = "Business N"; + break; + case PRODUCT_CLUSTER_SERVER: + edition = "HPC Edition"; + break; + case PRODUCT_DATACENTER_SERVER: + edition = "Datacenter Server"; + break; + case PRODUCT_DATACENTER_SERVER_CORE: + edition = "Datacenter Server (core installation)"; + break; + case PRODUCT_ENTERPRISE: + edition = "Enterprise"; + break; + case PRODUCT_ENTERPRISE_N: + edition = "Enterprise N"; + break; + case PRODUCT_ENTERPRISE_SERVER: + edition = "Enterprise Server"; + break; + case PRODUCT_ENTERPRISE_SERVER_CORE: + edition = "Enterprise Server (core installation)"; + break; + case PRODUCT_ENTERPRISE_SERVER_CORE_V: + edition = "Enterprise Server without Hyper-V (core installation)"; + break; + case PRODUCT_ENTERPRISE_SERVER_IA64: + edition = "Enterprise Server for Itanium-based Systems"; + break; + case PRODUCT_ENTERPRISE_SERVER_V: + edition = "Enterprise Server without Hyper-V"; + break; + case PRODUCT_HOME_BASIC: + edition = "Home Basic"; + break; + case PRODUCT_HOME_BASIC_N: + edition = "Home Basic N"; + break; + case PRODUCT_HOME_PREMIUM: + edition = "Home Premium"; + break; + case PRODUCT_HOME_PREMIUM_N: + edition = "Home Premium N"; + break; + case PRODUCT_HYPERV: + edition = "Microsoft Hyper-V Server"; + break; + case PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT: + edition = "Windows Essential Business Management Server"; + break; + case PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING: + edition = "Windows Essential Business Messaging Server"; + break; + case PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY: + edition = "Windows Essential Business Security Server"; + break; + case PRODUCT_SERVER_FOR_SMALLBUSINESS: + edition = "Windows Essential Server Solutions"; + break; + case PRODUCT_SERVER_FOR_SMALLBUSINESS_V: + edition = "Windows Essential Server Solutions without Hyper-V"; + break; + case PRODUCT_SMALLBUSINESS_SERVER: + edition = "Windows Small Business Server"; + break; + case PRODUCT_STANDARD_SERVER: + edition = "Standard Server"; + break; + case PRODUCT_STANDARD_SERVER_CORE: + edition = "Standard Server (core installation)"; + break; + case PRODUCT_STANDARD_SERVER_CORE_V: + edition = "Standard Server without Hyper-V (core installation)"; + break; + case PRODUCT_STANDARD_SERVER_V: + edition = "Standard Server without Hyper-V"; + break; + case PRODUCT_STARTER: + edition = "Starter"; + break; + case PRODUCT_STORAGE_ENTERPRISE_SERVER: + edition = "Enterprise Storage Server"; + break; + case PRODUCT_STORAGE_EXPRESS_SERVER: + edition = "Express Storage Server"; + break; + case PRODUCT_STORAGE_STANDARD_SERVER: + edition = "Standard Storage Server"; + break; + case PRODUCT_STORAGE_WORKGROUP_SERVER: + edition = "Workgroup Storage Server"; + break; + case PRODUCT_UNDEFINED: + edition = "Unknown product"; + break; + case PRODUCT_ULTIMATE: + edition = "Ultimate"; + break; + case PRODUCT_ULTIMATE_N: + edition = "Ultimate N"; + break; + case PRODUCT_WEB_SERVER: + edition = "Web Server"; + break; + case PRODUCT_WEB_SERVER_CORE: + edition = "Web Server (core installation)"; + break; + } } } } @@ -409,13 +518,13 @@ namespace Greenshot.Base.Core string name = "unknown"; OperatingSystem osVersion = Environment.OSVersion; - var osVersionInfo = OsVersionInfoEx.Create(); - if (Kernel32Api.GetVersionEx(ref osVersionInfo)) + OSVERSIONINFOEX osVersionInfo = OSVERSIONINFOEX.Create(); + if (GetVersionEx(ref osVersionInfo)) { int majorVersion = osVersion.Version.Major; int minorVersion = osVersion.Version.Minor; - var productType = osVersionInfo.ProductType; - var suiteMask = osVersionInfo.SuiteMask; + byte productType = osVersionInfo.ProductType; + ushort suiteMask = osVersionInfo.SuiteMask; switch (osVersion.Platform) { case PlatformID.Win32Windows: @@ -454,10 +563,10 @@ namespace Greenshot.Base.Core case 4: switch (productType) { - case WindowsProductTypes.VER_NT_WORKSTATION: + case 1: name = "Windows NT 4.0"; break; - case WindowsProductTypes.VER_NT_SERVER: + case 3: name = "Windows NT 4.0 Server"; break; } @@ -472,18 +581,18 @@ namespace Greenshot.Base.Core case 1: name = suiteMask switch { - WindowsSuites.Personal => "Windows XP Professional", + 0x0200 => "Windows XP Professional", _ => "Windows XP" }; break; case 2: name = suiteMask switch { - WindowsSuites.Personal => "Windows XP Professional x64", - WindowsSuites.Enterprise => "Windows Server 2003 Enterprise", - WindowsSuites.DataCenter => "Windows Server 2003 Data Center", - WindowsSuites.Blade => "Windows Server 2003 Web Edition", - WindowsSuites.WHServer => "Windows Home Server", + 0x0200 => "Windows XP Professional x64", + 0x0002 => "Windows Server 2003 Enterprise", + 0x0080 => "Windows Server 2003 Data Center", + 0x0400 => "Windows Server 2003 Web Edition", + 0x8000 => "Windows Home Server", _ => "Windows Server 2003" }; break; @@ -496,14 +605,14 @@ namespace Greenshot.Base.Core case 0: name = productType switch { - WindowsProductTypes.VER_NT_SERVER => "Windows Server 2008", + 3 => "Windows Server 2008", _ => "Windows Vista" }; break; case 1: name = productType switch { - WindowsProductTypes.VER_NT_SERVER => "Windows Server 2008 R2", + 3 => "Windows Server 2008 R2", _ => "Windows 7" }; break; @@ -531,6 +640,134 @@ namespace Greenshot.Base.Core } } + [DllImport("Kernel32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool GetProductInfo( + int osMajorVersion, + int osMinorVersion, + int spMajorVersion, + int spMinorVersion, + out int edition); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool GetVersionEx(ref OSVERSIONINFOEX osVersionInfo); + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + private unsafe struct OSVERSIONINFOEX + { + /// + /// The size of this data structure, in bytes. Set this member to sizeof(OSVERSIONINFOEX). + /// + private int _dwOSVersionInfoSize; + + private readonly int _dwMajorVersion; + private readonly int _dwMinorVersion; + private readonly int _dwBuildNumber; + private readonly int _dwPlatformId; + private fixed char _szCSDVersion[128]; + private readonly short _wServicePackMajor; + private readonly short _wServicePackMinor; + private readonly ushort _wSuiteMask; + private readonly byte _wProductType; + private readonly byte _wReserved; + + /// A null-terminated string, such as "Service Pack 3", that indicates the latest Service Pack installed on the system. + /// If no Service Pack has been installed, the string is empty. + /// + public string ServicePackVersion + { + get + { + fixed (char* servicePackVersion = _szCSDVersion) + { + return new string(servicePackVersion); + } + } + } + + /// + /// The major version number of the latest Service Pack installed on the system. For example, for Service Pack 3, the + /// major version number is 3. + /// If no Service Pack has been installed, the value is zero. + /// + public short ServicePackMajor => _wServicePackMajor; + + /// + /// The minor version number of the latest Service Pack installed on the system. For example, for Service Pack 3, the + /// minor version number is 0. + /// + public short ServicePackMinor => _wServicePackMinor; + + /// + /// A bit mask that identifies the product suites available on the system. This member can be a combination of the + /// following values. + /// + public ushort SuiteMask => _wSuiteMask; + + /// + /// Any additional information about the system. + /// + public byte ProductType => _wProductType; + + /// + /// Factory for an empty OsVersionInfoEx + /// + /// OSVERSIONINFOEX + public static OSVERSIONINFOEX Create() + { + return new OSVERSIONINFOEX + { + _dwOSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEX)) + }; + } + } + + private const int PRODUCT_UNDEFINED = 0x00000000; + private const int PRODUCT_ULTIMATE = 0x00000001; + private const int PRODUCT_HOME_BASIC = 0x00000002; + private const int PRODUCT_HOME_PREMIUM = 0x00000003; + private const int PRODUCT_ENTERPRISE = 0x00000004; + private const int PRODUCT_HOME_BASIC_N = 0x00000005; + private const int PRODUCT_BUSINESS = 0x00000006; + private const int PRODUCT_STANDARD_SERVER = 0x00000007; + private const int PRODUCT_DATACENTER_SERVER = 0x00000008; + private const int PRODUCT_SMALLBUSINESS_SERVER = 0x00000009; + private const int PRODUCT_ENTERPRISE_SERVER = 0x0000000A; + private const int PRODUCT_STARTER = 0x0000000B; + private const int PRODUCT_DATACENTER_SERVER_CORE = 0x0000000C; + private const int PRODUCT_STANDARD_SERVER_CORE = 0x0000000D; + private const int PRODUCT_ENTERPRISE_SERVER_CORE = 0x0000000E; + private const int PRODUCT_ENTERPRISE_SERVER_IA64 = 0x0000000F; + private const int PRODUCT_BUSINESS_N = 0x00000010; + private const int PRODUCT_WEB_SERVER = 0x00000011; + private const int PRODUCT_CLUSTER_SERVER = 0x00000012; + private const int PRODUCT_STORAGE_EXPRESS_SERVER = 0x00000014; + private const int PRODUCT_STORAGE_STANDARD_SERVER = 0x00000015; + private const int PRODUCT_STORAGE_WORKGROUP_SERVER = 0x00000016; + private const int PRODUCT_STORAGE_ENTERPRISE_SERVER = 0x00000017; + private const int PRODUCT_SERVER_FOR_SMALLBUSINESS = 0x00000018; + private const int PRODUCT_HOME_PREMIUM_N = 0x0000001A; + private const int PRODUCT_ENTERPRISE_N = 0x0000001B; + private const int PRODUCT_ULTIMATE_N = 0x0000001C; + private const int PRODUCT_WEB_SERVER_CORE = 0x0000001D; + private const int PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT = 0x0000001E; + private const int PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY = 0x0000001F; + private const int PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING = 0x00000020; + private const int PRODUCT_SERVER_FOR_SMALLBUSINESS_V = 0x00000023; + private const int PRODUCT_STANDARD_SERVER_V = 0x00000024; + private const int PRODUCT_ENTERPRISE_SERVER_V = 0x00000026; + private const int PRODUCT_STANDARD_SERVER_CORE_V = 0x00000028; + private const int PRODUCT_ENTERPRISE_SERVER_CORE_V = 0x00000029; + private const int PRODUCT_HYPERV = 0x0000002A; + + private const int VER_NT_WORKSTATION = 1; + private const int VER_NT_SERVER = 3; + private const int VER_SUITE_ENTERPRISE = 2; + private const int VER_SUITE_DATACENTER = 128; + private const int VER_SUITE_PERSONAL = 512; + private const int VER_SUITE_BLADE = 1024; + /// /// Gets the service pack information of the operating system running on this computer. /// @@ -539,9 +776,9 @@ namespace Greenshot.Base.Core get { string servicePack = string.Empty; - OsVersionInfoEx osVersionInfo = OsVersionInfoEx.Create(); + OSVERSIONINFOEX osVersionInfo = OSVERSIONINFOEX.Create(); - if (Kernel32Api.GetVersionEx(ref osVersionInfo)) + if (GetVersionEx(ref osVersionInfo)) { servicePack = osVersionInfo.ServicePackVersion; } @@ -550,7 +787,6 @@ namespace Greenshot.Base.Core } } - /// /// Gets the full version string of the operating system running on this computer. /// public static string VersionString diff --git a/src/Greenshot.Base/Core/FastBitmap.cs b/src/Greenshot.Base/Core/FastBitmap.cs index c0309763f..3875e6203 100644 --- a/src/Greenshot.Base/Core/FastBitmap.cs +++ b/src/Greenshot.Base/Core/FastBitmap.cs @@ -23,8 +23,6 @@ using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; namespace Greenshot.Base.Core { @@ -86,7 +84,7 @@ namespace Greenshot.Base.Core /// /// Size of the underlying image /// - NativeSize Size { get; } + Size Size { get; } /// /// Height of the image area that this fastbitmap covers @@ -129,19 +127,19 @@ namespace Greenshot.Base.Core bool HasAlphaChannel { get; } /// - /// Draw the stored bitmap to the destination bitmap at the supplied point + /// Draw the stored bitmap to the destionation bitmap at the supplied point /// /// Graphics - /// NativePoint with location - void DrawTo(Graphics graphics, NativePoint destination); + /// Point with location + void DrawTo(Graphics graphics, Point destination); /// /// Draw the stored Bitmap on the Destination bitmap with the specified rectangle /// Be aware that the stored bitmap will be resized to the specified rectangle!! /// /// Graphics - /// NativeRect with destination - void DrawTo(Graphics graphics, NativeRect destinationRect); + /// Rectangle with destination + void DrawTo(Graphics graphics, Rectangle destinationRect); /// /// Return true if the coordinates are inside the FastBitmap @@ -216,7 +214,7 @@ namespace Greenshot.Base.Core /// public interface IFastBitmapWithClip : IFastBitmap { - NativeRect Clip { get; set; } + Rectangle Clip { get; set; } bool InvertClip { get; set; } @@ -269,14 +267,14 @@ namespace Greenshot.Base.Core public const int ColorIndexB = 2; public const int ColorIndexA = 3; - protected NativeRect Area; + protected Rectangle Area; /// /// If this is set to true, the bitmap will be disposed when disposing the IFastBitmap /// public bool NeedsDispose { get; set; } - public NativeRect Clip { get; set; } + public Rectangle Clip { get; set; } public bool InvertClip { get; set; } @@ -292,7 +290,7 @@ namespace Greenshot.Base.Core public static IFastBitmap Create(Bitmap source) { - return Create(source, NativeRect.Empty); + return Create(source, Rectangle.Empty); } public void SetResolution(float horizontal, float vertical) @@ -305,37 +303,44 @@ namespace Greenshot.Base.Core /// The supplied rectangle specifies the area for which the FastBitmap does its thing /// /// Bitmap to access - /// NativeRect which specifies the area to have access to, can be NativeRect.Empty for the whole image + /// Rectangle which specifies the area to have access to, can be Rectangle.Empty for the whole image /// IFastBitmap - public static IFastBitmap Create(Bitmap source, NativeRect area) => - source.PixelFormat switch - { - PixelFormat.Format8bppIndexed => new FastChunkyBitmap(source, area), - PixelFormat.Format24bppRgb => new Fast24RgbBitmap(source, area), - PixelFormat.Format32bppRgb => new Fast32RgbBitmap(source, area), - PixelFormat.Format32bppArgb => new Fast32ArgbBitmap(source, area), - PixelFormat.Format32bppPArgb => new Fast32ArgbBitmap(source, area), - _ => throw new NotSupportedException($"Not supported PixelFormat {source.PixelFormat}") - }; - - /// - /// Factory for creating a FastBitmap as a destination for the source - /// - /// Bitmap to clone - /// new PixelFormat - /// IFastBitmap - public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat) + public static IFastBitmap Create(Bitmap source, Rectangle area) { - return CreateCloneOf(source, pixelFormat, NativeRect.Empty); + switch (source.PixelFormat) + { + case PixelFormat.Format8bppIndexed: + return new FastChunkyBitmap(source, area); + case PixelFormat.Format24bppRgb: + return new Fast24RgbBitmap(source, area); + case PixelFormat.Format32bppRgb: + return new Fast32RgbBitmap(source, area); + case PixelFormat.Format32bppArgb: + case PixelFormat.Format32bppPArgb: + return new Fast32ArgbBitmap(source, area); + default: + throw new NotSupportedException($"Not supported Pixelformat {source.PixelFormat}"); + } } /// /// Factory for creating a FastBitmap as a destination for the source /// /// Bitmap to clone - /// Area of the bitmap to access, can be NativeRect.Empty for the whole + /// new Pixelformat /// IFastBitmap - public static IFastBitmap CreateCloneOf(Image source, NativeRect area) + public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat) + { + return CreateCloneOf(source, pixelFormat, Rectangle.Empty); + } + + /// + /// Factory for creating a FastBitmap as a destination for the source + /// + /// Bitmap to clone + /// Area of the bitmap to access, can be Rectangle.Empty for the whole + /// IFastBitmap + public static IFastBitmap CreateCloneOf(Image source, Rectangle area) { return CreateCloneOf(source, PixelFormat.DontCare, area); } @@ -345,9 +350,9 @@ namespace Greenshot.Base.Core /// /// Bitmap to clone /// Pixelformat of the cloned bitmap - /// Area of the bitmap to access, can be NativeRect.Empty for the whole + /// Area of the bitmap to access, can be Rectangle.Empty for the whole /// IFastBitmap - public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat, NativeRect area) + public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat, Rectangle area) { Bitmap destination = ImageHelper.CloneArea(source, area, pixelFormat); FastBitmap fastBitmap = Create(destination) as FastBitmap; @@ -364,11 +369,11 @@ namespace Greenshot.Base.Core /// /// Factory for creating a FastBitmap as a destination /// - /// NativeSize - /// PixelFormat - /// Color + /// + /// + /// /// IFastBitmap - public static IFastBitmap CreateEmpty(NativeSize newSize, PixelFormat pixelFormat, Color backgroundColor) + public static IFastBitmap CreateEmpty(Size newSize, PixelFormat pixelFormat, Color backgroundColor) { Bitmap destination = ImageHelper.CreateEmpty(newSize.Width, newSize.Height, pixelFormat, backgroundColor, 96f, 96f); IFastBitmap fastBitmap = Create(destination); @@ -380,14 +385,14 @@ namespace Greenshot.Base.Core /// Constructor which stores the image and locks it when called /// /// Bitmap - /// NativeRect - protected FastBitmap(Bitmap bitmap, NativeRect area) + /// Rectangle + protected FastBitmap(Bitmap bitmap, Rectangle area) { Bitmap = bitmap; - var bitmapArea = new NativeRect(NativePoint.Empty, bitmap.Size); - if (area != NativeRect.Empty) + Rectangle bitmapArea = new Rectangle(Point.Empty, bitmap.Size); + if (area != Rectangle.Empty) { - area = area.Intersect(bitmapArea); + area.Intersect(bitmapArea); Area = area; } else @@ -408,11 +413,11 @@ namespace Greenshot.Base.Core /// /// Return the size of the image /// - public NativeSize Size + public Size Size { get { - if (Area == NativeRect.Empty) + if (Area == Rectangle.Empty) { return Bitmap.Size; } @@ -428,7 +433,7 @@ namespace Greenshot.Base.Core { get { - if (Area == NativeRect.Empty) + if (Area == Rectangle.Empty) { return Bitmap.Width; } @@ -444,7 +449,7 @@ namespace Greenshot.Base.Core { get { - if (Area == NativeRect.Empty) + if (Area == Rectangle.Empty) { return Bitmap.Height; } @@ -591,13 +596,13 @@ namespace Greenshot.Base.Core } /// - /// Draw the stored bitmap to the destination bitmap at the supplied point + /// Draw the stored bitmap to the destionation bitmap at the supplied point /// /// /// - public void DrawTo(Graphics graphics, NativePoint destination) + public void DrawTo(Graphics graphics, Point destination) { - DrawTo(graphics, new NativeRect(destination, Area.Size)); + DrawTo(graphics, new Rectangle(destination, Area.Size)); } /// @@ -605,8 +610,8 @@ namespace Greenshot.Base.Core /// Be aware that the stored bitmap will be resized to the specified rectangle!! /// /// - /// NativeRect - public void DrawTo(Graphics graphics, NativeRect destinationRect) + /// + public void DrawTo(Graphics graphics, Rectangle destinationRect) { // Make sure this.bitmap is unlocked, if it was locked bool isLocked = BitsLocked; @@ -710,7 +715,7 @@ namespace Greenshot.Base.Core } /// - /// This is the implementation of the FastBitmap for the 8BPP pixelformat + /// This is the implementation of the FastBitmat for the 8BPP pixelformat /// public unsafe class FastChunkyBitmap : FastBitmap { @@ -718,7 +723,7 @@ namespace Greenshot.Base.Core private readonly Color[] _colorEntries; private readonly Dictionary _colorCache = new Dictionary(); - public FastChunkyBitmap(Bitmap source, NativeRect area) : base(source, area) + public FastChunkyBitmap(Bitmap source, Rectangle area) : base(source, area) { _colorEntries = Bitmap.Palette.Entries; } @@ -820,7 +825,7 @@ namespace Greenshot.Base.Core /// public unsafe class Fast24RgbBitmap : FastBitmap { - public Fast24RgbBitmap(Bitmap source, NativeRect area) : base(source, area) + public Fast24RgbBitmap(Bitmap source, Rectangle area) : base(source, area) { } @@ -886,7 +891,7 @@ namespace Greenshot.Base.Core /// public unsafe class Fast32RgbBitmap : FastBitmap { - public Fast32RgbBitmap(Bitmap source, NativeRect area) : base(source, area) + public Fast32RgbBitmap(Bitmap source, Rectangle area) : base(source, area) { } @@ -956,7 +961,7 @@ namespace Greenshot.Base.Core public Color BackgroundBlendColor { get; set; } - public Fast32ArgbBitmap(Bitmap source, NativeRect area) : base(source, area) + public Fast32ArgbBitmap(Bitmap source, Rectangle area) : base(source, area) { BackgroundBlendColor = Color.White; } diff --git a/src/Greenshot.Base/Core/FileDescriptor.cs b/src/Greenshot.Base/Core/FileDescriptor.cs index 750cbab4d..5306cf3b8 100644 --- a/src/Greenshot.Base/Core/FileDescriptor.cs +++ b/src/Greenshot.Base/Core/FileDescriptor.cs @@ -22,7 +22,7 @@ using System; using System.IO; using System.Text; -using Dapplo.Windows.Common.Structs; +using Greenshot.Base.UnmanagedHelpers.Structs; namespace Greenshot.Base.Core { @@ -30,8 +30,8 @@ namespace Greenshot.Base.Core { public FileDescriptorFlags Flags { get; set; } public Guid ClassId { get; set; } - public NativeSize Size { get; set; } - public NativePoint Point { get; set; } + public SIZE Size { get; set; } + public POINT Point { get; set; } public FileAttributes FileAttributes { get; set; } public DateTime CreationTime { get; set; } public DateTime LastAccessTime { get; set; } @@ -46,9 +46,9 @@ namespace Greenshot.Base.Core //ClassID ClassId = new Guid(reader.ReadBytes(16)); //Size - Size = new NativeSize(reader.ReadInt32(), reader.ReadInt32()); + Size = new SIZE(reader.ReadInt32(), reader.ReadInt32()); //Point - Point = new NativePoint(reader.ReadInt32(), reader.ReadInt32()); + Point = new POINT(reader.ReadInt32(), reader.ReadInt32()); //FileAttributes FileAttributes = (FileAttributes)reader.ReadUInt32(); //CreationTime diff --git a/src/Greenshot.Base/Core/FileFormatHandlers/FileFormatHandlerExtensions.cs b/src/Greenshot.Base/Core/FileFormatHandlers/FileFormatHandlerExtensions.cs deleted file mode 100644 index 8941c7184..000000000 --- a/src/Greenshot.Base/Core/FileFormatHandlers/FileFormatHandlerExtensions.cs +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System.Collections.Generic; -using System.Drawing; -using System.IO; -using System.Linq; -using Greenshot.Base.Interfaces; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Base.Interfaces.Plugin; - -namespace Greenshot.Base.Core.FileFormatHandlers -{ - /// - /// This is the registry where all IFileFormatHandler are registered and can be used - /// - public static class FileFormatHandlerExtensions - { - /// - /// Make sure we handle the input extension always the same, by "normalizing" it - /// - /// string - /// string - public static string NormalizeExtension(string extension) - { - if (string.IsNullOrEmpty(extension)) - { - return null; - } - - extension = extension.ToLowerInvariant(); - return !extension.StartsWith(".") ? $".{extension}" : extension; - } - - /// - /// Return the extensions that the provided IFileFormatHandlers can accept for the specified action - /// - /// IEnumerable{IFileFormatHandler} - /// - /// - public static IEnumerable ExtensionsFor(this IEnumerable fileFormatHandlers, FileFormatHandlerActions fileFormatHandlerAction) - { - return fileFormatHandlers.Where(ffh => ffh.SupportedExtensions.ContainsKey(fileFormatHandlerAction)).SelectMany(ffh => ffh.SupportedExtensions[fileFormatHandlerAction]).Distinct().OrderBy(e => e); - } - - /// - /// Extension method to check if a certain IFileFormatHandler supports a certain action with a specific extension - /// - /// IFileFormatHandler - /// FileFormatHandlerActions - /// string - /// bool - public static bool Supports(this IFileFormatHandler fileFormatHandler, FileFormatHandlerActions fileFormatHandlerAction, string extension) - { - extension = NormalizeExtension(extension); - return fileFormatHandler.SupportedExtensions.ContainsKey(fileFormatHandlerAction) && fileFormatHandler.SupportedExtensions[fileFormatHandlerAction].Contains(extension); - } - - /// - /// This wrapper method for TrySaveToStream will do: - /// Find all the IFileFormatHandler which support the action for the supplied extension. - /// Take the first, to call the TrySaveToStream on. - /// - /// IEnumerable{IFileFormatHandler} - /// Bitmap - /// Stream - /// string - /// ISurface - /// bool - public static bool TrySaveToStream(this IEnumerable fileFormatHandlers, Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) - { - extension = NormalizeExtension(extension); - - var saveFileFormatHandlers = fileFormatHandlers - .Where(ffh => ffh.Supports(FileFormatHandlerActions.LoadFromStream, extension)) - .OrderBy(ffh => ffh.PriorityFor(FileFormatHandlerActions.LoadFromStream, extension)).ToList(); - - if (!saveFileFormatHandlers.Any()) - { - return false; - } - - foreach (var fileFormatHandler in saveFileFormatHandlers) - { - if (fileFormatHandler.TrySaveToStream(bitmap, destination, extension, surface)) - { - return true; - } - } - - return false; - } - - /// - /// Try to load a drawable container from the stream - /// - /// IEnumerable{IFileFormatHandler} - /// Stream - /// string - /// ISurface - /// IEnumerable{IDrawableContainer} - public static IEnumerable LoadDrawablesFromStream(this IEnumerable fileFormatHandlers, Stream stream, string extension, ISurface parentSurface = null) - { - extension = NormalizeExtension(extension); - - var loadfileFormatHandler = fileFormatHandlers - .Where(ffh => ffh.Supports(FileFormatHandlerActions.LoadDrawableFromStream, extension)) - .OrderBy(ffh => ffh.PriorityFor(FileFormatHandlerActions.LoadDrawableFromStream, extension)) - .FirstOrDefault(); - - if (loadfileFormatHandler != null) - { - return loadfileFormatHandler.LoadDrawablesFromStream(stream, extension, parentSurface); - } - - return Enumerable.Empty(); - } - - /// - /// Try to load a Bitmap from the stream - /// - /// IEnumerable{IFileFormatHandler} - /// Stream - /// string - /// Bitmap out - /// bool true if it was successful - public static bool TryLoadFromStream(this IEnumerable fileFormatHandlers, Stream stream, string extension, out Bitmap bitmap) - { - extension = NormalizeExtension(extension); - - var loadFileFormatHandler = fileFormatHandlers - .Where(ffh => ffh.Supports(FileFormatHandlerActions.LoadFromStream, extension)) - .OrderBy(ffh => ffh.PriorityFor(FileFormatHandlerActions.LoadFromStream, extension)) - .FirstOrDefault(); - - if (loadFileFormatHandler == null) - { - bitmap = null; - return false; - } - - return loadFileFormatHandler.TryLoadFromStream(stream, extension, out bitmap); - } - } -} diff --git a/src/Greenshot.Base/Core/HResultExtensions.cs b/src/Greenshot.Base/Core/HResultExtensions.cs new file mode 100644 index 000000000..c3300e80f --- /dev/null +++ b/src/Greenshot.Base/Core/HResultExtensions.cs @@ -0,0 +1,52 @@ +// Greenshot - a free and open source screenshot tool +// Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom +// +// For more information see: https://getgreenshot.org/ +// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 1 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System.Diagnostics.Contracts; +using Greenshot.Base.Core.Enums; + +namespace Greenshot.Base.Core +{ + /// + /// Extensions to handle the HResult + /// + public static class HResultExtensions + { + /// + /// Test if the HResult represents a fail + /// + /// HResult + /// bool + [Pure] + public static bool Failed(this HResult hResult) + { + return hResult < 0; + } + + /// + /// Test if the HResult represents a success + /// + /// HResult + /// bool + [Pure] + public static bool Succeeded(this HResult hResult) + { + return hResult >= HResult.S_OK; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/Core/IImage.cs b/src/Greenshot.Base/Core/IImage.cs index 20aa320d9..d6b30c667 100644 --- a/src/Greenshot.Base/Core/IImage.cs +++ b/src/Greenshot.Base/Core/IImage.cs @@ -61,7 +61,7 @@ namespace Greenshot.Base.Core float HorizontalResolution { get; } /// - /// Underlying image, or an on demand rendered version with different attributes as the original + /// Unterlying image, or an on demand rendered version with different attributes as the original /// Image Image { get; } } diff --git a/src/Greenshot.Base/Core/ImageHelper.cs b/src/Greenshot.Base/Core/ImageHelper.cs index f75cc9def..7565694e3 100644 --- a/src/Greenshot.Base/Core/ImageHelper.cs +++ b/src/Greenshot.Base/Core/ImageHelper.cs @@ -24,24 +24,28 @@ using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; -using System.Windows; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; -using Dapplo.Windows.Gdi32; -using Greenshot.Base.Core.Enums; +using System.IO; using Greenshot.Base.Effects; using Greenshot.Base.IniFile; +using Greenshot.Base.Interfaces; +using Greenshot.Base.UnmanagedHelpers; using log4net; -using Brush = System.Drawing.Brush; -using Color = System.Drawing.Color; -using Matrix = System.Drawing.Drawing2D.Matrix; -using Pen = System.Drawing.Pen; -using PixelFormat = System.Drawing.Imaging.PixelFormat; namespace Greenshot.Base.Core { + internal enum ExifOrientations : byte + { + Unknown = 0, + TopLeft = 1, + TopRight = 2, + BottomRight = 3, + BottomLeft = 4, + LeftTop = 5, + RightTop = 6, + RightBottom = 7, + LeftBottom = 8, + } + /// /// Description of ImageHelper. /// @@ -51,6 +55,83 @@ namespace Greenshot.Base.Core private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); private const int ExifOrientationId = 0x0112; + static ImageHelper() + { + StreamConverters["greenshot"] = (stream, s) => + { + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + return surface.GetImageForExport(); + }; + + // Add a SVG converter + StreamConverters["svg"] = (stream, s) => + { + stream.Position = 0; + try + { + return SvgImage.FromStream(stream).Image; + } + catch (Exception ex) + { + Log.Error("Can't load SVG", ex); + } + + return null; + }; + + static Image DefaultConverter(Stream stream, string s) + { + stream.Position = 0; + using var tmpImage = Image.FromStream(stream, true, true); + Log.DebugFormat("Loaded bitmap with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat); + return Clone(tmpImage, PixelFormat.Format32bppArgb); + } + + // Fallback + StreamConverters[string.Empty] = DefaultConverter; + StreamConverters["gif"] = DefaultConverter; + StreamConverters["bmp"] = DefaultConverter; + StreamConverters["jpg"] = DefaultConverter; + StreamConverters["jpeg"] = DefaultConverter; + StreamConverters["png"] = DefaultConverter; + StreamConverters["wmf"] = DefaultConverter; + + StreamConverters["ico"] = (stream, extension) => + { + // Icon logic, try to get the Vista icon, else the biggest possible + try + { + using Image tmpImage = ExtractVistaIcon(stream); + if (tmpImage != null) + { + return Clone(tmpImage, PixelFormat.Format32bppArgb); + } + } + catch (Exception vistaIconException) + { + Log.Warn("Can't read icon", vistaIconException); + } + + try + { + // No vista icon, try normal icon + stream.Position = 0; + // We create a copy of the bitmap, so everything else can be disposed + using Icon tmpIcon = new Icon(stream, new Size(1024, 1024)); + using Image tmpImage = tmpIcon.ToBitmap(); + return Clone(tmpImage, PixelFormat.Format32bppArgb); + } + catch (Exception iconException) + { + Log.Warn("Can't read icon", iconException); + } + + stream.Position = 0; + return DefaultConverter(stream, extension); + }; + } + + public static IDictionary> StreamConverters { get; } = new Dictionary>(); /// /// Make sure the image is orientated correctly @@ -157,7 +238,7 @@ namespace Greenshot.Base.Core graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - NativeRect rectDestination = new NativeRect(0, 0, thumbWidth, thumbHeight); + Rectangle rectDestination = new Rectangle(0, 0, thumbWidth, thumbHeight); graphics.DrawImage(image, rectDestination, 0, 0, srcWidth, srcHeight, GraphicsUnit.Pixel); } @@ -165,18 +246,18 @@ namespace Greenshot.Base.Core } /// - /// Crops the image to the specified NativeRect + /// Crops the image to the specified rectangle /// /// Image to crop - /// NativeRect with bitmap coordinates, will be "intersected" to the bitmap - public static bool Crop(ref Image image, ref NativeRect cropNativeRect) + /// Rectangle with bitmap coordinates, will be "intersected" to the bitmap + public static bool Crop(ref Image image, ref Rectangle cropRectangle) { if (image is Bitmap && (image.Width * image.Height > 0)) { - cropNativeRect = cropNativeRect.Intersect(new NativeRect(0, 0, image.Width, image.Height)); - if (cropNativeRect.Width != 0 || cropNativeRect.Height != 0) + cropRectangle.Intersect(new Rectangle(0, 0, image.Width, image.Height)); + if (cropRectangle.Width != 0 || cropRectangle.Height != 0) { - Image returnImage = CloneArea(image, cropNativeRect, PixelFormat.DontCare); + Image returnImage = CloneArea(image, cropRectangle, PixelFormat.DontCare); image.Dispose(); image = returnImage; return true; @@ -188,26 +269,24 @@ namespace Greenshot.Base.Core } /// - /// Private helper method for the FindAutoCropNativeRect + /// Private helper method for the FindAutoCropRectangle /// - /// IFastBitmap - /// NativePoint - /// int - /// NativeRect with optional area to scan in - /// NativeRect - private static NativeRect FindAutoCropNativeRect(IFastBitmap fastBitmap, NativePoint colorPoint, int cropDifference, NativeRect? area = null) + /// + /// + /// + /// Rectangle + private static Rectangle FindAutoCropRectangle(IFastBitmap fastBitmap, Point colorPoint, int cropDifference) { - area ??= new NativeRect(0, 0, fastBitmap.Width, fastBitmap.Height); - NativeRect cropNativeRect = NativeRect.Empty; + Rectangle cropRectangle = Rectangle.Empty; Color referenceColor = fastBitmap.GetColorAt(colorPoint.X, colorPoint.Y); - NativePoint min = new NativePoint(int.MaxValue, int.MaxValue); - NativePoint max = new NativePoint(int.MinValue, int.MinValue); + Point min = new Point(int.MaxValue, int.MaxValue); + Point max = new Point(int.MinValue, int.MinValue); if (cropDifference > 0) { - for (int y = area.Value.Top; y < area.Value.Bottom; y++) + for (int y = 0; y < fastBitmap.Height; y++) { - for (int x = area.Value.Left; x < area.Value.Right; x++) + for (int x = 0; x < fastBitmap.Width; x++) { Color currentColor = fastBitmap.GetColorAt(x, y); int diffR = Math.Abs(currentColor.R - referenceColor.R); @@ -218,18 +297,18 @@ namespace Greenshot.Base.Core continue; } - if (x < min.X) min = min.ChangeX(x); - if (y < min.Y) min = min.ChangeY(y); - if (x > max.X) max = max.ChangeX(x); - if (y > max.Y) max = max.ChangeY(y); + if (x < min.X) min.X = x; + if (y < min.Y) min.Y = y; + if (x > max.X) max.X = x; + if (y > max.Y) max.Y = y; } } } else { - for (int y = area.Value.Top; y < area.Value.Bottom; y++) + for (int y = 0; y < fastBitmap.Height; y++) { - for (int x = area.Value.Left; x < area.Value.Right; x++) + for (int x = 0; x < fastBitmap.Width; x++) { Color currentColor = fastBitmap.GetColorAt(x, y); if (!referenceColor.Equals(currentColor)) @@ -237,44 +316,41 @@ namespace Greenshot.Base.Core continue; } - if (x < min.X) min = min.ChangeX(x); - if (y < min.Y) min = min.ChangeY(y); - if (x > max.X) max = max.ChangeX(x); - if (y > max.Y) max = max.ChangeY(y); + if (x < min.X) min.X = x; + if (y < min.Y) min.Y = y; + if (x > max.X) max.X = x; + if (y > max.Y) max.Y = y; } } } - if (!(NativePoint.Empty.Equals(min) && max.Equals(new NativePoint(area.Value.Width - 1, area.Value.Height - 1)))) + if (!(Point.Empty.Equals(min) && max.Equals(new Point(fastBitmap.Width - 1, fastBitmap.Height - 1)))) { if (!(min.X == int.MaxValue || min.Y == int.MaxValue || max.X == int.MinValue || min.X == int.MinValue)) { - cropNativeRect = new NativeRect(min.X, min.Y, max.X - min.X + 1, max.Y - min.Y + 1); + cropRectangle = new Rectangle(min.X, min.Y, max.X - min.X + 1, max.Y - min.Y + 1); } } - return cropNativeRect; + return cropRectangle; } /// - /// Get a NativeRect for the image which crops the image of all colors equal to that on 0,0 + /// Get a rectangle for the image which crops the image of all colors equal to that on 0,0 /// - /// Image - /// int - /// NativeRect with optional area - /// NativeRect - public static NativeRect FindAutoCropRectangle(Image image, int cropDifference, NativeRect? area = null) + /// + /// + /// Rectangle + public static Rectangle FindAutoCropRectangle(Image image, int cropDifference) { - area ??= new NativeRect(0, 0, image.Width, image.Height); - NativeRect cropNativeRect = NativeRect.Empty; - var checkPoints = new List + Rectangle cropRectangle = Rectangle.Empty; + var checkPoints = new List { - new(area.Value.Left, area.Value.Top), - new(area.Value.Left, area.Value.Bottom - 1), - new(area.Value.Right - 1, area.Value.Top), - new(area.Value.Right - 1, area.Value.Bottom - 1) + new Point(0, 0), + new Point(0, image.Height - 1), + new Point(image.Width - 1, 0), + new Point(image.Width - 1, image.Height - 1) }; - // Top Left // Bottom Left // Top Right @@ -282,17 +358,138 @@ namespace Greenshot.Base.Core using (IFastBitmap fastBitmap = FastBitmap.Create((Bitmap) image)) { // find biggest area - foreach (var checkPoint in checkPoints) + foreach (Point checkPoint in checkPoints) { - var currentNativeRect = FindAutoCropNativeRect(fastBitmap, checkPoint, cropDifference, area); - if (currentNativeRect.Width * currentNativeRect.Height > cropNativeRect.Width * cropNativeRect.Height) + var currentRectangle = FindAutoCropRectangle(fastBitmap, checkPoint, cropDifference); + if (currentRectangle.Width * currentRectangle.Height > cropRectangle.Width * cropRectangle.Height) { - cropNativeRect = currentNativeRect; + cropRectangle = currentRectangle; } } } - return cropNativeRect; + return cropRectangle; + } + + /// + /// Load an image from file + /// + /// + /// + public static Image LoadImage(string filename) + { + if (string.IsNullOrEmpty(filename)) + { + return null; + } + + if (!File.Exists(filename)) + { + return null; + } + + Image fileImage; + Log.InfoFormat("Loading image from file {0}", filename); + // Fixed lock problem Bug #3431881 + using (Stream imageFileStream = File.OpenRead(filename)) + { + fileImage = FromStream(imageFileStream, Path.GetExtension(filename)); + } + + if (fileImage != null) + { + Log.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", filename, fileImage.Width, fileImage.Height, fileImage.PixelFormat, + fileImage.HorizontalResolution, fileImage.VerticalResolution); + } + + return fileImage; + } + + /// + /// Based on: https://www.codeproject.com/KB/cs/IconExtractor.aspx + /// And a hint from: https://www.codeproject.com/KB/cs/IconLib.aspx + /// + /// Stream with the icon information + /// Bitmap with the Vista Icon (256x256) + private static Bitmap ExtractVistaIcon(Stream iconStream) + { + const int sizeIconDir = 6; + const int sizeIconDirEntry = 16; + Bitmap bmpPngExtracted = null; + try + { + byte[] srcBuf = new byte[iconStream.Length]; + iconStream.Read(srcBuf, 0, (int) iconStream.Length); + int iCount = BitConverter.ToInt16(srcBuf, 4); + for (int iIndex = 0; iIndex < iCount; iIndex++) + { + int iWidth = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex]; + int iHeight = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex + 1]; + if (iWidth == 0 && iHeight == 0) + { + int iImageSize = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 8); + int iImageOffset = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 12); + using MemoryStream destStream = new MemoryStream(); + destStream.Write(srcBuf, iImageOffset, iImageSize); + destStream.Seek(0, SeekOrigin.Begin); + bmpPngExtracted = new Bitmap(destStream); // This is PNG! :) + break; + } + } + } + catch + { + return null; + } + + return bmpPngExtracted; + } + + /// + /// See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms648069%28v=vs.85%29.aspx + /// + /// The file (EXE or DLL) to get the icon from + /// Index of the icon + /// true if the large icon is wanted + /// Icon + public static Icon ExtractAssociatedIcon(string location, int index, bool takeLarge) + { + Shell32.ExtractIconEx(location, index, out var large, out var small, 1); + Icon returnIcon = null; + bool isLarge = false; + bool isSmall = false; + try + { + if (takeLarge && !IntPtr.Zero.Equals(large)) + { + returnIcon = Icon.FromHandle(large); + isLarge = true; + } + else if (!IntPtr.Zero.Equals(small)) + { + returnIcon = Icon.FromHandle(small); + isSmall = true; + } + else if (!IntPtr.Zero.Equals(large)) + { + returnIcon = Icon.FromHandle(large); + isLarge = true; + } + } + finally + { + if (isLarge && !IntPtr.Zero.Equals(small)) + { + User32.DestroyIcon(small); + } + + if (isSmall && !IntPtr.Zero.Equals(large)) + { + User32.DestroyIcon(large); + } + } + + return returnIcon; } /// @@ -300,7 +497,7 @@ namespace Greenshot.Base.Core /// /// Bitmap /// IEffect - /// Matrix + /// /// Bitmap public static Image ApplyEffect(Image sourceImage, IEffect effect, Matrix matrix) { @@ -346,7 +543,7 @@ namespace Greenshot.Base.Core /// /// Path to draw to /// Points for the lines to draw - private static void DrawLines(GraphicsPath path, List points) + private static void DrawLines(GraphicsPath path, List points) { path.AddLine(points[0], points[1]); for (int i = 0; i < points.Count - 1; i++) @@ -366,19 +563,20 @@ namespace Greenshot.Base.Core /// Changed bitmap public static Image CreateTornEdge(Image sourceImage, int toothHeight, int horizontalToothRange, int verticalToothRange, bool[] edges) { - Image returnImage = CreateEmpty(sourceImage.Width, sourceImage.Height, PixelFormat.Format32bppArgb, Color.Empty, sourceImage.HorizontalResolution, sourceImage.VerticalResolution); + Image returnImage = CreateEmpty(sourceImage.Width, sourceImage.Height, PixelFormat.Format32bppArgb, Color.Empty, sourceImage.HorizontalResolution, + sourceImage.VerticalResolution); using (var path = new GraphicsPath()) { Random random = new Random(); int horizontalRegions = (int) Math.Round((float) sourceImage.Width / horizontalToothRange); int verticalRegions = (int) Math.Round((float) sourceImage.Height / verticalToothRange); - var topLeft = new NativePoint(0, 0); - var topRight = new NativePoint(sourceImage.Width, 0); - var bottomLeft = new NativePoint(0, sourceImage.Height); - var bottomRight = new NativePoint(sourceImage.Width, sourceImage.Height); + Point topLeft = new Point(0, 0); + Point topRight = new Point(sourceImage.Width, 0); + Point bottomLeft = new Point(0, sourceImage.Height); + Point bottomRight = new Point(sourceImage.Width, sourceImage.Height); - var points = new List(); + List points = new List(); if (edges[0]) { @@ -389,15 +587,15 @@ namespace Greenshot.Base.Core } else { - points.Add(new NativePoint(random.Next(1, toothHeight), random.Next(1, toothHeight))); + points.Add(new Point(random.Next(1, toothHeight), random.Next(1, toothHeight))); } for (int i = 1; i < horizontalRegions - 1; i++) { - points.Add(new NativePoint(i * horizontalToothRange, random.Next(1, toothHeight))); + points.Add(new Point(i * horizontalToothRange, random.Next(1, toothHeight))); } - points.Add(new NativePoint(sourceImage.Width - random.Next(1, toothHeight), random.Next(1, toothHeight))); + points.Add(new Point(sourceImage.Width - random.Next(1, toothHeight), random.Next(1, toothHeight))); } else { @@ -411,10 +609,10 @@ namespace Greenshot.Base.Core { for (int i = 1; i < verticalRegions - 1; i++) { - points.Add(new NativePoint(sourceImage.Width - random.Next(1, toothHeight), i * verticalToothRange)); + points.Add(new Point(sourceImage.Width - random.Next(1, toothHeight), i * verticalToothRange)); } - points.Add(new NativePoint(sourceImage.Width - random.Next(1, toothHeight), sourceImage.Height - random.Next(1, toothHeight))); + points.Add(new Point(sourceImage.Width - random.Next(1, toothHeight), sourceImage.Height - random.Next(1, toothHeight))); } else { @@ -429,10 +627,10 @@ namespace Greenshot.Base.Core { for (int i = 1; i < horizontalRegions - 1; i++) { - points.Add(new NativePoint(sourceImage.Width - i * horizontalToothRange, sourceImage.Height - random.Next(1, toothHeight))); + points.Add(new Point(sourceImage.Width - i * horizontalToothRange, sourceImage.Height - random.Next(1, toothHeight))); } - points.Add(new NativePoint(random.Next(1, toothHeight), sourceImage.Height - random.Next(1, toothHeight))); + points.Add(new Point(random.Next(1, toothHeight), sourceImage.Height - random.Next(1, toothHeight))); } else { @@ -448,7 +646,7 @@ namespace Greenshot.Base.Core // One fewer as the end point is the starting point for (int i = 1; i < verticalRegions - 1; i++) { - points.Add(new NativePoint(random.Next(1, toothHeight), points[points.Count - 1].Y - verticalToothRange)); + points.Add(new Point(random.Next(1, toothHeight), points[points.Count - 1].Y - verticalToothRange)); } } else @@ -769,18 +967,19 @@ namespace Greenshot.Base.Core /// /// /// - /// NativeRect - public static NativeRect CreateIntersectRectangle(NativeSize applySize, NativeRect rect, bool invert) + /// + public static Rectangle CreateIntersectRectangle(Size applySize, Rectangle rect, bool invert) { - NativeRect myRect; + Rectangle myRect; if (invert) { - myRect = new NativeRect(0, 0, applySize.Width, applySize.Height); + myRect = new Rectangle(0, 0, applySize.Width, applySize.Height); } else { - NativeRect applyRect = new NativeRect(0, 0, applySize.Width, applySize.Height); - myRect = new NativeRect(rect.X, rect.Y, rect.Width, rect.Height).Intersect(applyRect); + Rectangle applyRect = new Rectangle(0, 0, applySize.Width, applySize.Height); + myRect = new Rectangle(rect.X, rect.Y, rect.Width, rect.Height); + myRect.Intersect(applyRect); } return myRect; @@ -796,9 +995,11 @@ namespace Greenshot.Base.Core /// /// The transform matrix which describes how the elements need to be transformed to stay at the same location /// Bitmap with the shadow, is bigger than the sourceBitmap!! - public static Bitmap CreateShadow(Image sourceBitmap, float darkness, int shadowSize, NativePoint shadowOffset, Matrix matrix, PixelFormat targetPixelformat) + public static Bitmap CreateShadow(Image sourceBitmap, float darkness, int shadowSize, Point shadowOffset, Matrix matrix, PixelFormat targetPixelformat) { - NativePoint offset = shadowOffset.Offset(shadowSize - 1, shadowSize - 1); + Point offset = shadowOffset; + offset.X += shadowSize - 1; + offset.Y += shadowSize - 1; matrix.Translate(offset.X, offset.Y, MatrixOrder.Append); // Create a new "clean" image Bitmap returnImage = CreateEmpty(sourceBitmap.Width + shadowSize * 2, sourceBitmap.Height + shadowSize * 2, targetPixelformat, Color.Empty, @@ -809,7 +1010,7 @@ namespace Greenshot.Base.Core shadowSize++; } - bool useGdiBlur = GdiPlusApi.IsBlurPossible(shadowSize); + bool useGdiBlur = GDIplus.IsBlurPossible(shadowSize); // Create "mask" for the shadow ColorMatrix maskMatrix = new ColorMatrix { @@ -826,20 +1027,20 @@ namespace Greenshot.Base.Core maskMatrix.Matrix33 = darkness; } - NativeRect shadowNativeRect = new NativeRect(new NativePoint(shadowSize, shadowSize), sourceBitmap.Size); - ApplyColorMatrix((Bitmap) sourceBitmap, NativeRect.Empty, returnImage, shadowNativeRect, maskMatrix); + Rectangle shadowRectangle = new Rectangle(new Point(shadowSize, shadowSize), sourceBitmap.Size); + ApplyColorMatrix((Bitmap) sourceBitmap, Rectangle.Empty, returnImage, shadowRectangle, maskMatrix); // blur "shadow", apply to whole new image if (useGdiBlur) { // Use GDI Blur - NativeRect newImageNativeRect = new NativeRect(0, 0, returnImage.Width, returnImage.Height); - GdiPlusApi.ApplyBlur(returnImage, newImageNativeRect, shadowSize + 1, false); + Rectangle newImageRectangle = new Rectangle(0, 0, returnImage.Width, returnImage.Height); + GDIplus.ApplyBlur(returnImage, newImageRectangle, shadowSize + 1, false); } else { // try normal software blur - //returnImage = CreateBlur(returnImage, newImageNativeRect, true, shadowSize, 1d, false, newImageNativeRect); + //returnImage = CreateBlur(returnImage, newImageRectangle, true, shadowSize, 1d, false, newImageRectangle); ApplyBoxBlur(returnImage, shadowSize); } @@ -903,18 +1104,18 @@ namespace Greenshot.Base.Core /// ColorMatrix to apply public static void ApplyColorMatrix(Bitmap source, ColorMatrix colorMatrix) { - ApplyColorMatrix(source, NativeRect.Empty, source, NativeRect.Empty, colorMatrix); + ApplyColorMatrix(source, Rectangle.Empty, source, Rectangle.Empty, colorMatrix); } /// /// Apply a color matrix by copying from the source to the destination /// /// Image to copy from - /// NativeRect to copy from - /// NativeRect to copy to + /// Rectangle to copy from + /// Rectangle to copy to /// Image to copy to /// ColorMatrix to apply - public static void ApplyColorMatrix(Bitmap source, NativeRect sourceRect, Bitmap dest, NativeRect destRect, ColorMatrix colorMatrix) + public static void ApplyColorMatrix(Bitmap source, Rectangle sourceRect, Bitmap dest, Rectangle destRect, ColorMatrix colorMatrix) { using ImageAttributes imageAttributes = new ImageAttributes(); imageAttributes.ClearColorMatrix(); @@ -926,15 +1127,15 @@ namespace Greenshot.Base.Core /// Apply a color matrix by copying from the source to the destination /// /// Image to copy from - /// NativeRect to copy from - /// NativeRect to copy to + /// Rectangle to copy from + /// Rectangle to copy to /// Image to copy to /// ImageAttributes to apply - public static void ApplyImageAttributes(Bitmap source, NativeRect sourceRect, Bitmap dest, NativeRect destRect, ImageAttributes imageAttributes) + public static void ApplyImageAttributes(Bitmap source, Rectangle sourceRect, Bitmap dest, Rectangle destRect, ImageAttributes imageAttributes) { - if (sourceRect == NativeRect.Empty) + if (sourceRect == Rectangle.Empty) { - sourceRect = new NativeRect(0, 0, source.Width, source.Height); + sourceRect = new Rectangle(0, 0, source.Width, source.Height); } if (dest == null) @@ -942,9 +1143,9 @@ namespace Greenshot.Base.Core dest = source; } - if (destRect == NativeRect.Empty) + if (destRect == Rectangle.Empty) { - destRect = new NativeRect(0, 0, dest.Width, dest.Height); + destRect = new Rectangle(0, 0, dest.Width, dest.Height); } using Graphics graphics = Graphics.FromImage(dest); @@ -993,7 +1194,7 @@ namespace Greenshot.Base.Core public static Image CreateBorder(Image sourceImage, int borderSize, Color borderColor, PixelFormat targetPixelformat, Matrix matrix) { // "return" the shifted offset, so the caller can e.g. move elements - NativePoint offset = new NativePoint(borderSize, borderSize); + Point offset = new Point(borderSize, borderSize); matrix.Translate(offset.X, offset.Y, MatrixOrder.Append); // Create a new "clean" image @@ -1008,7 +1209,7 @@ namespace Greenshot.Base.Core graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; using (GraphicsPath path = new GraphicsPath()) { - path.AddRectangle(new NativeRect(borderSize >> 1, borderSize >> 1, newImage.Width - borderSize, newImage.Height - borderSize)); + path.AddRectangle(new Rectangle(borderSize >> 1, borderSize >> 1, newImage.Width - borderSize, newImage.Height - borderSize)); using Pen pen = new Pen(borderColor, borderSize) { LineJoin = LineJoin.Round, @@ -1088,7 +1289,7 @@ namespace Greenshot.Base.Core sourceImage.VerticalResolution); using (ImageAttributes adjustAttributes = CreateAdjustAttributes(brightness, contrast, gamma)) { - ApplyImageAttributes((Bitmap) sourceImage, NativeRect.Empty, newBitmap, NativeRect.Empty, adjustAttributes); + ApplyImageAttributes((Bitmap) sourceImage, Rectangle.Empty, newBitmap, Rectangle.Empty, adjustAttributes); } return newBitmap; @@ -1154,7 +1355,7 @@ namespace Greenshot.Base.Core return (Image) sourceImage.Clone(); } - return CloneArea(sourceImage, NativeRect.Empty, PixelFormat.DontCare); + return CloneArea(sourceImage, Rectangle.Empty, PixelFormat.DontCare); } /// @@ -1165,7 +1366,7 @@ namespace Greenshot.Base.Core /// Bitmap with clone image data public static Bitmap Clone(Image sourceBitmap, PixelFormat targetFormat) { - return CloneArea(sourceBitmap, NativeRect.Empty, targetFormat); + return CloneArea(sourceBitmap, Rectangle.Empty, targetFormat); } /// @@ -1176,26 +1377,26 @@ namespace Greenshot.Base.Core /// 2) When going from a transparent to a non transparent bitmap, we draw the background white! /// /// Source bitmap to clone - /// NativeRect to copy from the source, use NativeRect.Empty for all + /// Rectangle to copy from the source, use Rectangle.Empty for all /// Target Format, use PixelFormat.DontCare if you want the original (or a default if the source PixelFormat is not supported) /// - public static Bitmap CloneArea(Image sourceImage, NativeRect sourceRect, PixelFormat targetFormat) + public static Bitmap CloneArea(Image sourceImage, Rectangle sourceRect, PixelFormat targetFormat) { Bitmap newImage; - NativeRect bitmapRect = new NativeRect(0, 0, sourceImage.Width, sourceImage.Height); + Rectangle bitmapRect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height); - // Make sure the source is not NativeRect.Empty - if (NativeRect.Empty.Equals(sourceRect)) + // Make sure the source is not Rectangle.Empty + if (Rectangle.Empty.Equals(sourceRect)) { - sourceRect = new NativeRect(0, 0, sourceImage.Width, sourceImage.Height); + sourceRect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height); } else { - sourceRect = sourceRect.Intersect(bitmapRect); + sourceRect.Intersect(bitmapRect); } // If no pixelformat is supplied - if (targetFormat is PixelFormat.DontCare or PixelFormat.Undefined) + if (PixelFormat.DontCare == targetFormat || PixelFormat.Undefined == targetFormat) { if (SupportsPixelFormat(sourceImage.PixelFormat)) { @@ -1316,10 +1517,10 @@ namespace Greenshot.Base.Core /// /// /// The color to fill with, or Color.Empty to take the default depending on the pixel format - /// float - /// float + /// + /// /// Bitmap - public static Bitmap CreateEmpty(int width, int height, PixelFormat format, Color backgroundColor, float horizontalResolution = 96f, float verticalResolution = 96f) + public static Bitmap CreateEmpty(int width, int height, PixelFormat format, Color backgroundColor, float horizontalResolution, float verticalResolution) { // Create a new "clean" image Bitmap newImage = new Bitmap(width, height, format); @@ -1503,113 +1704,105 @@ namespace Greenshot.Base.Core graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; using ImageAttributes wrapMode = new ImageAttributes(); wrapMode.SetWrapMode(WrapMode.TileFlipXY); - graphics.DrawImage(sourceImage, new NativeRect(destX, destY, destWidth, destHeight), 0, 0, sourceImage.Width, sourceImage.Height, GraphicsUnit.Pixel, wrapMode); + graphics.DrawImage(sourceImage, new Rectangle(destX, destY, destWidth, destHeight), 0, 0, sourceImage.Width, sourceImage.Height, GraphicsUnit.Pixel, wrapMode); } return newImage; } - + /// - /// Rotate the image + /// Load a Greenshot surface from a stream /// - /// Input image - /// Angle in degrees - /// Rotated image - public static Image Rotate(this Image image, float rotationAngle) + /// Stream + /// + /// ISurface + public static ISurface LoadGreenshotSurface(Stream surfaceFileStream, ISurface returnSurface) { - var bitmap = CreateEmptyLike(image, Color.Transparent); + Image fileImage; + // Fixed problem that the bitmap stream is disposed... by Cloning the image + // This also ensures the bitmap is correctly created - using var graphics = Graphics.FromImage(bitmap); - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + // We create a copy of the bitmap, so everything else can be disposed + surfaceFileStream.Position = 0; + using (Image tmpImage = Image.FromStream(surfaceFileStream, true, true)) + { + Log.DebugFormat("Loaded .greenshot file with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat); + fileImage = Clone(tmpImage); + } - graphics.TranslateTransform((float)bitmap.Width / 2, (float)bitmap.Height / 2); - graphics.RotateTransform(rotationAngle); - graphics.TranslateTransform(-(float)bitmap.Width / 2, -(float)bitmap.Height / 2); + // Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor) + const int markerSize = 14; + surfaceFileStream.Seek(-markerSize, SeekOrigin.End); + using (StreamReader streamReader = new StreamReader(surfaceFileStream)) + { + var greenshotMarker = streamReader.ReadToEnd(); + if (!greenshotMarker.StartsWith("Greenshot")) + { + throw new ArgumentException("Stream is not a Greenshot file!"); + } - graphics.DrawImage(image, new NativePoint(0, 0)); + Log.InfoFormat("Greenshot file format: {0}", greenshotMarker); + const int filesizeLocation = 8 + markerSize; + surfaceFileStream.Seek(-filesizeLocation, SeekOrigin.End); + using BinaryReader reader = new BinaryReader(surfaceFileStream); + long bytesWritten = reader.ReadInt64(); + surfaceFileStream.Seek(-(bytesWritten + filesizeLocation), SeekOrigin.End); + returnSurface.LoadElementsFromStream(surfaceFileStream); + } - return bitmap; + if (fileImage != null) + { + returnSurface.Image = fileImage; + Log.InfoFormat("Information about .greenshot file: {0}x{1}-{2} Resolution {3}x{4}", fileImage.Width, fileImage.Height, fileImage.PixelFormat, + fileImage.HorizontalResolution, fileImage.VerticalResolution); + } + + return returnSurface; } /// - /// Map a System.Drawing.Imaging.PixelFormat to a System.Windows.Media.PixelFormat + /// Create an image from a stream, if an extension is supplied more formats are supported. /// - /// System.Drawing.Imaging.PixelFormat - /// System.Windows.Media.PixelFormat - /// - public static System.Windows.Media.PixelFormat Map(this PixelFormat pixelFormat) => - pixelFormat switch - { - PixelFormat.Format32bppArgb => PixelFormats.Bgra32, - PixelFormat.Format24bppRgb => PixelFormats.Bgr24, - PixelFormat.Format32bppRgb => PixelFormats.Bgr32, - _ => throw new NotSupportedException($"Can't map {pixelFormat}.") - }; - - /// - /// Map a System.Windows.Media.PixelFormat to a System.Drawing.Imaging.PixelFormat - /// - /// System.Windows.Media.PixelFormat - /// System.Drawing.Imaging.PixelFormat - /// - public static PixelFormat Map(this System.Windows.Media.PixelFormat pixelFormat) + /// Stream + /// + /// Image + public static Image FromStream(Stream stream, string extension = null) { - if (pixelFormat == PixelFormats.Bgra32) + if (stream == null) { - return PixelFormat.Format32bppArgb; - } - if (pixelFormat == PixelFormats.Bgr24) - { - return PixelFormat.Format24bppRgb; - } - if (pixelFormat == PixelFormats.Bgr32) - { - return PixelFormat.Format32bppRgb; + return null; } - throw new NotSupportedException($"Can't map {pixelFormat}."); - } - - /// - /// Convert a Bitmap to a BitmapSource - /// - /// Bitmap - /// BitmapSource - public static BitmapSource ToBitmapSource(this Bitmap bitmap) - { - var bitmapData = bitmap.LockBits(new NativeRect(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat); - - BitmapSource bitmapSource; - try + if (!string.IsNullOrEmpty(extension)) { - bitmapSource = BitmapSource.Create( - bitmapData.Width, bitmapData.Height, - bitmap.HorizontalResolution, bitmap.VerticalResolution, - bitmap.PixelFormat.Map(), null, - bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride); - } - finally - { - bitmap.UnlockBits(bitmapData); + extension = extension.Replace(".", string.Empty); } - return bitmapSource; - } + // Make sure we can try multiple times + if (!stream.CanSeek) + { + var memoryStream = new MemoryStream(); + stream.CopyTo(memoryStream); + stream = memoryStream; + } - /// - /// Convert a BitmapSource to a Bitmap - /// - /// BitmapSource - /// Bitmap - public static Bitmap ToBitmap(this BitmapSource bitmapSource) - { - var pixelFormat = bitmapSource.Format.Map(); + Image returnImage = null; + if (StreamConverters.TryGetValue(extension ?? string.Empty, out var converter)) + { + returnImage = converter(stream, extension); + } - Bitmap bitmap = new Bitmap(bitmapSource.PixelWidth, bitmapSource.PixelHeight, pixelFormat); - BitmapData data = bitmap.LockBits(new NativeRect(NativePoint.Empty, bitmap.Size), ImageLockMode.WriteOnly, pixelFormat); - bitmapSource.CopyPixels(Int32Rect.Empty, data.Scan0, data.Height * data.Stride, data.Stride); - bitmap.UnlockBits(data); - return bitmap; + // Fallback + if (returnImage == null) + { + // We create a copy of the bitmap, so everything else can be disposed + stream.Position = 0; + using var tmpImage = Image.FromStream(stream, true, true); + Log.DebugFormat("Loaded bitmap with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat); + returnImage = Clone(tmpImage, PixelFormat.Format32bppArgb); + } + + return returnImage; } } } \ No newline at end of file diff --git a/src/Greenshot.Base/Core/ImageIO.cs b/src/Greenshot.Base/Core/ImageOutput.cs similarity index 59% rename from src/Greenshot.Base/Core/ImageIO.cs rename to src/Greenshot.Base/Core/ImageOutput.cs index 9d6bbf1cd..39c4896c1 100644 --- a/src/Greenshot.Base/Core/ImageIO.cs +++ b/src/Greenshot.Base/Core/ImageOutput.cs @@ -20,11 +20,12 @@ */ using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IO; -using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text; @@ -32,20 +33,20 @@ using System.Text.RegularExpressions; using System.Windows.Forms; using Greenshot.Base.Controls; using Greenshot.Base.Core.Enums; -using Greenshot.Base.Core.FileFormatHandlers; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Plugin; using log4net; +using Encoder = System.Drawing.Imaging.Encoder; namespace Greenshot.Base.Core { /// - /// This contains all io related logic for image + /// Description of ImageOutput. /// - public static class ImageIO + public static class ImageOutput { - private static readonly ILog Log = LogManager.GetLogger(typeof(ImageIO)); + private static readonly ILog Log = LogManager.GetLogger(typeof(ImageOutput)); private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); private static readonly int PROPERTY_TAG_SOFTWARE_USED = 0x0131; private static readonly Cache TmpFileCache = new Cache(10 * 60 * 60, RemoveExpiredTmpFile); @@ -53,7 +54,7 @@ namespace Greenshot.Base.Core /// /// Creates a PropertyItem (Metadata) to store with the image. /// For the possible ID's see: https://msdn.microsoft.com/de-de/library/system.drawing.imaging.propertyitem.id(v=vs.80).aspx - /// This code uses Reflection to create a PropertyItem, although it's not advised it's not as stupid as having a image in the project so we can read a PropertyItem from that! + /// This code uses Reflection to create a PropertyItem, although it's not adviced it's not as stupid as having a image in the project so we can read a PropertyItem from that! /// /// ID /// Text @@ -123,21 +124,102 @@ namespace Greenshot.Base.Core try { - // Check if we want to use a memory stream, to prevent issues with non seekable streams + var imageFormat = outputSettings.Format switch + { + OutputFormat.bmp => ImageFormat.Bmp, + OutputFormat.gif => ImageFormat.Gif, + OutputFormat.jpg => ImageFormat.Jpeg, + OutputFormat.tiff => ImageFormat.Tiff, + OutputFormat.ico => ImageFormat.Icon, + _ => ImageFormat.Png + }; + Log.DebugFormat("Saving image to stream with Format {0} and PixelFormat {1}", imageFormat, imageToSave.PixelFormat); + + // Check if we want to use a memory stream, to prevent issues with non seakable streams // The save is made to the targetStream, this is directed to either the MemoryStream or the original Stream targetStream = stream; if (!stream.CanSeek) { useMemoryStream = true; - Log.Warn("Using a memory stream prevent an issue with saving to a non seekable stream."); + Log.Warn("Using memorystream prevent an issue with saving to a non seekable stream."); memoryStream = new MemoryStream(); targetStream = memoryStream; } - var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); - if (!fileFormatHandlers.TrySaveToStream(imageToSave as Bitmap, targetStream, outputSettings.Format.ToString(), surface, outputSettings)) + if (Equals(imageFormat, ImageFormat.Jpeg)) { - return; + bool foundEncoder = false; + foreach (ImageCodecInfo imageCodec in ImageCodecInfo.GetImageEncoders()) + { + if (imageCodec.FormatID == imageFormat.Guid) + { + EncoderParameters parameters = new EncoderParameters(1) + { + Param = + { + [0] = new EncoderParameter(Encoder.Quality, outputSettings.JPGQuality) + } + }; + // Removing transparency if it's not supported in the output + if (Image.IsAlphaPixelFormat(imageToSave.PixelFormat)) + { + Image nonAlphaImage = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb); + AddTag(nonAlphaImage); + nonAlphaImage.Save(targetStream, imageCodec, parameters); + nonAlphaImage.Dispose(); + } + else + { + AddTag(imageToSave); + imageToSave.Save(targetStream, imageCodec, parameters); + } + + foundEncoder = true; + break; + } + } + + if (!foundEncoder) + { + throw new ApplicationException("No JPG encoder found, this should not happen."); + } + } + else if (Equals(imageFormat, ImageFormat.Icon)) + { + // FEATURE-916: Added Icon support + IList images = new List + { + imageToSave + }; + WriteIcon(stream, images); + } + else + { + bool needsDispose = false; + // Removing transparency if it's not supported in the output + if (!Equals(imageFormat, ImageFormat.Png) && Image.IsAlphaPixelFormat(imageToSave.PixelFormat)) + { + imageToSave = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb); + needsDispose = true; + } + + AddTag(imageToSave); + // Added for OptiPNG + bool processed = false; + if (Equals(imageFormat, ImageFormat.Png) && !string.IsNullOrEmpty(CoreConfig.OptimizePNGCommand)) + { + processed = ProcessPngImageExternally(imageToSave, targetStream); + } + + if (!processed) + { + imageToSave.Save(targetStream, imageFormat); + } + + if (needsDispose) + { + imageToSave.Dispose(); + } } // If we used a memory stream, we need to stream the memory stream to the original stream. @@ -145,6 +227,21 @@ namespace Greenshot.Base.Core { memoryStream.WriteTo(stream); } + + // Output the surface elements, size and marker to the stream + if (outputSettings.Format != OutputFormat.greenshot) + { + return; + } + + using MemoryStream tmpStream = new MemoryStream(); + long bytesWritten = surface.SaveElementsToStream(tmpStream); + using BinaryWriter writer = new BinaryWriter(tmpStream); + writer.Write(bytesWritten); + Version v = Assembly.GetExecutingAssembly().GetName().Version; + byte[] marker = Encoding.ASCII.GetBytes($"Greenshot{v.Major:00}.{v.Minor:00}"); + writer.Write(marker); + tmpStream.WriteTo(stream); } finally { @@ -152,6 +249,89 @@ namespace Greenshot.Base.Core } } + /// + /// Write the passed Image to a tmp-file and call an external process, than read the file back and write it to the targetStream + /// + /// Image to pass to the external process + /// stream to write the processed image to + /// + private static bool ProcessPngImageExternally(Image imageToProcess, Stream targetStream) + { + if (string.IsNullOrEmpty(CoreConfig.OptimizePNGCommand)) + { + return false; + } + + if (!File.Exists(CoreConfig.OptimizePNGCommand)) + { + Log.WarnFormat("Can't find 'OptimizePNGCommand' {0}", CoreConfig.OptimizePNGCommand); + return false; + } + + string tmpFileName = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".png"); + try + { + using (FileStream tmpStream = File.Create(tmpFileName)) + { + Log.DebugFormat("Writing png to tmp file: {0}", tmpFileName); + imageToProcess.Save(tmpStream, ImageFormat.Png); + if (Log.IsDebugEnabled) + { + Log.DebugFormat("File size before processing {0}", new FileInfo(tmpFileName).Length); + } + } + + if (Log.IsDebugEnabled) + { + Log.DebugFormat("Starting : {0}", CoreConfig.OptimizePNGCommand); + } + + ProcessStartInfo processStartInfo = new ProcessStartInfo(CoreConfig.OptimizePNGCommand) + { + Arguments = string.Format(CoreConfig.OptimizePNGCommandArguments, tmpFileName), + CreateNoWindow = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false + }; + using Process process = Process.Start(processStartInfo); + if (process != null) + { + process.WaitForExit(); + if (process.ExitCode == 0) + { + if (Log.IsDebugEnabled) + { + Log.DebugFormat("File size after processing {0}", new FileInfo(tmpFileName).Length); + Log.DebugFormat("Reading back tmp file: {0}", tmpFileName); + } + + byte[] processedImage = File.ReadAllBytes(tmpFileName); + targetStream.Write(processedImage, 0, processedImage.Length); + return true; + } + + Log.ErrorFormat("Error while processing PNG image: {0}", process.ExitCode); + Log.ErrorFormat("Output: {0}", process.StandardOutput.ReadToEnd()); + Log.ErrorFormat("Error: {0}", process.StandardError.ReadToEnd()); + } + } + catch (Exception e) + { + Log.Error("Error while processing PNG image: ", e); + } + finally + { + if (File.Exists(tmpFileName)) + { + Log.DebugFormat("Cleaning up tmp file: {0}", tmpFileName); + File.Delete(tmpFileName); + } + } + + return false; + } + /// /// Create an image from a surface with the settings from the output settings applied /// @@ -249,18 +429,20 @@ namespace Greenshot.Base.Core /// Add the greenshot property! /// /// - public static void AddTag(this Image imageToSave) + private static void AddTag(Image imageToSave) { // Create meta-data PropertyItem softwareUsedPropertyItem = CreatePropertyItem(PROPERTY_TAG_SOFTWARE_USED, "Greenshot"); - if (softwareUsedPropertyItem == null) return; - try + if (softwareUsedPropertyItem != null) { - imageToSave.SetPropertyItem(softwareUsedPropertyItem); - } - catch (Exception) - { - Log.WarnFormat("Couldn't set property {0}", softwareUsedPropertyItem.Id); + try + { + imageToSave.SetPropertyItem(softwareUsedPropertyItem); + } + catch (Exception) + { + Log.WarnFormat("Couldn't set property {0}", softwareUsedPropertyItem.Id); + } } } @@ -281,7 +463,7 @@ namespace Greenshot.Base.Core // Fixed lock problem Bug #3431881 using (Stream surfaceFileStream = File.OpenRead(fullPath)) { - returnSurface = LoadGreenshotSurface(surfaceFileStream, returnSurface); + returnSurface = ImageHelper.LoadGreenshotSurface(surfaceFileStream, returnSurface); } if (returnSurface != null) @@ -365,25 +547,27 @@ namespace Greenshot.Base.Core using (SaveImageFileDialog saveImageFileDialog = new SaveImageFileDialog(captureDetails)) { DialogResult dialogResult = saveImageFileDialog.ShowDialog(); - if (!dialogResult.Equals(DialogResult.OK)) return returnValue; - try + if (dialogResult.Equals(DialogResult.OK)) { - string fileNameWithExtension = saveImageFileDialog.FileNameWithExtension; - SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(FormatForFilename(fileNameWithExtension)); - if (CoreConfig.OutputFilePromptQuality) + try { - QualityDialog qualityDialog = new QualityDialog(outputSettings); - qualityDialog.ShowDialog(); - } + string fileNameWithExtension = saveImageFileDialog.FileNameWithExtension; + SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(FormatForFilename(fileNameWithExtension)); + if (CoreConfig.OutputFilePromptQuality) + { + QualityDialog qualityDialog = new QualityDialog(outputSettings); + qualityDialog.ShowDialog(); + } - // TODO: For now we always overwrite, should be changed - Save(surface, fileNameWithExtension, true, outputSettings, CoreConfig.OutputFileCopyPathToClipboard); - returnValue = fileNameWithExtension; - IniConfig.Save(); - } - catch (ExternalException) - { - MessageBox.Show(Language.GetFormattedString("error_nowriteaccess", saveImageFileDialog.FileName).Replace(@"\\", @"\"), Language.GetString("error")); + // TODO: For now we always overwrite, should be changed + Save(surface, fileNameWithExtension, true, outputSettings, CoreConfig.OutputFileCopyPathToClipboard); + returnValue = fileNameWithExtension; + IniConfig.Save(); + } + catch (ExternalException) + { + MessageBox.Show(Language.GetFormattedString("error_nowriteaccess", saveImageFileDialog.FileName).Replace(@"\\", @"\"), Language.GetString("error")); + } } } @@ -525,131 +709,91 @@ namespace Greenshot.Base.Core } /// - /// Load an image from file + /// Write the images to the stream as icon + /// Every image is resized to 256x256 (but the content maintains the aspect ratio) /// - /// - /// - public static Image LoadImage(string filename) + /// Stream to write to + /// List of images + public static void WriteIcon(Stream stream, IList images) { - if (string.IsNullOrEmpty(filename)) - { - return null; - } + var binaryWriter = new BinaryWriter(stream); + // + // ICONDIR structure + // + binaryWriter.Write((short) 0); // reserved + binaryWriter.Write((short) 1); // image type (icon) + binaryWriter.Write((short) images.Count); // number of images - if (!File.Exists(filename)) + IList imageSizes = new List(); + IList encodedImages = new List(); + foreach (var image in images) { - return null; - } - - Image fileImage; - Log.InfoFormat("Loading image from file {0}", filename); - // Fixed lock problem Bug #3431881 - using (Stream imageFileStream = File.OpenRead(filename)) - { - fileImage = FromStream(imageFileStream, Path.GetExtension(filename)); - } - - if (fileImage != null) - { - Log.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", filename, fileImage.Width, fileImage.Height, fileImage.PixelFormat, - fileImage.HorizontalResolution, fileImage.VerticalResolution); - } - - return fileImage; - } - - /// - /// Create an image from a stream, if an extension is supplied more formats are supported. - /// - /// Stream - /// - /// Image - public static Image FromStream(Stream stream, string extension = null) - { - if (stream == null) - { - return null; - } - - if (!string.IsNullOrEmpty(extension)) - { - extension = extension.Replace(".", string.Empty); - } - - var startingPosition = stream.Position; - - // Make sure we can try multiple times - if (!stream.CanSeek) - { - var memoryStream = new MemoryStream(); - stream.CopyTo(memoryStream); - stream = memoryStream; - // As we are if a different stream, which starts at 0, change the starting position - startingPosition = 0; - } - var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); - foreach (var fileFormatHandler in fileFormatHandlers - .Where(ffh => ffh.Supports(FileFormatHandlerActions.LoadFromStream, extension)) - .OrderBy(ffh => ffh.PriorityFor(FileFormatHandlerActions.LoadFromStream, extension))) - { - stream.Seek(startingPosition, SeekOrigin.Begin); - if (fileFormatHandler.TryLoadFromStream(stream, extension, out var bitmap)) + // Pick the best fit + var sizes = new[] { - return bitmap; - } - } - - return null; - } - - /// - /// Load a Greenshot surface from a stream - /// - /// Stream - /// - /// ISurface - public static ISurface LoadGreenshotSurface(Stream surfaceFileStream, ISurface returnSurface) - { - Image fileImage; - // Fixed problem that the bitmap stream is disposed... by Cloning the image - // This also ensures the bitmap is correctly created - - // We create a copy of the bitmap, so everything else can be disposed - surfaceFileStream.Position = 0; - using (Image tmpImage = Image.FromStream(surfaceFileStream, true, true)) - { - Log.DebugFormat("Loaded .greenshot file with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat); - fileImage = ImageHelper.Clone(tmpImage); - } - - // Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor) - const int markerSize = 14; - surfaceFileStream.Seek(-markerSize, SeekOrigin.End); - using (StreamReader streamReader = new StreamReader(surfaceFileStream)) - { - var greenshotMarker = streamReader.ReadToEnd(); - if (!greenshotMarker.StartsWith("Greenshot")) + 16, 32, 48 + }; + int size = 256; + foreach (var possibleSize in sizes) { - throw new ArgumentException("Stream is not a Greenshot file!"); + if (image.Width <= possibleSize && image.Height <= possibleSize) + { + size = possibleSize; + break; + } } - Log.InfoFormat("Greenshot file format: {0}", greenshotMarker); - const int filesizeLocation = 8 + markerSize; - surfaceFileStream.Seek(-filesizeLocation, SeekOrigin.End); - using BinaryReader reader = new BinaryReader(surfaceFileStream); - long bytesWritten = reader.ReadInt64(); - surfaceFileStream.Seek(-(bytesWritten + filesizeLocation), SeekOrigin.End); - returnSurface.LoadElementsFromStream(surfaceFileStream); + var imageStream = new MemoryStream(); + if (image.Width == size && image.Height == size) + { + using var clonedImage = ImageHelper.Clone(image, PixelFormat.Format32bppArgb); + clonedImage.Save(imageStream, ImageFormat.Png); + imageSizes.Add(new Size(size, size)); + } + else + { + // Resize to the specified size, first make sure the image is 32bpp + using var clonedImage = ImageHelper.Clone(image, PixelFormat.Format32bppArgb); + using var resizedImage = ImageHelper.ResizeImage(clonedImage, true, true, Color.Empty, size, size, null); + resizedImage.Save(imageStream, ImageFormat.Png); + imageSizes.Add(resizedImage.Size); + } + + imageStream.Seek(0, SeekOrigin.Begin); + encodedImages.Add(imageStream); } - if (fileImage != null) + // + // ICONDIRENTRY structure + // + const int iconDirSize = 6; + const int iconDirEntrySize = 16; + + var offset = iconDirSize + (images.Count * iconDirEntrySize); + for (int i = 0; i < images.Count; i++) { - returnSurface.Image = fileImage; - Log.InfoFormat("Information about .greenshot file: {0}x{1}-{2} Resolution {3}x{4}", fileImage.Width, fileImage.Height, fileImage.PixelFormat, - fileImage.HorizontalResolution, fileImage.VerticalResolution); + var imageSize = imageSizes[i]; + // Write the width / height, 0 means 256 + binaryWriter.Write(imageSize.Width == 256 ? (byte) 0 : (byte) imageSize.Width); + binaryWriter.Write(imageSize.Height == 256 ? (byte) 0 : (byte) imageSize.Height); + binaryWriter.Write((byte) 0); // no pallete + binaryWriter.Write((byte) 0); // reserved + binaryWriter.Write((short) 0); // no color planes + binaryWriter.Write((short) 32); // 32 bpp + binaryWriter.Write((int) encodedImages[i].Length); // image data length + binaryWriter.Write(offset); + offset += (int) encodedImages[i].Length; } - return returnSurface; + binaryWriter.Flush(); + // + // Write image data + // + foreach (var encodedImage in encodedImages) + { + encodedImage.WriteTo(stream); + encodedImage.Dispose(); + } } } } \ No newline at end of file diff --git a/src/Greenshot.Base/Core/ImageWrapper.cs b/src/Greenshot.Base/Core/ImageWrapper.cs new file mode 100644 index 000000000..517249534 --- /dev/null +++ b/src/Greenshot.Base/Core/ImageWrapper.cs @@ -0,0 +1,77 @@ +using System.Drawing; +using System.Drawing.Imaging; + +namespace Greenshot.Base.Core +{ + /// + /// Wrap an image, make it resizeable + /// + public class ImageWrapper : IImage + { + // Underlying image, is used to generate a resized version of it when needed + private readonly Image _image; + private Image _imageClone; + + public ImageWrapper(Image image) + { + // Make sure the orientation is set correctly so Greenshot can process the image correctly + ImageHelper.Orientate(image); + _image = image; + Width = _image.Width; + Height = _image.Height; + } + + public void Dispose() + { + _image.Dispose(); + _imageClone?.Dispose(); + } + + /// + /// Height of the image, can be set to change + /// + public int Height { get; set; } + + /// + /// Width of the image, can be set to change. + /// + public int Width { get; set; } + + /// + /// Size of the image + /// + public Size Size => new Size(Width, Height); + + /// + /// Pixelformat of the underlying image + /// + public PixelFormat PixelFormat => Image.PixelFormat; + + public float HorizontalResolution => Image.HorizontalResolution; + public float VerticalResolution => Image.VerticalResolution; + + public Image Image + { + get + { + if (_imageClone == null) + { + if (_image.Height == Height && _image.Width == Width) + { + return _image; + } + } + + if (_imageClone?.Height == Height && _imageClone?.Width == Width) + { + return _imageClone; + } + + // Calculate new image clone + _imageClone?.Dispose(); + _imageClone = ImageHelper.ResizeImage(_image, false, Width, Height, null); + return _imageClone; + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/Core/Language.cs b/src/Greenshot.Base/Core/Language.cs index 4627a10fc..8037393ad 100644 --- a/src/Greenshot.Base/Core/Language.cs +++ b/src/Greenshot.Base/Core/Language.cs @@ -470,7 +470,7 @@ namespace Greenshot.Base.Core languageFile }; LanguageFiles.Add(languageFile.Ietf, currentFiles); - Log.DebugFormat("Added language definition {0} from: {1}", languageFile.Description, languageFile.Filepath); + Log.InfoFormat("Added language definition {0} from: {1}", languageFile.Description, languageFile.Filepath); } } } diff --git a/src/Greenshot.Base/Core/NetworkHelper.cs b/src/Greenshot.Base/Core/NetworkHelper.cs index 88cfff569..5170db504 100644 --- a/src/Greenshot.Base/Core/NetworkHelper.cs +++ b/src/Greenshot.Base/Core/NetworkHelper.cs @@ -24,14 +24,11 @@ using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.IO; -using System.Linq; using System.Net; using System.Text; using System.Text.RegularExpressions; -using Greenshot.Base.Core.FileFormatHandlers; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; -using Greenshot.Base.Interfaces.Drawing; using Greenshot.Base.Interfaces.Plugin; using log4net; @@ -90,77 +87,24 @@ namespace Greenshot.Base.Core } /// - /// Download the uri to build an IDrawableContainer - /// - /// Of an image - /// IDrawableContainer - public static IDrawableContainer DownloadImageAsDrawableContainer(string url) - { - var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); - var extensions = string.Join("|", fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadFromStream)); - - var imageUrlRegex = new Regex($@"(http|https)://.*(?{extensions})"); - var match = imageUrlRegex.Match(url); - try - { - using var memoryStream = GetAsMemoryStream(url); - try - { - var extension = match.Success ? match.Groups["extension"]?.Value : null; - var drawableContainer = fileFormatHandlers.LoadDrawablesFromStream(memoryStream, extension).FirstOrDefault(); - if (drawableContainer != null) - { - return drawableContainer; - } - } - catch (Exception) - { - // If we arrive here, the image loading didn't work, try to see if the response has a http(s) URL to an image and just take this instead. - string content; - using (var streamReader = new StreamReader(memoryStream, Encoding.UTF8, true)) - { - content = streamReader.ReadLine(); - } - - if (string.IsNullOrEmpty(content)) - { - throw; - } - - match = imageUrlRegex.Match(content); - if (!match.Success) - { - throw; - } - - using var memoryStream2 = GetAsMemoryStream(match.Value); - - var extension = match.Success ? match.Groups["extension"]?.Value : null; - var drawableContainer = fileFormatHandlers.LoadDrawablesFromStream(memoryStream2, extension).FirstOrDefault(); - if (drawableContainer != null) - { - return drawableContainer; - } - } - } - catch (Exception e) - { - Log.Error("Problem downloading the image from: " + url, e); - } - - return null; - } - - /// - /// Download the uri to create a Bitmap + /// Download the uri to Bitmap /// /// Of an image /// Bitmap - public static Bitmap DownloadImage(string url) + public static Image DownloadImage(string url) { - var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); + var extensions = new StringBuilder(); + foreach (var extension in ImageHelper.StreamConverters.Keys) + { + if (string.IsNullOrEmpty(extension)) + { + continue; + } - var extensions = string.Join("|", fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadFromStream)); + extensions.AppendFormat(@"\.{0}|", extension); + } + + extensions.Length--; var imageUrlRegex = new Regex($@"(http|https)://.*(?{extensions})"); var match = imageUrlRegex.Match(url); @@ -169,10 +113,7 @@ namespace Greenshot.Base.Core using var memoryStream = GetAsMemoryStream(url); try { - if (fileFormatHandlers.TryLoadFromStream(memoryStream, match.Success ? match.Groups["extension"]?.Value : null, out var bitmap)) - { - return bitmap; - } + return ImageHelper.FromStream(memoryStream, match.Success ? match.Groups["extension"]?.Value : null); } catch (Exception) { @@ -195,10 +136,7 @@ namespace Greenshot.Base.Core } using var memoryStream2 = GetAsMemoryStream(match.Value); - if (fileFormatHandlers.TryLoadFromStream(memoryStream2, match.Success ? match.Groups["extension"]?.Value : null, out var bitmap)) - { - return bitmap; - } + return ImageHelper.FromStream(memoryStream2, match.Groups["extension"]?.Value); } } catch (Exception e) @@ -732,7 +670,7 @@ namespace Greenshot.Base.Core public string ToBase64String(Base64FormattingOptions formattingOptions) { using MemoryStream stream = new MemoryStream(); - ImageIO.SaveToStream(_surface, stream, _outputSettings); + ImageOutput.SaveToStream(_surface, stream, _outputSettings); return Convert.ToBase64String(stream.GetBuffer(), 0, (int) stream.Length, formattingOptions); } @@ -744,7 +682,7 @@ namespace Greenshot.Base.Core public byte[] ToByteArray() { using MemoryStream stream = new MemoryStream(); - ImageIO.SaveToStream(_surface, stream, _outputSettings); + ImageOutput.SaveToStream(_surface, stream, _outputSettings); return stream.ToArray(); } @@ -760,7 +698,7 @@ namespace Greenshot.Base.Core string header = $"--{boundary}\r\nContent-Disposition: form-data; name=\"{name}\"; filename=\"{Filename ?? name}\";\r\nContent-Type: {ContentType}\r\n\r\n"; formDataStream.Write(Encoding.UTF8.GetBytes(header), 0, Encoding.UTF8.GetByteCount(header)); - ImageIO.SaveToStream(_surface, formDataStream, _outputSettings); + ImageOutput.SaveToStream(_surface, formDataStream, _outputSettings); } /// @@ -770,7 +708,7 @@ namespace Greenshot.Base.Core public void WriteToStream(Stream dataStream) { // Write the file data directly to the Stream, rather than serializing it to a string. - ImageIO.SaveToStream(_surface, dataStream, _outputSettings); + ImageOutput.SaveToStream(_surface, dataStream, _outputSettings); } /// diff --git a/src/Greenshot.Base/Core/PluginUtils.cs b/src/Greenshot.Base/Core/PluginUtils.cs index 76f08422b..3d3ec79f2 100644 --- a/src/Greenshot.Base/Core/PluginUtils.cs +++ b/src/Greenshot.Base/Core/PluginUtils.cs @@ -24,10 +24,9 @@ using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.IO; -using System.Linq; using System.Windows.Forms; -using Dapplo.Windows.Icons; using Greenshot.Base.IniFile; +using Greenshot.Base.UnmanagedHelpers; using log4net; using Microsoft.Win32; @@ -40,9 +39,9 @@ namespace Greenshot.Base.Core { private static readonly ILog Log = LogManager.GetLogger(typeof(PluginUtils)); private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); - private static readonly IDictionary ExeIconCache = new Dictionary(); private const string PathKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\"; - + private static readonly IDictionary ExeIconCache = new Dictionary(); + static PluginUtils() { CoreConfig.PropertyChanged += OnIconSizeChanged; @@ -85,7 +84,7 @@ namespace Greenshot.Base.Core if (key != null) { // "" is the default key, which should point to the requested location - return (string)key.GetValue(string.Empty); + return (string) key.GetValue(string.Empty); } } @@ -114,7 +113,7 @@ namespace Greenshot.Base.Core /// /// path to the exe or dll /// index of the icon - /// Bitmap with the icon or null if something happened + /// Bitmap with the icon or null if something happended public static Image GetCachedExeIcon(string path, int index) { string cacheKey = $"{path}:{index}"; @@ -149,7 +148,7 @@ namespace Greenshot.Base.Core /// /// path to the exe or dll /// index of the icon - /// Bitmap with the icon or null if something happened + /// Bitmap with the icon or null if something happended private static Bitmap GetExeIcon(string path, int index) { if (!File.Exists(path)) @@ -159,11 +158,20 @@ namespace Greenshot.Base.Core try { - var appIcon = IconHelper.ExtractAssociatedIcon(path, index, CoreConfig.UseLargeIcons); - if (appIcon != null) + using (Icon appIcon = ImageHelper.ExtractAssociatedIcon(path, index, CoreConfig.UseLargeIcons)) { - Log.DebugFormat("Loaded icon for {0}, with dimensions {1}x{2}", path, appIcon.Width, appIcon.Height); - return appIcon; + if (appIcon != null) + { + return appIcon.ToBitmap(); + } + } + + using (Icon appIcon = Shell32.GetFileIcon(path, CoreConfig.UseLargeIcons ? Shell32.IconSize.Large : Shell32.IconSize.Small, false)) + { + if (appIcon != null) + { + return appIcon.ToBitmap(); + } } } catch (Exception exIcon) @@ -187,25 +195,27 @@ namespace Greenshot.Base.Core // Try to find a separator, so we insert ourselves after it for (int i = 0; i < contextMenu.Items.Count; i++) { - if (contextMenu.Items[i].GetType() != typeof(ToolStripSeparator)) continue; - // Check if we need to add a new separator, which is done if the first found has a Tag with the value "PluginsAreAddedBefore" - if ("PluginsAreAddedBefore".Equals(contextMenu.Items[i].Tag)) + if (contextMenu.Items[i].GetType() == typeof(ToolStripSeparator)) { - var separator = new ToolStripSeparator + // Check if we need to add a new separator, which is done if the first found has a Tag with the value "PluginsAreAddedBefore" + if ("PluginsAreAddedBefore".Equals(contextMenu.Items[i].Tag)) { - Tag = "PluginsAreAddedAfter", - Size = new Size(305, 6) - }; - contextMenu.Items.Insert(i, separator); - } - else if (!"PluginsAreAddedAfter".Equals(contextMenu.Items[i].Tag)) - { - continue; - } + var separator = new ToolStripSeparator + { + Tag = "PluginsAreAddedAfter", + Size = new Size(305, 6) + }; + contextMenu.Items.Insert(i, separator); + } + else if (!"PluginsAreAddedAfter".Equals(contextMenu.Items[i].Tag)) + { + continue; + } - contextMenu.Items.Insert(i + 1, item); - addedItem = true; - break; + contextMenu.Items.Insert(i + 1, item); + addedItem = true; + break; + } } // If we didn't insert the item, we just add it... diff --git a/src/Greenshot.Base/Core/SimpleServiceProvider.cs b/src/Greenshot.Base/Core/SimpleServiceProvider.cs index 084f5b1fd..bff814540 100644 --- a/src/Greenshot.Base/Core/SimpleServiceProvider.cs +++ b/src/Greenshot.Base/Core/SimpleServiceProvider.cs @@ -10,19 +10,22 @@ namespace Greenshot.Base.Core /// public class SimpleServiceProvider : IServiceLocator { - private readonly Dictionary> _services = new(); + private readonly Dictionary> _services = new Dictionary>(); public static IServiceLocator Current { get; } = new SimpleServiceProvider(); - public IReadOnlyList GetAllInstances() + public IEnumerable GetAllInstances() { var typeOfService = typeof(TService); if (!_services.TryGetValue(typeOfService, out var results)) { - return Array.Empty(); + yield break; } - return results.Cast().ToArray(); + foreach (TService result in results) + { + yield return result; + } } public TService GetInstance() diff --git a/src/Greenshot.Base/Core/SvgImage.cs b/src/Greenshot.Base/Core/SvgImage.cs new file mode 100644 index 000000000..25f98b212 --- /dev/null +++ b/src/Greenshot.Base/Core/SvgImage.cs @@ -0,0 +1,117 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using Svg; + +namespace Greenshot.Base.Core +{ + /// + /// Create an image look like of the SVG + /// + public sealed class SvgImage : IImage + { + private readonly SvgDocument _svgDocument; + + private Image _imageClone; + + /// + /// Factory to create via a stream + /// + /// Stream + /// IImage + public static IImage FromStream(Stream stream) + { + return new SvgImage(stream); + } + + /// + /// Default constructor + /// + /// + public SvgImage(Stream stream) + { + _svgDocument = SvgDocument.Open(stream); + Height = (int) _svgDocument.ViewBox.Height; + Width = (int) _svgDocument.ViewBox.Width; + } + + /// + /// Height of the image, can be set to change + /// + public int Height { get; set; } + + /// + /// Width of the image, can be set to change. + /// + public int Width { get; set; } + + /// + /// Size of the image + /// + public Size Size => new Size(Width, Height); + + /// + /// Pixelformat of the underlying image + /// + public PixelFormat PixelFormat => Image.PixelFormat; + + /// + /// Horizontal resolution of the underlying image + /// + public float HorizontalResolution => Image.HorizontalResolution; + + /// + /// Vertical resolution of the underlying image + /// + public float VerticalResolution => Image.VerticalResolution; + + /// + /// Underlying image, or an on demand rendered version with different attributes as the original + /// + public Image Image + { + get + { + if (_imageClone?.Height == Height && _imageClone?.Width == Width) + { + return _imageClone; + } + + // Calculate new image clone + _imageClone?.Dispose(); + _imageClone = ImageHelper.CreateEmpty(Width, Height, PixelFormat.Format32bppArgb, Color.Transparent, 96, 96); + _svgDocument.Draw((Bitmap) _imageClone); + return _imageClone; + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + _imageClone?.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/Core/WindowCapture.cs b/src/Greenshot.Base/Core/WindowCapture.cs index 01e1bce56..a6b846fd4 100644 --- a/src/Greenshot.Base/Core/WindowCapture.cs +++ b/src/Greenshot.Base/Core/WindowCapture.cs @@ -26,20 +26,10 @@ using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; using System.Windows.Forms; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; -using Dapplo.Windows.Gdi32; -using Dapplo.Windows.Gdi32.Enums; -using Dapplo.Windows.Gdi32.SafeHandles; -using Dapplo.Windows.Gdi32.Structs; -using Dapplo.Windows.Icons; -using Dapplo.Windows.Icons.SafeHandles; -using Dapplo.Windows.Kernel32; -using Dapplo.Windows.User32; -using Dapplo.Windows.User32.Enums; -using Dapplo.Windows.User32.Structs; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; +using Greenshot.Base.UnmanagedHelpers; +using Greenshot.Base.UnmanagedHelpers.Structs; using log4net; namespace Greenshot.Base.Core @@ -52,6 +42,35 @@ namespace Greenshot.Base.Core private static readonly ILog Log = LogManager.GetLogger(typeof(WindowCapture)); private static readonly CoreConfiguration Configuration = IniConfig.GetIniSection(); + /// + /// Used to cleanup the unmanaged resource in the iconInfo for the CaptureCursor method + /// + /// + /// + [DllImport("gdi32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DeleteObject(IntPtr hObject); + + /// + /// Get the bounds of all screens combined. + /// + /// A Rectangle of the bounds of the entire display area. + public static Rectangle GetScreenBounds() + { + int left = 0, top = 0, bottom = 0, right = 0; + foreach (Screen screen in Screen.AllScreens) + { + left = Math.Min(left, screen.Bounds.X); + top = Math.Min(top, screen.Bounds.Y); + int screenAbsRight = screen.Bounds.X + screen.Bounds.Width; + int screenAbsBottom = screen.Bounds.Y + screen.Bounds.Height; + right = Math.Max(right, screenAbsRight); + bottom = Math.Max(bottom, screenAbsBottom); + } + + return new Rectangle(left, top, (right + Math.Abs(left)), (bottom + Math.Abs(top))); + } + /// /// Retrieves the cursor location safely, accounting for DPI settings in Vista/Windows 7. This implementation /// can conveniently be used when the cursor location is needed to deal with a fullscreen bitmap. @@ -59,9 +78,9 @@ namespace Greenshot.Base.Core /// /// Point with cursor location, relative to the top left corner of the monitor setup (which itself might actually not be on any screen) /// - public static NativePoint GetCursorLocationRelativeToScreenBounds() + public static Point GetCursorLocationRelativeToScreenBounds() { - return GetLocationRelativeToScreenBounds(User32Api.GetCursorLocation()); + return GetLocationRelativeToScreenBounds(User32.GetCursorLocation()); } /// @@ -71,10 +90,12 @@ namespace Greenshot.Base.Core /// /// /// Point - public static NativePoint GetLocationRelativeToScreenBounds(NativePoint locationRelativeToScreenOrigin) + public static Point GetLocationRelativeToScreenBounds(Point locationRelativeToScreenOrigin) { - NativeRect bounds = DisplayInfo.ScreenBounds; - return locationRelativeToScreenOrigin.Offset(-bounds.X, -bounds.Y); + Point ret = locationRelativeToScreenOrigin; + Rectangle bounds = GetScreenBounds(); + ret.Offset(-bounds.X, -bounds.Y); + return ret; } /// @@ -89,26 +110,36 @@ namespace Greenshot.Base.Core capture = new Capture(); } - var cursorInfo = CursorInfo.Create(); - if (!NativeCursorMethods.GetCursorInfo(ref cursorInfo)) return capture; - if (cursorInfo.Flags != CursorInfoFlags.Showing) return capture; + var cursorInfo = new CursorInfo(); + cursorInfo.cbSize = Marshal.SizeOf(cursorInfo); + if (!User32.GetCursorInfo(out cursorInfo)) return capture; + if (cursorInfo.flags != User32.CURSOR_SHOWING) return capture; - using SafeIconHandle safeIcon = NativeIconMethods.CopyIcon(cursorInfo.CursorHandle); - if (!NativeIconMethods.GetIconInfo(safeIcon, out var iconInfo)) return capture; + using SafeIconHandle safeIcon = User32.CopyIcon(cursorInfo.hCursor); + if (!User32.GetIconInfo(safeIcon, out var iconInfo)) return capture; - NativePoint cursorLocation = User32Api.GetCursorLocation(); + Point cursorLocation = User32.GetCursorLocation(); // Align cursor location to Bitmap coordinates (instead of Screen coordinates) - var x = cursorLocation.X - iconInfo.Hotspot.X - capture.ScreenBounds.X; - var y = cursorLocation.Y - iconInfo.Hotspot.Y - capture.ScreenBounds.Y; + var x = cursorLocation.X - iconInfo.xHotspot - capture.ScreenBounds.X; + var y = cursorLocation.Y - iconInfo.yHotspot - capture.ScreenBounds.Y; // Set the location - capture.CursorLocation = new NativePoint(x, y); + capture.CursorLocation = new Point(x, y); using (Icon icon = Icon.FromHandle(safeIcon.DangerousGetHandle())) { capture.Cursor = icon; } - iconInfo.BitmaskBitmapHandle.Dispose(); - iconInfo.ColorBitmapHandle.Dispose(); + + if (iconInfo.hbmMask != IntPtr.Zero) + { + DeleteObject(iconInfo.hbmMask); + } + + if (iconInfo.hbmColor != IntPtr.Zero) + { + DeleteObject(iconInfo.hbmColor); + } + return capture; } @@ -130,11 +161,11 @@ namespace Greenshot.Base.Core /// Helper method to create an exception that might explain what is wrong while capturing /// /// string with current method - /// NativeRect of what we want to capture + /// Rectangle of what we want to capture /// - private static Exception CreateCaptureException(string method, NativeRect captureBounds) + private static Exception CreateCaptureException(string method, Rectangle captureBounds) { - Exception exceptionToThrow = User32Api.CreateWin32Exception(method); + Exception exceptionToThrow = User32.CreateWin32Exception(method); if (!captureBounds.IsEmpty) { exceptionToThrow.Data.Add("Height", captureBounds.Height); @@ -202,9 +233,9 @@ namespace Greenshot.Base.Core /// This method will use User32 code to capture the specified captureBounds from the screen /// /// ICapture where the captured Bitmap will be stored - /// NativeRect with the bounds to capture + /// Rectangle with the bounds to capture /// A Capture Object with a part of the Screen as an Image - public static ICapture CaptureRectangle(ICapture capture, NativeRect captureBounds) + public static ICapture CaptureRectangle(ICapture capture, Rectangle captureBounds) { if (capture == null) { @@ -240,9 +271,9 @@ namespace Greenshot.Base.Core /// This method will use User32 code to capture the specified captureBounds from the screen /// /// ICapture where the captured Bitmap will be stored - /// NativeRect with the bounds to capture + /// Rectangle with the bounds to capture /// A Capture Object with a part of the Screen as an Image - public static ICapture CaptureRectangleFromDesktopScreen(ICapture capture, NativeRect captureBounds) + public static ICapture CaptureRectangleFromDesktopScreen(ICapture capture, Rectangle captureBounds) { if (capture == null) { @@ -257,9 +288,9 @@ namespace Greenshot.Base.Core /// /// This method will use User32 code to capture the specified captureBounds from the screen /// - /// NativeRect with the bounds to capture + /// Rectangle with the bounds to capture /// Bitmap which is captured from the screen at the location specified by the captureBounds - public static Bitmap CaptureRectangle(NativeRect captureBounds) + public static Bitmap CaptureRectangle(Rectangle captureBounds) { Bitmap returnBitmap = null; if (captureBounds.Height <= 0 || captureBounds.Width <= 0) @@ -290,7 +321,7 @@ namespace Greenshot.Base.Core } // create a device context we can copy to - using SafeCompatibleDcHandle safeCompatibleDcHandle = Gdi32Api.CreateCompatibleDC(desktopDcHandle); + using SafeCompatibleDCHandle safeCompatibleDcHandle = GDI32.CreateCompatibleDC(desktopDcHandle); // Check if the device context is there, if not throw an error with as much info as possible! if (safeCompatibleDcHandle.IsInvalid) { @@ -301,13 +332,13 @@ namespace Greenshot.Base.Core } // Create BITMAPINFOHEADER for CreateDIBSection - var bitmapInfoHeader = BitmapV5Header.Create(captureBounds.Width, captureBounds.Height, 24); + BITMAPINFOHEADERV5 bmi = new BITMAPINFOHEADERV5(captureBounds.Width, captureBounds.Height, 24); // Make sure the last error is set to 0 - Kernel32Api.SetLastError(0); + Win32.SetLastError(0); // create a bitmap we can copy it to, using GetDeviceCaps to get the width/height - using SafeDibSectionHandle safeDibSectionHandle = Gdi32Api.CreateDIBSection(desktopDcHandle, ref bitmapInfoHeader, DibColors.RgbColors, out _, IntPtr.Zero, 0); + using SafeDibSectionHandle safeDibSectionHandle = GDI32.CreateDIBSection(desktopDcHandle, ref bmi, BITMAPINFOHEADERV5.DIB_RGB_COLORS, out _, IntPtr.Zero, 0); if (safeDibSectionHandle.IsInvalid) { // Get Exception before the error is lost @@ -324,8 +355,8 @@ namespace Greenshot.Base.Core { // bitblt over (make copy) // ReSharper disable once BitwiseOperatorOnEnumWithoutFlags - Gdi32Api.BitBlt(safeCompatibleDcHandle, 0, 0, captureBounds.Width, captureBounds.Height, desktopDcHandle, captureBounds.X, captureBounds.Y, - RasterOperations.SourceCopy | RasterOperations.CaptureBlt); + GDI32.BitBlt(safeCompatibleDcHandle, 0, 0, captureBounds.Width, captureBounds.Height, desktopDcHandle, captureBounds.X, captureBounds.Y, + CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt); } // get a .NET image object for it @@ -357,7 +388,7 @@ namespace Greenshot.Base.Core } // If the region is not empty, we have "offscreenContent" - using Graphics screenGraphics = Graphics.FromHwnd(User32Api.GetDesktopWindow()); + using Graphics screenGraphics = Graphics.FromHwnd(User32.GetDesktopWindow()); offscreenContent = !captureRegion.IsEmpty(screenGraphics); } @@ -366,16 +397,17 @@ namespace Greenshot.Base.Core { using Bitmap tmpBitmap = Image.FromHbitmap(safeDibSectionHandle.DangerousGetHandle()); // Create a new bitmap which has a transparent background - returnBitmap = ImageHelper.CreateEmpty(tmpBitmap.Width, tmpBitmap.Height, PixelFormat.Format32bppArgb, Color.Transparent, tmpBitmap.HorizontalResolution, tmpBitmap.VerticalResolution); + returnBitmap = ImageHelper.CreateEmpty(tmpBitmap.Width, tmpBitmap.Height, PixelFormat.Format32bppArgb, Color.Transparent, + tmpBitmap.HorizontalResolution, tmpBitmap.VerticalResolution); // Content will be copied here using Graphics graphics = Graphics.FromImage(returnBitmap); // For all screens copy the content to the new bitmap - - foreach (var displayInfo in DisplayInfo.AllDisplayInfos) + foreach (Screen screen in Screen.AllScreens) { - // Make sure the bounds are offset to the capture bounds - var displayBounds = displayInfo.Bounds.Offset(-captureBounds.X, -captureBounds.Y); - graphics.DrawImage(tmpBitmap, displayBounds, displayBounds.X, displayBounds.Y, displayBounds.Width, displayBounds.Height, GraphicsUnit.Pixel); + Rectangle screenBounds = screen.Bounds; + // Make sure the bounds are offsetted to the capture bounds + screenBounds.Offset(-captureBounds.X, -captureBounds.Y); + graphics.DrawImage(tmpBitmap, screenBounds, screenBounds.X, screenBounds.Y, screenBounds.Width, screenBounds.Height, GraphicsUnit.Pixel); } } else diff --git a/src/Greenshot.Base/Core/WindowDetails.cs b/src/Greenshot.Base/Core/WindowDetails.cs index 9cdf5f370..0063e3edb 100644 --- a/src/Greenshot.Base/Core/WindowDetails.cs +++ b/src/Greenshot.Base/Core/WindowDetails.cs @@ -7,25 +7,13 @@ using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; -using Dapplo.Windows.Common; -using Dapplo.Windows.Common.Enums; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; -using Dapplo.Windows.DesktopWindowsManager; -using Dapplo.Windows.DesktopWindowsManager.Enums; -using Dapplo.Windows.DesktopWindowsManager.Structs; -using Dapplo.Windows.Gdi32; -using Dapplo.Windows.Gdi32.SafeHandles; -using Dapplo.Windows.Kernel32; -using Dapplo.Windows.Kernel32.Enums; -using Dapplo.Windows.Messages.Enumerations; -using Dapplo.Windows.User32; -using Dapplo.Windows.User32.Enums; -using Dapplo.Windows.User32.Structs; using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using Greenshot.Base.Interop; +using Greenshot.Base.UnmanagedHelpers; +using Greenshot.Base.UnmanagedHelpers.Enums; +using Greenshot.Base.UnmanagedHelpers.Structs; using log4net; namespace Greenshot.Base.Core @@ -42,6 +30,8 @@ namespace Greenshot.Base.Core { private const string AppWindowClass = "Windows.UI.Core.CoreWindow"; //Used for Windows 8(.1) private const string AppFrameWindowClass = "ApplicationFrameWindow"; // Windows 10 uses ApplicationFrameWindow + private const string ApplauncherClass = "ImmersiveLauncher"; + private const string GutterClass = "ImmersiveGutter"; private static readonly IList IgnoreClasses = new List(new[] { @@ -87,7 +77,13 @@ namespace Greenshot.Base.Core private IntPtr _parentHandle = IntPtr.Zero; private WindowDetails _parent; private bool _frozen; - + + /// + /// This checks if the window is a Windows 8 App + /// For Windows 10 most normal code works, as it's hosted inside "ApplicationFrameWindow" + /// + public bool IsApp => AppWindowClass.Equals(ClassName); + /// /// This checks if the window is a Windows 10 App /// For Windows 10 apps are hosted inside "ApplicationFrameWindow" @@ -100,6 +96,20 @@ namespace Greenshot.Base.Core public bool IsBackgroundWin10App => WindowsVersion.IsWindows10OrLater && AppFrameWindowClass.Equals(ClassName) && !Children.Any(window => string.Equals(window.ClassName, AppWindowClass)); + /// + /// Check if the window is the metro gutter (sizeable separator) + /// + public bool IsGutter => GutterClass.Equals(ClassName); + + /// + /// Test if this window is for the App-Launcher + /// + public bool IsAppLauncher => ApplauncherClass.Equals(ClassName); + + /// + /// Check if this window is the window of a metro app + /// + public bool IsMetroApp => IsAppLauncher || IsApp; /// /// To allow items to be compared, the hash code @@ -177,8 +187,8 @@ namespace Greenshot.Base.Core } // Get the process id - User32Api.GetWindowThreadProcessId(Handle, out var processId); - return Kernel32Api.GetProcessPath(processId); + User32.GetWindowThreadProcessId(Handle, out var processId); + return Kernel32.GetProcessPath(processId); } } @@ -204,6 +214,12 @@ namespace Greenshot.Base.Core Log.Warn(ex); } + if (IsMetroApp) + { + // No method yet to get the metro icon + return null; + } + try { return PluginUtils.GetCachedExeIcon(ProcessPath, 0); @@ -232,35 +248,35 @@ namespace Greenshot.Base.Core IntPtr iconHandle; if (Conf.UseLargeIcons) { - iconHandle = User32Api.SendMessage(hWnd, WindowsMessages.WM_GETICON, iconBig, IntPtr.Zero); + iconHandle = User32.SendMessage(hWnd, (int) WindowsMessages.WM_GETICON, iconBig, IntPtr.Zero); if (iconHandle == IntPtr.Zero) { - iconHandle = User32Api.GetClassLongWrapper(hWnd, ClassLongIndex.IconHandle); + iconHandle = User32.GetClassLongWrapper(hWnd, (int) ClassLongIndex.GCL_HICON); } } else { - iconHandle = User32Api.SendMessage(hWnd, WindowsMessages.WM_GETICON, iconSmall2, IntPtr.Zero); + iconHandle = User32.SendMessage(hWnd, (int) WindowsMessages.WM_GETICON, iconSmall2, IntPtr.Zero); } if (iconHandle == IntPtr.Zero) { - iconHandle = User32Api.SendMessage(hWnd, WindowsMessages.WM_GETICON, iconSmall, IntPtr.Zero); + iconHandle = User32.SendMessage(hWnd, (int) WindowsMessages.WM_GETICON, iconSmall, IntPtr.Zero); } if (iconHandle == IntPtr.Zero) { - iconHandle = User32Api.GetClassLongWrapper(hWnd, ClassLongIndex.IconHandle); + iconHandle = User32.GetClassLongWrapper(hWnd, (int) ClassLongIndex.GCL_HICONSM); } if (iconHandle == IntPtr.Zero) { - iconHandle = User32Api.SendMessage(hWnd, WindowsMessages.WM_GETICON, iconBig, IntPtr.Zero); + iconHandle = User32.SendMessage(hWnd, (int) WindowsMessages.WM_GETICON, iconBig, IntPtr.Zero); } if (iconHandle == IntPtr.Zero) { - iconHandle = User32Api.GetClassLongWrapper(hWnd, ClassLongIndex.IconHandle); + iconHandle = User32.GetClassLongWrapper(hWnd, (int) ClassLongIndex.GCL_HICON); } if (iconHandle == IntPtr.Zero) @@ -326,7 +342,7 @@ namespace Greenshot.Base.Core { if (_parentHandle == IntPtr.Zero) { - _parentHandle = User32Api.GetParent(Handle); + _parentHandle = User32.GetParent(Handle); _parent = null; } @@ -352,7 +368,7 @@ namespace Greenshot.Base.Core { if (_parentHandle == IntPtr.Zero) { - _parentHandle = User32Api.GetParent(Handle); + _parentHandle = User32.GetParent(Handle); } if (_parentHandle != IntPtr.Zero) @@ -418,7 +434,9 @@ namespace Greenshot.Base.Core { if (_text == null) { - _text = User32Api.GetText(Handle); + var title = new StringBuilder(260, 260); + User32.GetWindowText(Handle, title, title.Capacity); + _text = title.ToString(); } return _text; @@ -430,7 +448,7 @@ namespace Greenshot.Base.Core /// /// Gets the window's class name. /// - public string ClassName => _className ??= User32Api.GetClassname(Handle); + public string ClassName => _className ??= GetClassName(Handle); /// /// Gets/Sets whether the window is iconic (minimized) or not. @@ -439,17 +457,22 @@ namespace Greenshot.Base.Core { get { - return User32Api.IsIconic(Handle) || Location.X <= -32000; + if (IsMetroApp) + { + return !Visible; + } + + return User32.IsIconic(Handle) || Location.X <= -32000; } set { if (value) { - User32Api.SendMessage(Handle, WindowsMessages.WM_SYSCOMMAND, SysCommands.SC_MINIMIZE, IntPtr.Zero); + User32.SendMessage(Handle, (int) WindowsMessages.WM_SYSCOMMAND, (IntPtr) User32.SC_MINIMIZE, IntPtr.Zero); } else { - User32Api.SendMessage(Handle, WindowsMessages.WM_SYSCOMMAND, SysCommands.SC_RESTORE, IntPtr.Zero); + User32.SendMessage(Handle, (int) WindowsMessages.WM_SYSCOMMAND, (IntPtr) User32.SC_RESTORE, IntPtr.Zero); } } } @@ -461,17 +484,37 @@ namespace Greenshot.Base.Core { get { - return User32Api.IsZoomed(Handle); + if (IsApp) + { + if (Visible) + { + Rectangle windowRectangle = WindowRectangle; + foreach (var screen in Screen.AllScreens) + { + if (screen.Bounds.Contains(windowRectangle)) + { + if (windowRectangle.Equals(screen.Bounds)) + { + return true; + } + } + } + } + + return false; + } + + return User32.IsZoomed(Handle); } set { if (value) { - User32Api.SendMessage(Handle, WindowsMessages.WM_SYSCOMMAND, SysCommands.SC_MAXIMIZE, IntPtr.Zero); + User32.SendMessage(Handle, (int) WindowsMessages.WM_SYSCOMMAND, (IntPtr) User32.SC_MAXIMIZE, IntPtr.Zero); } else { - User32Api.SendMessage(Handle, WindowsMessages.WM_SYSCOMMAND, SysCommands.SC_MINIMIZE, IntPtr.Zero); + User32.SendMessage(Handle, (int) WindowsMessages.WM_SYSCOMMAND, (IntPtr) User32.SC_MINIMIZE, IntPtr.Zero); } } } @@ -481,7 +524,7 @@ namespace Greenshot.Base.Core /// public bool IsCloaked { - get => DwmApi.IsWindowCloaked(Handle); + get => DWM.IsWindowCloaked(Handle); } /// @@ -497,7 +540,52 @@ namespace Greenshot.Base.Core return false; } - return User32Api.IsWindowVisible(Handle); + if (IsApp) + { + Rectangle windowRectangle = WindowRectangle; + foreach (Screen screen in Screen.AllScreens) + { + if (screen.Bounds.Contains(windowRectangle)) + { + if (windowRectangle.Equals(screen.Bounds)) + { + // Fullscreen, it's "visible" when AppVisibilityOnMonitor says yes + // Although it might be the other App, this is not "very" important + RECT rect = new RECT(screen.Bounds); + IntPtr monitor = User32.MonitorFromRect(ref rect, User32.MONITOR_DEFAULTTONULL); + if (monitor != IntPtr.Zero) + { + MONITOR_APP_VISIBILITY? monitorAppVisibility = AppVisibility?.GetAppVisibilityOnMonitor(monitor); + //LOG.DebugFormat("App {0} visible: {1} on {2}", Text, monitorAppVisibility, screen.Bounds); + if (monitorAppVisibility == MONITOR_APP_VISIBILITY.MAV_APP_VISIBLE) + { + return true; + } + } + } + else + { + // Is only partly on the screen, when this happens the app is always visible! + return true; + } + } + } + + return false; + } + + if (IsGutter) + { + // gutter is only made available when it's visible + return true; + } + + if (IsAppLauncher) + { + return IsAppLauncherVisible; + } + + return User32.IsWindowVisible(Handle); } } @@ -514,7 +602,7 @@ namespace Greenshot.Base.Core { get { - User32Api.GetWindowThreadProcessId(Handle, out var processId); + User32.GetWindowThreadProcessId(Handle, out var processId); return processId; } } @@ -525,7 +613,7 @@ namespace Greenshot.Base.Core { try { - User32Api.GetWindowThreadProcessId(Handle, out var processId); + User32.GetWindowThreadProcessId(Handle, out var processId); return Process.GetProcessById(processId); } catch (Exception ex) @@ -537,92 +625,114 @@ namespace Greenshot.Base.Core } } - private NativeRect _previousWindowRectangle = NativeRect.Empty; + private Rectangle _previousWindowRectangle = Rectangle.Empty; private long _lastWindowRectangleRetrieveTime; private const long CacheTime = TimeSpan.TicksPerSecond * 2; /// /// Gets the bounding rectangle of the window /// - public NativeRect WindowRectangle + public Rectangle WindowRectangle { get { // Try to return a cached value long now = DateTime.Now.Ticks; - if (!_previousWindowRectangle.IsEmpty && _frozen) return _previousWindowRectangle; - - if (!_previousWindowRectangle.IsEmpty && now - _lastWindowRectangleRetrieveTime <= CacheTime) + if (_previousWindowRectangle.IsEmpty || !_frozen) { - return _previousWindowRectangle; - } - NativeRect windowRect = new(); - if (DwmApi.IsDwmEnabled) - { - bool gotFrameBounds = GetExtendedFrameBounds(out windowRect); - if (IsWin10App) + if (!_previousWindowRectangle.IsEmpty && now - _lastWindowRectangleRetrieveTime <= CacheTime) { - // Pre-Cache for maximized call, this is only on Windows 8 apps (full screen) - if (gotFrameBounds) + return _previousWindowRectangle; + } + + Rectangle windowRect = Rectangle.Empty; + if (DWM.IsDwmEnabled) + { + bool gotFrameBounds = GetExtendedFrameBounds(out windowRect); + if (IsApp) { + // Pre-Cache for maximized call, this is only on Windows 8 apps (full screen) + if (gotFrameBounds) + { + _previousWindowRectangle = windowRect; + _lastWindowRectangleRetrieveTime = now; + } + } + + if (gotFrameBounds && WindowsVersion.IsWindows10OrLater && !Maximised) + { + // Somehow DWM doesn't calculate it corectly, there is a 1 pixel border around the capture + // Remove this border, currently it's fixed but TODO: Make it depend on the OS? + windowRect.Inflate(Conf.Win10BorderCrop); _previousWindowRectangle = windowRect; _lastWindowRectangleRetrieveTime = now; + return windowRect; } } - if (gotFrameBounds && WindowsVersion.IsWindows10OrLater && !Maximised) + if (windowRect.IsEmpty) { - // Somehow DWM doesn't calculate it correctly, there is a 1 pixel border around the capture - // Remove this border, currently it's fixed but TODO: Make it depend on the OS? - windowRect = windowRect.Inflate(Conf.Win10BorderCrop); - _previousWindowRectangle = windowRect; - _lastWindowRectangleRetrieveTime = now; - return windowRect; + if (!GetWindowRect(out windowRect)) + { + Win32Error error = Win32.GetLastErrorCode(); + Log.WarnFormat("Couldn't retrieve the windows rectangle: {0}", Win32.GetMessage(error)); + } } - } - if (windowRect.IsEmpty) - { - if (!GetWindowRect(out windowRect)) + // Correction for maximized windows, only if it's not an app + if (!HasParent && !IsApp && Maximised) { - Win32Error error = Win32.GetLastErrorCode(); - Log.WarnFormat("Couldn't retrieve the windows rectangle: {0}", Win32.GetMessage(error)); + // Only if the border size can be retrieved + if (GetBorderSize(out var size)) + { + windowRect = new Rectangle(windowRect.X + size.Width, windowRect.Y + size.Height, windowRect.Width - (2 * size.Width), + windowRect.Height - (2 * size.Height)); + } } + + _lastWindowRectangleRetrieveTime = now; + // Try to return something valid, by getting returning the previous size if the window doesn't have a Rectangle anymore + if (windowRect.IsEmpty) + { + return _previousWindowRectangle; + } + + _previousWindowRectangle = windowRect; + return windowRect; } - _lastWindowRectangleRetrieveTime = now; - // Try to return something valid, by getting returning the previous size if the window doesn't have a NativeRect anymore - if (windowRect.IsEmpty) - { - return _previousWindowRectangle; - } - - _previousWindowRectangle = windowRect; - return windowRect; - + return _previousWindowRectangle; } } /// /// Gets the location of the window relative to the screen. /// - public NativePoint Location + public Point Location { - get => WindowRectangle.Location; + get + { + Rectangle tmpRectangle = WindowRectangle; + return new Point(tmpRectangle.Left, tmpRectangle.Top); + } } /// /// Gets the size of the window. /// - public NativeSize Size + public Size Size { - get => WindowRectangle.Size; + get + { + Rectangle tmpRectangle = WindowRectangle; + return new Size(tmpRectangle.Right - tmpRectangle.Left, tmpRectangle.Bottom - tmpRectangle.Top); + } } /// /// Get the client rectangle, this is the part of the window inside the borders (drawable area) /// - public NativeRect ClientRectangle + public Rectangle ClientRectangle { get { @@ -641,7 +751,7 @@ namespace Greenshot.Base.Core /// /// Point with the coordinates to check /// true if the point lies within - public bool Contains(NativePoint p) + public bool Contains(Point p) { return WindowRectangle.Contains(p); } @@ -654,11 +764,11 @@ namespace Greenshot.Base.Core { if (Iconic) { - User32Api.SendMessage(Handle, WindowsMessages.WM_SYSCOMMAND, SysCommands.SC_RESTORE, IntPtr.Zero); + User32.SendMessage(Handle, (int) WindowsMessages.WM_SYSCOMMAND, (IntPtr) User32.SC_RESTORE, IntPtr.Zero); } - User32Api.BringWindowToTop(Handle); - User32Api.SetForegroundWindow(Handle); + User32.BringWindowToTop(Handle); + User32.SetForegroundWindow(Handle); // Make sure windows has time to perform the action // TODO: this is BAD practice! while (Iconic) @@ -672,10 +782,8 @@ namespace Greenshot.Base.Core /// public WindowStyleFlags WindowStyle { - get => unchecked( - (WindowStyleFlags)User32Api.GetWindowLongWrapper(Handle, WindowLongIndex.GWL_STYLE).ToInt64() - ); - set => User32Api.SetWindowLongWrapper(Handle, WindowLongIndex.GWL_STYLE, new IntPtr((long) value)); + get => (WindowStyleFlags) User32.GetWindowLongWrapper(Handle, (int) WindowLongIndex.GWL_STYLE); + set => User32.SetWindowLongWrapper(Handle, (int) WindowLongIndex.GWL_STYLE, new IntPtr((long) value)); } /// @@ -685,11 +793,11 @@ namespace Greenshot.Base.Core { get { - var placement = WindowPlacement.Create(); - User32Api.GetWindowPlacement(Handle, ref placement); + var placement = WindowPlacement.Default; + User32.GetWindowPlacement(Handle, ref placement); return placement; } - set { User32Api.SetWindowPlacement(Handle, ref value); } + set { User32.SetWindowPlacement(Handle, ref value); } } /// @@ -697,8 +805,8 @@ namespace Greenshot.Base.Core /// public ExtendedWindowStyleFlags ExtendedWindowStyle { - get => (ExtendedWindowStyleFlags) User32Api.GetWindowLongWrapper(Handle, WindowLongIndex.GWL_EXSTYLE); - set => User32Api.SetWindowLongWrapper(Handle, WindowLongIndex.GWL_EXSTYLE, new IntPtr((uint) value)); + get => (ExtendedWindowStyleFlags) User32.GetWindowLongWrapper(Handle, (int) WindowLongIndex.GWL_EXSTYLE); + set => User32.SetWindowLongWrapper(Handle, (int) WindowLongIndex.GWL_EXSTYLE, new IntPtr((uint) value)); } /// @@ -738,10 +846,10 @@ namespace Greenshot.Base.Core }; // Register the Thumbnail - DwmApi.DwmRegisterThumbnail(tempForm.Handle, Handle, out thumbnailHandle); + DWM.DwmRegisterThumbnail(tempForm.Handle, Handle, out thumbnailHandle); // Get the original size - DwmApi.DwmQueryThumbnailSourceSize(thumbnailHandle, out var sourceSize); + DWM.DwmQueryThumbnailSourceSize(thumbnailHandle, out var sourceSize); if (sourceSize.Width <= 0 || sourceSize.Height <= 0) { @@ -749,9 +857,9 @@ namespace Greenshot.Base.Core } // Calculate the location of the temp form - NativeRect windowRectangle = WindowRectangle; - NativePoint formLocation = windowRectangle.Location; - NativeSize borderSize = new NativeSize(); + Rectangle windowRectangle = WindowRectangle; + Point formLocation = windowRectangle.Location; + Size borderSize = new Size(); bool doesCaptureFit = false; if (!Maximised) { @@ -772,13 +880,12 @@ namespace Greenshot.Base.Core if (!workingArea.AreRectangleCornersVisisble(windowRectangle)) { // If none found we find the biggest screen - - foreach (var displayInfo in DisplayInfo.AllDisplayInfos) + foreach (Screen screen in Screen.AllScreens) { - var newWindowRectangle = new NativeRect(displayInfo.WorkingArea.Location, windowRectangle.Size); + Rectangle newWindowRectangle = new Rectangle(screen.WorkingArea.Location, windowRectangle.Size); if (workingArea.AreRectangleCornersVisisble(newWindowRectangle)) { - formLocation = displayInfo.Bounds.Location; + formLocation = screen.Bounds.Location; doesCaptureFit = true; break; } @@ -793,26 +900,26 @@ namespace Greenshot.Base.Core { //GetClientRect(out windowRectangle); GetBorderSize(out borderSize); - formLocation = new NativePoint(windowRectangle.X - borderSize.Width, windowRectangle.Y - borderSize.Height); + formLocation = new Point(windowRectangle.X - borderSize.Width, windowRectangle.Y - borderSize.Height); } tempForm.Location = formLocation; - tempForm.Size = sourceSize; + tempForm.Size = sourceSize.ToSize(); // Prepare rectangle to capture from the screen. - var captureRectangle = new NativeRect(formLocation.X, formLocation.Y, sourceSize.Width, sourceSize.Height); + Rectangle captureRectangle = new Rectangle(formLocation.X, formLocation.Y, sourceSize.Width, sourceSize.Height); if (Maximised) { // Correct capture size for maximized window by offsetting the X,Y with the border size // and subtracting the border from the size (2 times, as we move right/down for the capture without resizing) - captureRectangle = captureRectangle.Inflate(borderSize.Width, borderSize.Height); + captureRectangle.Inflate(borderSize.Width, borderSize.Height); } else { // TODO: Also 8.x? if (WindowsVersion.IsWindows10OrLater) { - captureRectangle = captureRectangle.Inflate(Conf.Win10BorderCrop); + captureRectangle.Inflate(Conf.Win10BorderCrop); } if (autoMode) @@ -822,7 +929,7 @@ namespace Greenshot.Base.Core { // if GDI is allowed.. (a screenshot won't be better than we comes if we continue) using Process thisWindowProcess = Process; - if (WindowCapture.IsGdiAllowed(thisWindowProcess)) + if (!IsMetroApp && WindowCapture.IsGdiAllowed(thisWindowProcess)) { // we return null which causes the capturing code to try another method. return null; @@ -832,18 +939,18 @@ namespace Greenshot.Base.Core } // Prepare the displaying of the Thumbnail - var props = new DwmThumbnailProperties() + DWM_THUMBNAIL_PROPERTIES props = new DWM_THUMBNAIL_PROPERTIES { Opacity = 255, Visible = true, - Destination = new NativeRect(0, 0, sourceSize.Width, sourceSize.Height) + Destination = new RECT(0, 0, sourceSize.Width, sourceSize.Height) }; - DwmApi.DwmUpdateThumbnailProperties(thumbnailHandle, ref props); + DWM.DwmUpdateThumbnailProperties(thumbnailHandle, ref props); tempForm.Show(); tempFormShown = true; // Intersect with screen - captureRectangle = captureRectangle.Intersect(capture.ScreenBounds); + captureRectangle.Intersect(capture.ScreenBounds); // Destination bitmap for the capture Bitmap capturedBitmap = null; @@ -867,8 +974,11 @@ namespace Greenshot.Base.Core tempForm.BackColor = Color.Black; // Make sure everything is visible tempForm.Refresh(); - // Make sure the application window is active, so the colors & buttons are right - ToForeground(); + if (!IsMetroApp) + { + // Make sure the application window is active, so the colors & buttons are right + ToForeground(); + } // Make sure all changes are processed and visible Application.DoEvents(); @@ -897,15 +1007,19 @@ namespace Greenshot.Base.Core } else { - var colorizationColor = DwmApi.ColorizationColor; + Color colorizationColor = DWM.ColorizationColor; // Modify by losing the transparency and increasing the intensity (as if the background color is white) - tempForm.BackColor = Color.FromArgb(255, (colorizationColor.R + 255) >> 1, (colorizationColor.G + 255) >> 1, (colorizationColor.B + 255) >> 1); + colorizationColor = Color.FromArgb(255, (colorizationColor.R + 255) >> 1, (colorizationColor.G + 255) >> 1, (colorizationColor.B + 255) >> 1); + tempForm.BackColor = colorizationColor; } // Make sure everything is visible tempForm.Refresh(); - // Make sure the application window is active, so the colors & buttons are right - ToForeground(); + if (!IsMetroApp) + { + // Make sure the application window is active, so the colors & buttons are right + ToForeground(); + } // Make sure all changes are processed and visible Application.DoEvents(); @@ -953,7 +1067,7 @@ namespace Greenshot.Base.Core if (thumbnailHandle != IntPtr.Zero) { // Un-register (cleanup), as we are finished we don't need the form or the thumbnail anymore - DwmApi.DwmUnregisterThumbnail(thumbnailHandle); + DWM.DwmUnregisterThumbnail(thumbnailHandle); } if (tempForm != null) @@ -992,7 +1106,7 @@ namespace Greenshot.Base.Core /// /// Apply transparency by comparing a transparent capture with a black and white background /// A "Math.min" makes sure there is no overflow, but this could cause the picture to have shifted colors. - /// The pictures should have been taken without difference, except for the colors. + /// The pictures should have been taken without differency, except for the colors. /// /// Bitmap with the black image /// Bitmap with the black image @@ -1042,67 +1156,49 @@ namespace Greenshot.Base.Core return targetBuffer.UnlockAndReturnBitmap(); } - /// - /// If a window is hidden (Iconic), it also has the specified dimensions. - /// - /// NativeRect - /// bool true if hidden - private bool IsHidden(NativeRect rect) => rect.Width == 65535 && rect.Height == 65535 && rect.Left == 32767 && rect.Top == 32767; - /// /// Helper method to get the window size for DWM Windows /// - /// out NativeRect + /// out Rectangle /// bool true if it worked - private bool GetExtendedFrameBounds(out NativeRect rectangle) + private bool GetExtendedFrameBounds(out Rectangle rectangle) { - var result = DwmApi.DwmGetWindowAttribute(Handle, DwmWindowAttributes.ExtendedFrameBounds, out NativeRect rect, Marshal.SizeOf(typeof(NativeRect))); - if (result.Succeeded()) + int result = DWM.DwmGetWindowAttribute(Handle, DWMWINDOWATTRIBUTE.DWMWA_EXTENDED_FRAME_BOUNDS, out RECT rect, Marshal.SizeOf(typeof(RECT))); + if (result >= 0) { - if (IsHidden(rect)) - { - rect = NativeRect.Empty; - } - rectangle = rect; + rectangle = rect.ToRectangle(); return true; } - rectangle = NativeRect.Empty; + rectangle = Rectangle.Empty; return false; } /// /// Helper method to get the window size for GDI Windows /// - /// out NativeRect + /// out Rectangle /// bool true if it worked - private bool GetClientRect(out NativeRect rectangle) + private bool GetClientRect(out Rectangle rectangle) { var windowInfo = new WindowInfo(); // Get the Window Info for this window - bool result = User32Api.GetWindowInfo(Handle, ref windowInfo); - rectangle = result ? windowInfo.ClientBounds : NativeRect.Empty; + bool result = User32.GetWindowInfo(Handle, ref windowInfo); + rectangle = result ? windowInfo.rcClient.ToRectangle() : Rectangle.Empty; return result; } /// /// Helper method to get the window size for GDI Windows /// - /// out NativeRect + /// out Rectangle /// bool true if it worked - private bool GetWindowRect(out NativeRect rectangle) + private bool GetWindowRect(out Rectangle rectangle) { var windowInfo = new WindowInfo(); // Get the Window Info for this window - bool result = User32Api.GetWindowInfo(Handle, ref windowInfo); - if (IsHidden(windowInfo.Bounds)) - { - rectangle = NativeRect.Empty; - } - else - { - rectangle = result ? windowInfo.Bounds : NativeRect.Empty; - } + bool result = User32.GetWindowInfo(Handle, ref windowInfo); + rectangle = result ? windowInfo.rcWindow.ToRectangle() : Rectangle.Empty; return result; } @@ -1111,12 +1207,12 @@ namespace Greenshot.Base.Core /// /// out Size /// bool true if it worked - private bool GetBorderSize(out NativeSize size) + private bool GetBorderSize(out Size size) { var windowInfo = new WindowInfo(); // Get the Window Info for this window - bool result = User32Api.GetWindowInfo(Handle, ref windowInfo); - size = result ? windowInfo.BorderSize : NativeSize.Empty; + bool result = User32.GetWindowInfo(Handle, ref windowInfo); + size = result ? new Size((int) windowInfo.cxWindowBorders, (int) windowInfo.cyWindowBorders) : Size.Empty; return result; } @@ -1126,7 +1222,7 @@ namespace Greenshot.Base.Core /// hWnd of the window to bring to the foreground public static void ToForeground(IntPtr hWnd) { - var foregroundWindow = User32Api.GetForegroundWindow(); + var foregroundWindow = User32.GetForegroundWindow(); if (hWnd == foregroundWindow) { return; @@ -1139,22 +1235,22 @@ namespace Greenshot.Base.Core return; } - var threadId1 = User32Api.GetWindowThreadProcessId(foregroundWindow, IntPtr.Zero); - var threadId2 = User32Api.GetWindowThreadProcessId(hWnd, IntPtr.Zero); + var threadId1 = User32.GetWindowThreadProcessId(foregroundWindow, IntPtr.Zero); + var threadId2 = User32.GetWindowThreadProcessId(hWnd, IntPtr.Zero); // Show window in foreground. if (threadId1 != threadId2) { - User32Api.AttachThreadInput(threadId1, threadId2, 1); - User32Api.SetForegroundWindow(hWnd); - User32Api.AttachThreadInput(threadId1, threadId2, 0); + User32.AttachThreadInput(threadId1, threadId2, 1); + User32.SetForegroundWindow(hWnd); + User32.AttachThreadInput(threadId1, threadId2, 0); } else { - User32Api.SetForegroundWindow(hWnd); + User32.SetForegroundWindow(hWnd); } - User32Api.BringWindowToTop(hWnd); + User32.BringWindowToTop(hWnd); if (window.Iconic) { @@ -1175,12 +1271,12 @@ namespace Greenshot.Base.Core /// private Region GetRegion() { - using (SafeRegionHandle region = Gdi32Api.CreateRectRgn(0, 0, 0, 0)) + using (SafeRegionHandle region = GDI32.CreateRectRgn(0, 0, 0, 0)) { if (!region.IsInvalid) { - var result = User32Api.GetWindowRgn(Handle, region); - if (result != RegionResults.Error && result != RegionResults.NullRegion) + RegionResult result = User32.GetWindowRgn(Handle, region); + if (result != RegionResult.REGION_ERROR && result != RegionResult.REGION_NULLREGION) { return Region.FromHrgn(region.DangerousGetHandle()); } @@ -1240,7 +1336,7 @@ namespace Greenshot.Base.Core foreach (ProcessThread pT in proc.Threads) { - IntPtr pOpenThread = Kernel32Api.OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint) pT.Id); + IntPtr pOpenThread = Kernel32.OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint) pT.Id); if (pOpenThread == IntPtr.Zero) { @@ -1248,7 +1344,7 @@ namespace Greenshot.Base.Core } frozen = true; - Kernel32Api.SuspendThread(pOpenThread); + Kernel32.SuspendThread(pOpenThread); pT.Dispose(); } } @@ -1279,14 +1375,14 @@ namespace Greenshot.Base.Core foreach (ProcessThread pT in proc.Threads) { - IntPtr pOpenThread = Kernel32Api.OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint) pT.Id); + IntPtr pOpenThread = Kernel32.OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint) pT.Id); if (pOpenThread == IntPtr.Zero) { break; } - Kernel32Api.ResumeThread(pOpenThread); + Kernel32.ResumeThread(pOpenThread); } } @@ -1297,7 +1393,7 @@ namespace Greenshot.Base.Core /// public Image PrintWindow() { - NativeRect windowRect = WindowRectangle; + Rectangle windowRect = WindowRectangle; // Start the capture Exception exceptionOccurred = null; Image returnImage; @@ -1314,14 +1410,14 @@ namespace Greenshot.Base.Core returnImage = ImageHelper.CreateEmpty(windowRect.Width, windowRect.Height, pixelFormat, backgroundColor, 96,96); using Graphics graphics = Graphics.FromImage(returnImage); - using (SafeGraphicsDcHandle graphicsDc = graphics.GetSafeDeviceContext()) + using (SafeDeviceContextHandle graphicsDc = graphics.GetSafeDeviceContext()) { - var pwFlags = WindowsVersion.IsWindows10OrLater ? PrintWindowFlags.PW_RENDERFULLCONTENT : PrintWindowFlags.PW_COMPLETE; - bool printSucceeded = User32Api.PrintWindow(Handle, graphicsDc.DangerousGetHandle(), pwFlags); + var pwFlags = WindowsVersion.IsWindows10OrLater ? PrintWindowFlags.PW_RENDERFULLCONTENT : PrintWindowFlags.PW_ENTIREWINDOW; + bool printSucceeded = User32.PrintWindow(Handle, graphicsDc.DangerousGetHandle(), pwFlags); if (!printSucceeded) { // something went wrong, most likely a "0x80004005" (Access Denied) when using UAC - exceptionOccurred = User32Api.CreateWin32Exception("PrintWindow"); + exceptionOccurred = User32.CreateWin32Exception("PrintWindow"); } } @@ -1345,9 +1441,10 @@ namespace Greenshot.Base.Core if (!HasParent && Maximised) { - Log.Debug("Correcting for maximized window"); + Log.Debug("Correcting for maximalization"); GetBorderSize(out var borderSize); - NativeRect borderRectangle = new NativeRect(borderSize.Width, borderSize.Height, windowRect.Width - (2 * borderSize.Width), windowRect.Height - (2 * borderSize.Height)); + Rectangle borderRectangle = new Rectangle(borderSize.Width, borderSize.Height, windowRect.Width - (2 * borderSize.Width), + windowRect.Height - (2 * borderSize.Height)); ImageHelper.Crop(ref returnImage, ref borderRectangle); } @@ -1370,7 +1467,7 @@ namespace Greenshot.Base.Core /// WindowDetails of the current window public static WindowDetails GetActiveWindow() { - IntPtr hWnd = User32Api.GetForegroundWindow(); + IntPtr hWnd = User32.GetForegroundWindow(); if (hWnd != IntPtr.Zero) { if (IgnoreHandles.Contains(hWnd)) @@ -1397,7 +1494,7 @@ namespace Greenshot.Base.Core /// WindowDetails for the desktop window public static WindowDetails GetDesktopWindow() { - return new WindowDetails(User32Api.GetDesktopWindow()); + return new WindowDetails(User32.GetDesktopWindow()); } /// @@ -1421,9 +1518,9 @@ namespace Greenshot.Base.Core /// /// Recursive "find children which" /// - /// NativePoint to check for - /// WindowDetails - public WindowDetails FindChildUnderPoint(NativePoint point) + /// point to check for + /// + public WindowDetails FindChildUnderPoint(Point point) { if (!Contains(point)) { @@ -1449,13 +1546,25 @@ namespace Greenshot.Base.Core return this; } + /// + /// Retrieves the classname for a hWnd + /// + /// IntPtr with the windows handle + /// String with ClassName + public static string GetClassName(IntPtr hWnd) + { + var classNameBuilder = new StringBuilder(260, 260); + User32.GetClassName(hWnd, classNameBuilder, classNameBuilder.Capacity); + return classNameBuilder.ToString(); + } + /// /// Helper method to decide if a top level window is visible /// /// /// /// - private static bool IsVisible(WindowDetails window, NativeRect screenBounds) + private static bool IsVisible(WindowDetails window, Rectangle screenBounds) { // Ignore invisible if (!window.Visible) @@ -1475,7 +1584,8 @@ namespace Greenshot.Base.Core } // On windows which are visible on the screen - var windowRect = window.WindowRectangle.Intersect(screenBounds); + var windowRect = window.WindowRectangle; + windowRect.Intersect(screenBounds); if (windowRect.IsEmpty) { return false; @@ -1483,7 +1593,7 @@ namespace Greenshot.Base.Core // Skip everything which is not rendered "normally", trying to fix BUG-2017 var exWindowStyle = window.ExtendedWindowStyle; - if (!window.IsWin10App && (exWindowStyle & ExtendedWindowStyleFlags.WS_EX_NOREDIRECTIONBITMAP) != 0) + if (!window.IsApp && !window.IsWin10App && (exWindowStyle & ExtendedWindowStyleFlags.WS_EX_NOREDIRECTIONBITMAP) != 0) { return false; } @@ -1497,7 +1607,14 @@ namespace Greenshot.Base.Core /// List WindowDetails with all the visible top level windows public static IEnumerable GetVisibleWindows() { - var screenBounds = DisplayInfo.ScreenBounds; + Rectangle screenBounds = WindowCapture.GetScreenBounds(); + foreach (var window in GetAppWindows()) + { + if (IsVisible(window, screenBounds)) + { + yield return window; + } + } foreach (var window in GetAllWindows()) { @@ -1508,6 +1625,38 @@ namespace Greenshot.Base.Core } } + /// + /// Get the WindowDetails for all Metro Apps + /// These are all Windows with Classname "Windows.UI.Core.CoreWindow" + /// + /// List WindowDetails with visible metro apps + public static IEnumerable GetAppWindows() + { + // if the appVisibility != null we have Windows 8. + if (AppVisibility == null) + { + yield break; + } + + var nextHandle = User32.FindWindow(AppWindowClass, null); + while (nextHandle != IntPtr.Zero) + { + var metroApp = new WindowDetails(nextHandle); + yield return metroApp; + // Check if we have a gutter! + if (metroApp.Visible && !metroApp.Maximised) + { + var gutterHandle = User32.FindWindow(GutterClass, null); + if (gutterHandle != IntPtr.Zero) + { + yield return new WindowDetails(gutterHandle); + } + } + + nextHandle = User32.FindWindowEx(IntPtr.Zero, nextHandle, AppWindowClass, null); + } + } + /// /// Check if the window is a top level /// @@ -1538,7 +1687,7 @@ namespace Greenshot.Base.Core } // Skip everything which is not rendered "normally", trying to fix BUG-2017 - if (!window.IsWin10App && (exWindowStyle & ExtendedWindowStyleFlags.WS_EX_NOREDIRECTIONBITMAP) != 0) + if (!window.IsApp && !window.IsWin10App && (exWindowStyle & ExtendedWindowStyleFlags.WS_EX_NOREDIRECTIONBITMAP) != 0) { return false; } @@ -1574,6 +1723,14 @@ namespace Greenshot.Base.Core /// List WindowDetails with all the top level windows public static IEnumerable GetTopLevelWindows() { + foreach (var possibleTopLevel in GetAppWindows()) + { + if (IsTopLevel(possibleTopLevel)) + { + yield return possibleTopLevel; + } + } + foreach (var possibleTopLevel in GetAllWindows()) { if (IsTopLevel(possibleTopLevel)) @@ -1649,6 +1806,27 @@ namespace Greenshot.Base.Core } } + /// + /// Get the AppLauncher + /// + /// + public static WindowDetails GetAppLauncher() + { + // Only if Windows 8 (or higher) + if (AppVisibility == null) + { + return null; + } + + IntPtr appLauncher = User32.FindWindow(ApplauncherClass, null); + if (appLauncher != IntPtr.Zero) + { + return new WindowDetails(appLauncher); + } + + return null; + } + /// /// Return true if the metro-app-launcher is visible /// @@ -1680,8 +1858,9 @@ namespace Greenshot.Base.Core result.AppendLine($"Size: {WindowRectangle.Size}"); result.AppendLine($"HasParent: {HasParent}"); result.AppendLine($"IsWin10App: {IsWin10App}"); + result.AppendLine($"IsApp: {IsApp}"); result.AppendLine($"Visible: {Visible}"); - result.AppendLine($"IsWindowVisible: {User32Api.IsWindowVisible(Handle)}"); + result.AppendLine($"IsWindowVisible: {User32.IsWindowVisible(Handle)}"); result.AppendLine($"IsCloaked: {IsCloaked}"); result.AppendLine($"Iconic: {Iconic}"); result.AppendLine($"IsBackgroundWin10App: {IsBackgroundWin10App}"); diff --git a/src/Greenshot.Base/Core/WindowsEnumerator.cs b/src/Greenshot.Base/Core/WindowsEnumerator.cs index 1db1f5007..df3de72b0 100644 --- a/src/Greenshot.Base/Core/WindowsEnumerator.cs +++ b/src/Greenshot.Base/Core/WindowsEnumerator.cs @@ -21,7 +21,8 @@ using System; using System.Collections.Generic; -using Dapplo.Windows.User32; +using System.Text; +using Greenshot.Base.UnmanagedHelpers; namespace Greenshot.Base.Core { @@ -43,13 +44,15 @@ namespace Greenshot.Base.Core public WindowsEnumerator GetWindows(IntPtr hWndParent, string classname) { Items = new List(); - User32Api.EnumChildWindows(hWndParent, OnWindowEnum, IntPtr.Zero); + User32.EnumChildWindows(hWndParent, WindowEnum, IntPtr.Zero); bool hasParent = !IntPtr.Zero.Equals(hWndParent); string parentText = null; if (hasParent) { - parentText = User32Api.GetText(hWndParent); + var title = new StringBuilder(260, 260); + User32.GetWindowText(hWndParent, title, title.Capacity); + parentText = title.ToString(); } var windows = new List(); @@ -71,6 +74,17 @@ namespace Greenshot.Base.Core return this; } + /// + /// The enum Windows callback. + /// + /// Window Handle + /// Application defined value + /// 1 to continue enumeration, 0 to stop + private int WindowEnum(IntPtr hWnd, int lParam) + { + return OnWindowEnum(hWnd) ? 1 : 0; + } + /// /// Called whenever a new window is about to be added /// by the Window enumeration called from GetWindows. @@ -80,9 +94,8 @@ namespace Greenshot.Base.Core /// be empty. /// /// Window handle to add - /// /// True to continue enumeration, False to stop - private bool OnWindowEnum(IntPtr hWnd, IntPtr lParam) + private bool OnWindowEnum(IntPtr hWnd) { if (!WindowDetails.IsIgnoreHandle(hWnd)) { diff --git a/src/Greenshot.Base/Core/WmInputLangChangeRequestFilter.cs b/src/Greenshot.Base/Core/WmInputLangChangeRequestFilter.cs index fb95dccb3..a76fcdf21 100644 --- a/src/Greenshot.Base/Core/WmInputLangChangeRequestFilter.cs +++ b/src/Greenshot.Base/Core/WmInputLangChangeRequestFilter.cs @@ -20,7 +20,7 @@ */ using System.Windows.Forms; -using Dapplo.Windows.Messages.Enumerations; +using Greenshot.Base.UnmanagedHelpers.Enums; using log4net; namespace Greenshot.Base.Core @@ -51,13 +51,16 @@ namespace Greenshot.Base.Core public static bool PreFilterMessageExternal(ref Message m) { WindowsMessages message = (WindowsMessages) m.Msg; - if (message != WindowsMessages.WM_INPUTLANGCHANGEREQUEST && message != WindowsMessages.WM_INPUTLANGCHANGE) return false; + if (message == WindowsMessages.WM_INPUTLANGCHANGEREQUEST || message == WindowsMessages.WM_INPUTLANGCHANGE) + { + LOG.WarnFormat("Filtering: {0}, {1:X} - {2:X} - {3:X}", message, m.LParam.ToInt64(), m.WParam.ToInt64(), m.HWnd.ToInt64()); + // For now we always return true + return true; + // But it could look something like this: + //return (m.LParam.ToInt64() | 0x7FFFFFFF) != 0; + } - LOG.DebugFormat("Filtering: {0}, {1:X} - {2:X} - {3:X}", message, m.LParam.ToInt64(), m.WParam.ToInt64(), m.HWnd.ToInt64()); - // For now we always return true - return true; - // But it could look something like this: - //return (m.LParam.ToInt64() | 0x7FFFFFFF) != 0; + return false; } } } \ No newline at end of file diff --git a/src/Greenshot.Base/Effects/DropShadowEffect.cs b/src/Greenshot.Base/Effects/DropShadowEffect.cs index b597e4463..8f69854bc 100644 --- a/src/Greenshot.Base/Effects/DropShadowEffect.cs +++ b/src/Greenshot.Base/Effects/DropShadowEffect.cs @@ -23,7 +23,6 @@ using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Core; namespace Greenshot.Base.Effects @@ -43,7 +42,7 @@ namespace Greenshot.Base.Effects public int ShadowSize { get; set; } - public NativePoint ShadowOffset { get; set; } + public Point ShadowOffset { get; set; } public virtual void Reset() { diff --git a/src/Greenshot.Base/Greenshot.Base.csproj b/src/Greenshot.Base/Greenshot.Base.csproj index 550682cd0..1c9b9b84c 100644 --- a/src/Greenshot.Base/Greenshot.Base.csproj +++ b/src/Greenshot.Base/Greenshot.Base.csproj @@ -3,22 +3,12 @@ true - - none - false - - - - - - - - - - - + + + + diff --git a/src/Greenshot.Base/Hooking/WindowsEventHook.cs b/src/Greenshot.Base/Hooking/WindowsEventHook.cs new file mode 100644 index 000000000..7982cc35a --- /dev/null +++ b/src/Greenshot.Base/Hooking/WindowsEventHook.cs @@ -0,0 +1,151 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Greenshot.Base.UnmanagedHelpers.Enums; + +namespace Greenshot.Base.Hooking +{ + /// + /// The WinEventHook can register handlers to become important windows events + /// This makes it possible to know a.o. when a window is created, moved, updated and closed. + /// + public class WindowsEventHook : IDisposable + { + private readonly WinEventDelegate _winEventHandler; + private GCHandle _gcHandle; + + /// + /// Used with Register hook + /// + /// + /// + /// + /// + /// + /// + public delegate void WinEventHandler(WinEvent eventType, IntPtr hWnd, EventObjects idObject, int idChild, uint dwEventThread, uint dwmsEventTime); + + /// + /// Create a WindowsEventHook object + /// + public WindowsEventHook() + { + _winEventHandler = WinEventDelegateHandler; + _gcHandle = GCHandle.Alloc(_winEventHandler); + } + + [DllImport("user32", SetLastError = true)] + private static extern bool UnhookWinEvent(IntPtr hWinEventHook); + + [DllImport("user32", SetLastError = true)] + private static extern IntPtr SetWinEventHook(WinEvent eventMin, WinEvent eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, int idProcess, int idThread, + WinEventHookFlags dwFlags); + + /// + /// Used with SetWinEventHook + /// + /// + /// + /// + /// + /// + /// + /// + private delegate void WinEventDelegate(IntPtr hWinEventHook, WinEvent eventType, IntPtr hWnd, EventObjects idObject, int idChild, uint dwEventThread, uint dwmsEventTime); + + private readonly IDictionary _winEventHandlers = new Dictionary(); + + /// + /// Are hooks active? + /// + public bool IsHooked => _winEventHandlers.Count > 0; + + /// + /// Hook a WinEvent + /// + /// + /// + /// true if success + public void Hook(WinEvent winEvent, WinEventHandler winEventHandler) + { + Hook(winEvent, winEvent, winEventHandler); + } + + /// + /// Hook a WinEvent + /// + /// + /// + /// + public void Hook(WinEvent winEventStart, WinEvent winEventEnd, WinEventHandler winEventHandler) + { + var hookPtr = SetWinEventHook(winEventStart, winEventEnd, IntPtr.Zero, _winEventHandler, 0, 0, + WinEventHookFlags.WINEVENT_SKIPOWNPROCESS | WinEventHookFlags.WINEVENT_OUTOFCONTEXT); + _winEventHandlers.Add(hookPtr, winEventHandler); + } + + /// + /// Remove all hooks + /// + private void Unhook() + { + foreach (var hookPtr in _winEventHandlers.Keys) + { + if (hookPtr != IntPtr.Zero) + { + UnhookWinEvent(hookPtr); + } + } + + _winEventHandlers.Clear(); + _gcHandle.Free(); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Unhook(); + } + + /// + /// Call the WinEventHandler for this event + /// + /// + /// + /// + /// + /// + /// + /// + private void WinEventDelegateHandler(IntPtr hWinEventHook, WinEvent eventType, IntPtr hWnd, EventObjects idObject, int idChild, uint dwEventThread, uint dwmsEventTime) + { + if (_winEventHandlers.TryGetValue(hWinEventHook, out var handler)) + { + handler(eventType, hWnd, idObject, idChild, dwEventThread, dwmsEventTime); + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/Hooking/WindowsOpenCloseMonitor.cs b/src/Greenshot.Base/Hooking/WindowsOpenCloseMonitor.cs new file mode 100644 index 000000000..b8a4781a3 --- /dev/null +++ b/src/Greenshot.Base/Hooking/WindowsOpenCloseMonitor.cs @@ -0,0 +1,180 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using Greenshot.Base.Core; +using Greenshot.Base.UnmanagedHelpers.Enums; + +namespace Greenshot.Base.Hooking +{ + /// + /// Event arguments for the WindowOpenCloseEvent + /// + public class WindowOpenCloseEventArgs : EventArgs + { + public bool IsOpen { get; set; } + + /// + /// HWnd of the window which has a changed title + /// + public IntPtr HWnd { get; set; } + + /// + /// Title which is changed + /// + public string Title { get; set; } + + public string ClassName { get; set; } + } + + /// + /// Delegate for the window open close event + /// + /// + public delegate void WindowOpenCloseEventDelegate(WindowOpenCloseEventArgs eventArgs); + + /// + /// Monitor all new and destroyed windows + /// + public sealed class WindowsOpenCloseMonitor : IDisposable + { + private WindowsEventHook _hook; + private readonly object _lockObject = new object(); + + // ReSharper disable once InconsistentNaming + private event WindowOpenCloseEventDelegate _windowOpenCloseEvent; + + /// + /// Add / remove event handler to the title monitor + /// + public event WindowOpenCloseEventDelegate WindowOpenCloseChangeEvent + { + add + { + lock (_lockObject) + { + if (_hook == null) + { + _hook = new WindowsEventHook(); + _hook.Hook(WinEvent.EVENT_OBJECT_CREATE, WinEvent.EVENT_OBJECT_DESTROY, WinEventHandler); + } + + _windowOpenCloseEvent += value; + } + } + remove + { + lock (_lockObject) + { + _windowOpenCloseEvent -= value; + if (_windowOpenCloseEvent == null || _windowOpenCloseEvent.GetInvocationList().Length == 0) + { + if (_hook != null) + { + _hook.Dispose(); + _hook = null; + } + } + } + } + } + + /// + /// WinEventDelegate for the creation and destruction + /// + /// + /// + /// + /// + /// + /// + private void WinEventHandler(WinEvent eventType, IntPtr hWnd, EventObjects idObject, int idChild, uint dwEventThread, uint dwmsEventTime) + { + if (hWnd == IntPtr.Zero || idObject != EventObjects.OBJID_WINDOW) + { + return; + } + + if (eventType == WinEvent.EVENT_OBJECT_CREATE) + { + if (_windowOpenCloseEvent != null) + { + var windowsDetails = new WindowDetails(hWnd); + _windowOpenCloseEvent(new WindowOpenCloseEventArgs + { + HWnd = hWnd, + IsOpen = true, + Title = windowsDetails.Text, + ClassName = windowsDetails.ClassName + }); + } + } + + if (eventType == WinEvent.EVENT_OBJECT_DESTROY) + { + _windowOpenCloseEvent?.Invoke(new WindowOpenCloseEventArgs + { + HWnd = hWnd, + IsOpen = false + }); + } + } + + private bool _disposedValue; // To detect redundant calls + + /// + /// Dispose the underlying hook + /// + public void Dispose(bool disposing) + { + if (_disposedValue) + { + return; + } + + lock (_lockObject) + { + _hook?.Dispose(); + } + + _disposedValue = true; + } + + /// + /// Make sure the finalizer disposes the underlying hook + /// + ~WindowsOpenCloseMonitor() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(false); + } + + /// + /// Dispose the underlying hook + /// + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/Hooking/WindowsTitleMonitor.cs b/src/Greenshot.Base/Hooking/WindowsTitleMonitor.cs new file mode 100644 index 000000000..f93542228 --- /dev/null +++ b/src/Greenshot.Base/Hooking/WindowsTitleMonitor.cs @@ -0,0 +1,165 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using Greenshot.Base.Core; +using Greenshot.Base.UnmanagedHelpers.Enums; + +namespace Greenshot.Base.Hooking +{ + /// + /// Event arguments for the TitleChangeEvent + /// + public class TitleChangeEventArgs : EventArgs + { + /// + /// HWnd of the window which has a changed title + /// + public IntPtr HWnd { get; set; } + + /// + /// Title which is changed + /// + public string Title { get; set; } + } + + /// + /// Delegate for the title change event + /// + /// + public delegate void TitleChangeEventDelegate(TitleChangeEventArgs eventArgs); + + /// + /// Monitor all title changes + /// + public sealed class WindowsTitleMonitor : IDisposable + { + private WindowsEventHook _hook; + private readonly object _lockObject = new object(); + + // ReSharper disable once InconsistentNaming + private event TitleChangeEventDelegate _titleChangeEvent; + + /// + /// Add / remove event handler to the title monitor + /// + public event TitleChangeEventDelegate TitleChangeEvent + { + add + { + lock (_lockObject) + { + if (_hook == null) + { + _hook = new WindowsEventHook(); + _hook.Hook(WinEvent.EVENT_OBJECT_NAMECHANGE, WinEventHandler); + } + + _titleChangeEvent += value; + } + } + remove + { + lock (_lockObject) + { + _titleChangeEvent -= value; + if (_titleChangeEvent == null || _titleChangeEvent.GetInvocationList().Length == 0) + { + if (_hook != null) + { + _hook.Dispose(); + _hook = null; + } + } + } + } + } + + /// + /// WinEventDelegate for the creation & destruction + /// + /// + /// + /// + /// + /// + /// + private void WinEventHandler(WinEvent eventType, IntPtr hWnd, EventObjects idObject, int idChild, uint dwEventThread, uint dwmsEventTime) + { + if (hWnd == IntPtr.Zero || idObject != EventObjects.OBJID_WINDOW) + { + return; + } + + if (eventType == WinEvent.EVENT_OBJECT_NAMECHANGE) + { + if (_titleChangeEvent != null) + { + string newTitle = new WindowDetails(hWnd).Text; + _titleChangeEvent(new TitleChangeEventArgs + { + HWnd = hWnd, + Title = newTitle + }); + } + } + } + + private bool _disposedValue; // To detect redundant calls + + /// + /// Dispose the underlying hook + /// + public void Dispose(bool disposing) + { + if (_disposedValue) + { + return; + } + + lock (_lockObject) + { + _hook?.Dispose(); + } + + _disposedValue = true; + } + + /// + /// Make sure the finalizer disposes the underlying hook + /// + ~WindowsTitleMonitor() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(false); + } + + /// + /// Dispose the underlying hook + /// + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/Interfaces/Drawing/Adorners/IAdorner.cs b/src/Greenshot.Base/Interfaces/Drawing/Adorners/IAdorner.cs index 1894eb647..624c156b1 100644 --- a/src/Greenshot.Base/Interfaces/Drawing/Adorners/IAdorner.cs +++ b/src/Greenshot.Base/Interfaces/Drawing/Adorners/IAdorner.cs @@ -22,7 +22,6 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; -using Dapplo.Windows.Common.Structs; namespace Greenshot.Base.Interfaces.Drawing.Adorners { @@ -36,7 +35,7 @@ namespace Greenshot.Base.Interfaces.Drawing.Adorners /// /// These are the bounds of the adorner /// - NativeRect Bounds { get; } + Rectangle Bounds { get; } /// /// The current edit status, this is needed to locate the adorner to send events to @@ -54,7 +53,7 @@ namespace Greenshot.Base.Interfaces.Drawing.Adorners /// /// Point to test /// true if so - bool HitTest(NativePoint point); + bool HitTest(Point point); /// /// Handle the MouseDown event @@ -98,21 +97,6 @@ namespace Greenshot.Base.Interfaces.Drawing.Adorners /// Adjust UI elements to the supplied DPI settings /// /// - void AdjustToDpi(int dpi); - - /// - /// The color of the lines around the adorner - /// - Color OutlineColor { get; set; } - - /// - /// The color of the fill of the adorner - /// - Color FillColor { get; set; } - - /// - /// This is to TAG the adorner so we know the type - /// - string Tag { get; set; } + void AdjustToDpi(uint dpi); } } \ No newline at end of file diff --git a/src/Greenshot.Base/Interfaces/Drawing/IDrawableContainer.cs b/src/Greenshot.Base/Interfaces/Drawing/IDrawableContainer.cs index 80e2bd140..1742b8054 100644 --- a/src/Greenshot.Base/Interfaces/Drawing/IDrawableContainer.cs +++ b/src/Greenshot.Base/Interfaces/Drawing/IDrawableContainer.cs @@ -22,23 +22,15 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Drawing; using System.Drawing.Drawing2D; -using System.Windows.Forms; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Interfaces.Drawing.Adorners; namespace Greenshot.Base.Interfaces.Drawing { public interface IDrawableContainer : INotifyPropertyChanged, IDisposable { - /// - /// The parent surface where this IDrawableContainer is on - /// ISurface Parent { get; set; } - - /// - /// Is this IDrawableContainer selected on the surface - /// bool Selected { get; set; } int Left { get; set; } @@ -49,43 +41,28 @@ namespace Greenshot.Base.Interfaces.Drawing int Height { get; set; } - NativePoint Location { get; } + Point Location { get; } - NativeSize Size { get; } + Size Size { get; } - NativeRect Bounds { get; } + Rectangle Bounds { get; } - NativeRect DrawingBounds { get; } + Rectangle DrawingBounds { get; } - void ApplyBounds(NativeRectFloat newBounds); + void ApplyBounds(RectangleF newBounds); bool HasFilters { get; } EditStatus Status { get; set; } - void Invalidate(); - bool ClickableAt(int x, int y); - void MoveBy(int x, int y); - void Transform(Matrix matrix); - bool HandleMouseDown(int x, int y); - void HandleMouseUp(int x, int y); - bool HandleMouseMove(int x, int y); - bool InitContent(); - - /// - /// Defines if the drawable container participates in undo / redo - /// - bool IsUndoable { get; } - void MakeBoundsChangeUndoable(bool allowMerge); - EditStatus DefaultEditMode { get; } /// @@ -93,23 +70,10 @@ namespace Greenshot.Base.Interfaces.Drawing /// IList Adorners { get; } - /// - /// Is confirm/cancel possible for this container - /// - bool IsConfirmable { get; } - /// /// Adjust UI elements to the supplied DPI settings /// - /// int - void AdjustToDpi(int dpi); - - /// - /// Enable a way for elements to add a context menu entry - /// - /// ContextMenuStrip - /// ISurface - /// MouseEventArgs - void AddContextMenuItems(ContextMenuStrip menu, ISurface surface, MouseEventArgs mouseEventArgs); + /// uint + void AdjustToDpi(uint dpi); } } \ No newline at end of file diff --git a/src/Greenshot.Base/Interfaces/Drawing/IDrawableContainerList.cs b/src/Greenshot.Base/Interfaces/Drawing/IDrawableContainerList.cs index 795c36afe..39235f63b 100644 --- a/src/Greenshot.Base/Interfaces/Drawing/IDrawableContainerList.cs +++ b/src/Greenshot.Base/Interfaces/Drawing/IDrawableContainerList.cs @@ -24,7 +24,6 @@ using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; -using Dapplo.Windows.Common.Structs; namespace Greenshot.Base.Interfaces.Drawing { @@ -36,16 +35,16 @@ namespace Greenshot.Base.Interfaces.Drawing ISurface Parent { get; set; } EditStatus Status { get; set; } - NativeRect DrawingBounds { get; } + Rectangle DrawingBounds { get; } void MakeBoundsChangeUndoable(bool allowMerge); void Transform(Matrix matrix); void MoveBy(int dx, int dy); bool ClickableAt(int x, int y); IDrawableContainer ClickableElementAt(int x, int y); void OnDoubleClick(); - bool HasIntersectingFilters(NativeRect clipRectangle); - bool IntersectsWith(NativeRect clipRectangle); - void Draw(Graphics g, Bitmap bitmap, RenderMode renderMode, NativeRect clipRectangle); + bool HasIntersectingFilters(Rectangle clipRectangle); + bool IntersectsWith(Rectangle clipRectangle); + void Draw(Graphics g, Bitmap bitmap, RenderMode renderMode, Rectangle clipRectangle); void SetForegroundColor(Color color); void SetBackgroundColor(Color color); int IncreaseLineThickness(int increaseBy); @@ -59,6 +58,6 @@ namespace Greenshot.Base.Interfaces.Drawing void PushElementsToBottom(IDrawableContainerList elements); void ShowContextMenu(MouseEventArgs e, ISurface surface); void HandleFieldChangedEvent(object sender, FieldChangedEventArgs e); - void AdjustToDpi(int dpi); + void AdjustToDpi(uint dpi); } } \ No newline at end of file diff --git a/src/Greenshot.Base/Interfaces/ICapture.cs b/src/Greenshot.Base/Interfaces/ICapture.cs index d1c8881a8..6e4fc31b2 100644 --- a/src/Greenshot.Base/Interfaces/ICapture.cs +++ b/src/Greenshot.Base/Interfaces/ICapture.cs @@ -21,7 +21,6 @@ using System; using System.Drawing; -using Dapplo.Windows.Common.Structs; namespace Greenshot.Base.Interfaces { @@ -48,7 +47,7 @@ namespace Greenshot.Base.Interfaces /// /// Bounds on the screen from which the capture comes /// - NativeRect ScreenBounds { get; set; } + Rectangle ScreenBounds { get; set; } /// /// The cursor @@ -63,18 +62,18 @@ namespace Greenshot.Base.Interfaces /// /// Location of the cursor /// - NativePoint CursorLocation { get; set; } + Point CursorLocation { get; set; } /// /// Location of the capture /// - NativePoint Location { get; set; } + Point Location { get; set; } /// /// Crops the capture to the specified rectangle (with Bitmap coordinates!) /// - /// NativeRect with bitmap coordinates - bool Crop(NativeRect cropRectangle); + /// Rectangle with bitmap coordinates + bool Crop(Rectangle cropRectangle); /// /// Apply a translate to the mouse location. e.g. needed for crop diff --git a/src/Greenshot.Base/Interfaces/IFileFormatHandler.cs b/src/Greenshot.Base/Interfaces/IFileFormatHandler.cs deleted file mode 100644 index bd5558857..000000000 --- a/src/Greenshot.Base/Interfaces/IFileFormatHandler.cs +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System.Collections.Generic; -using System.Drawing; -using System.IO; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Base.Interfaces.Plugin; - -namespace Greenshot.Base.Interfaces -{ - /// - /// The possible actions a IFileFormatHandler might support - /// - public enum FileFormatHandlerActions - { - SaveToStream, - LoadFromStream, - LoadDrawableFromStream - } - - /// - /// This interface is for code to implement the loading and saving of certain file formats - /// - public interface IFileFormatHandler - { - /// - /// Registry for all the extensions this IFileFormatHandler support - /// - IDictionary> SupportedExtensions { get; } - - /// - /// Priority (from high int.MinValue, low int.MaxValue) of this IFileFormatHandler for the specified action and extension - /// This should be used to sort the possible IFileFormatHandler - /// - /// FileFormatHandlerActions - /// string - /// int specifying the priority for the action and extension - public int PriorityFor(FileFormatHandlerActions fileFormatHandlerAction, string extension); - - /// - /// Try to save the specified bitmap to the stream in the format belonging to the extension - /// - /// Bitmap - /// Stream - /// extension - /// ISurface with the elements for those file types which can store a surface (.greenshot) - /// SurfaceOutputSettings - /// bool true if it was successful - public bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null); - - /// - /// - /// - /// - /// - /// - /// bool true if it was successful - public bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap); - - /// - /// Try to load a drawable container from the stream - /// - /// Stream - /// string - /// ISurface - /// IEnumerable{IDrawableContainer} - public IEnumerable LoadDrawablesFromStream(Stream stream, string extension, ISurface parentSurface = null); - } -} diff --git a/src/Greenshot.Base/Interfaces/IServiceLocator.cs b/src/Greenshot.Base/Interfaces/IServiceLocator.cs index 5cf1fb529..c22e3463b 100644 --- a/src/Greenshot.Base/Interfaces/IServiceLocator.cs +++ b/src/Greenshot.Base/Interfaces/IServiceLocator.cs @@ -32,7 +32,7 @@ namespace Greenshot.Base.Interfaces /// /// Service to find /// IEnumerable{TService} - IReadOnlyList GetAllInstances(); + IEnumerable GetAllInstances(); /// /// Get the only instance of the specified service diff --git a/src/Greenshot.Base/Interfaces/ISurface.cs b/src/Greenshot.Base/Interfaces/ISurface.cs index 4b39fb9b8..d5df7ff44 100644 --- a/src/Greenshot.Base/Interfaces/ISurface.cs +++ b/src/Greenshot.Base/Interfaces/ISurface.cs @@ -21,10 +21,8 @@ using System; using System.Drawing; -using System.Drawing.Drawing2D; using System.IO; using System.Windows.Forms; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Core; using Greenshot.Base.Effects; using Greenshot.Base.Interfaces.Drawing; @@ -102,49 +100,13 @@ namespace Greenshot.Base.Interfaces long SaveElementsToStream(Stream stream); void LoadElementsFromStream(Stream stream); - /// - /// Provides the selected elements - /// - IDrawableContainerList SelectedElements { get; } - - /// - /// Is there an element selected on the surface? - /// bool HasSelectedElements { get; } - - /// - /// Remove all selected elements - /// void RemoveSelectedElements(); - - /// - /// Cut the selected elements to the clipboard - /// void CutSelectedElements(); - - /// - /// Copy the selected elements to the clipboard - /// void CopySelectedElements(); - - /// - /// Paste the elements from the clipboard - /// void PasteElementFromClipboard(); - - /// - /// Duplicate the selected elements - /// void DuplicateSelectedElements(); - - /// - /// Deselected the specified element - /// void DeselectElement(IDrawableContainer container, bool generateEvents = true); - - /// - /// Deselected all elements - /// void DeselectAllElements(); /// @@ -186,8 +148,8 @@ namespace Greenshot.Base.Interfaces /// Invalidates the specified region of the Surface. /// Takes care of the Surface zoom level, accepts rectangle in the coordinate space of the Image. /// - /// NativeRect Bounding rectangle for updated elements, in the coordinate space of the Image. - void InvalidateElements(NativeRect rectangleToInvalidate); + /// Bounding rectangle for updated elements, in the coordinate space of the Image. + void InvalidateElements(Rectangle rectangleToInvalidate); bool Modified { get; set; } string LastSaveFullPath { get; set; } @@ -215,54 +177,29 @@ namespace Greenshot.Base.Interfaces Fraction ZoomFactor { get; set; } /// - /// Translate a point from image coordinate space to surface coordinate space. + /// Translate a point from image coorditate space to surface coordinate space. /// /// A point in the coordinate space of the image. - NativePoint ToSurfaceCoordinates(NativePoint point); + Point ToSurfaceCoordinates(Point point); /// - /// Translate a rectangle from image coordinate space to surface coordinate space. + /// Translate a rectangle from image coorditate space to surface coordinate space. /// - /// NativeRect in the coordinate space of the image. - NativeRect ToSurfaceCoordinates(NativeRect rc); + /// A rectangle in the coordinate space of the image. + Rectangle ToSurfaceCoordinates(Rectangle rc); /// - /// Translate a point from surface coordinate space to image coordinate space. + /// Translate a point from surface coorditate space to image coordinate space. /// - /// NativePoint in the coordinate space of the surface. - NativePoint ToImageCoordinates(NativePoint point); + /// A point in the coordinate space of the surface. + Point ToImageCoordinates(Point point); /// - /// Translate a NativeRect from surface coordinate space to image coordinate space. + /// Translate a rectangle from surface coorditate space to image coordinate space. /// - /// NativeRect in the coordinate space of the surface. - NativeRect ToImageCoordinates(NativeRect rc); + /// A rectangle in the coordinate space of the surface. + Rectangle ToImageCoordinates(Rectangle rc); - /// - /// Make it possible to undo the specified IMemento - /// - /// IMemento - /// bool to specify if the action can be merged, e.g. we do not want an undo for every part of a resize void MakeUndoable(IMemento memento, bool allowMerge); - - /// - /// The IFieldAggregator - /// - IFieldAggregator FieldAggregator { get; } - - /// - /// This reverses a change of the background image - /// - /// Image - /// Matrix - void UndoBackgroundChange(Image previous, Matrix matrix); - - /// - /// The most recent DPI value that was used - /// - public int CurrentDpi - { - get; - } } } \ No newline at end of file diff --git a/src/Greenshot.Base/Interfaces/Ocr/Line.cs b/src/Greenshot.Base/Interfaces/Ocr/Line.cs index ff5914a4e..fc21f72ae 100644 --- a/src/Greenshot.Base/Interfaces/Ocr/Line.cs +++ b/src/Greenshot.Base/Interfaces/Ocr/Line.cs @@ -1,26 +1,4 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; +using System.Drawing; namespace Greenshot.Base.Interfaces.Ocr { @@ -29,7 +7,7 @@ namespace Greenshot.Base.Interfaces.Ocr /// public class Line { - private NativeRect? _calculatedBounds; + private Rectangle? _calculatedBounds; /// /// Constructor will preallocate the number of words @@ -57,18 +35,18 @@ namespace Greenshot.Base.Interfaces.Ocr /// /// Calculate the bounds of the words /// - /// NativeRect - private NativeRect CalculateBounds() + /// Rectangle + private Rectangle CalculateBounds() { if (Words.Length == 0) { - return NativeRect.Empty; + return Rectangle.Empty; } var result = Words[0].Bounds; for (var index = 0; index < Words.Length; index++) { - result = result.Union(Words[index].Bounds); + result = Rectangle.Union(result, Words[index].Bounds); } return result; @@ -77,7 +55,7 @@ namespace Greenshot.Base.Interfaces.Ocr /// /// Return the calculated bounds for the whole line /// - public NativeRect CalculatedBounds + public Rectangle CalculatedBounds { get { return _calculatedBounds ??= CalculateBounds(); } } @@ -91,7 +69,9 @@ namespace Greenshot.Base.Interfaces.Ocr { foreach (var word in Words) { - word.Bounds = word.Bounds.Offset(x, y); + var location = word.Bounds; + location.Offset(x, y); + word.Bounds = location; } _calculatedBounds = null; diff --git a/src/Greenshot.Base/Interfaces/Ocr/OcrInformation.cs b/src/Greenshot.Base/Interfaces/Ocr/OcrInformation.cs index 216265099..d7cb82559 100644 --- a/src/Greenshot.Base/Interfaces/Ocr/OcrInformation.cs +++ b/src/Greenshot.Base/Interfaces/Ocr/OcrInformation.cs @@ -1,25 +1,4 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Text; diff --git a/src/Greenshot.Base/Interfaces/Ocr/Word.cs b/src/Greenshot.Base/Interfaces/Ocr/Word.cs index 0e6920f40..467b5278b 100644 --- a/src/Greenshot.Base/Interfaces/Ocr/Word.cs +++ b/src/Greenshot.Base/Interfaces/Ocr/Word.cs @@ -1,25 +1,4 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using Dapplo.Windows.Common.Structs; +using System.Drawing; namespace Greenshot.Base.Interfaces.Ocr { @@ -36,6 +15,6 @@ namespace Greenshot.Base.Interfaces.Ocr /// /// The bounds of the word /// - public NativeRect Bounds { get; set; } + public Rectangle Bounds { get; set; } } } \ No newline at end of file diff --git a/src/Greenshot.Base/Interfaces/Plugin/AssemblyPluginIdentifierAttribute.cs b/src/Greenshot.Base/Interfaces/Plugin/AssemblyPluginIdentifierAttribute.cs deleted file mode 100644 index 2be14c7a8..000000000 --- a/src/Greenshot.Base/Interfaces/Plugin/AssemblyPluginIdentifierAttribute.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; - -namespace Greenshot.Base.Interfaces.Plugin -{ - /// - /// Attribute to specify a custom plugin identifier at assembly level - /// - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] - public class AssemblyPluginIdentifierAttribute : Attribute - { - /// - /// The identifier used for the plugin in configuration - /// - public string Identifier { get; } - - /// - /// Constructor for the plugin identifier attribute - /// - /// The identifier for the plugin in configuration - public AssemblyPluginIdentifierAttribute(string identifier) - { - Identifier = identifier; - } - } -} \ No newline at end of file diff --git a/src/Greenshot.Base/Interfaces/Plugin/SurfaceOutputSettings.cs b/src/Greenshot.Base/Interfaces/Plugin/SurfaceOutputSettings.cs index efce47bbb..ef2a58e2f 100644 --- a/src/Greenshot.Base/Interfaces/Plugin/SurfaceOutputSettings.cs +++ b/src/Greenshot.Base/Interfaces/Plugin/SurfaceOutputSettings.cs @@ -1,25 +1,4 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System.Collections.Generic; +using System.Collections.Generic; using Greenshot.Base.Core; using Greenshot.Base.Core.Enums; using Greenshot.Base.Effects; diff --git a/src/Greenshot.Base/Interfaces/SurfaceLineThicknessEventArgs.cs b/src/Greenshot.Base/Interfaces/SurfaceLineThicknessEventArgs.cs index 61ac1a848..5fd5694ba 100644 --- a/src/Greenshot.Base/Interfaces/SurfaceLineThicknessEventArgs.cs +++ b/src/Greenshot.Base/Interfaces/SurfaceLineThicknessEventArgs.cs @@ -20,6 +20,7 @@ */ using System; +using System.Drawing; namespace Greenshot.Base.Interfaces { diff --git a/src/Greenshot.Base/Interfaces/SurfaceShadowEventArgs.cs b/src/Greenshot.Base/Interfaces/SurfaceShadowEventArgs.cs index 5159d4d05..a7bd682b7 100644 --- a/src/Greenshot.Base/Interfaces/SurfaceShadowEventArgs.cs +++ b/src/Greenshot.Base/Interfaces/SurfaceShadowEventArgs.cs @@ -20,6 +20,7 @@ */ using System; +using System.Drawing; namespace Greenshot.Base.Interfaces { diff --git a/src/Greenshot.Base/UnmanagedHelpers/DWM.cs b/src/Greenshot.Base/UnmanagedHelpers/DWM.cs new file mode 100644 index 000000000..5a2b59d87 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/DWM.cs @@ -0,0 +1,123 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using Greenshot.Base.Core; +using Greenshot.Base.Core.Enums; +using Greenshot.Base.UnmanagedHelpers.Enums; +using Greenshot.Base.UnmanagedHelpers.Structs; +using Microsoft.Win32; + +namespace Greenshot.Base.UnmanagedHelpers +{ + /// + /// Desktop Window Manager helper code + /// + public static class DWM + { + // DWM + [DllImport("dwmapi", SetLastError = true)] + public static extern int DwmRegisterThumbnail(IntPtr dest, IntPtr src, out IntPtr thumb); + + [DllImport("dwmapi", SetLastError = true)] + public static extern int DwmUnregisterThumbnail(IntPtr thumb); + + [DllImport("dwmapi", SetLastError = true)] + public static extern HResult DwmQueryThumbnailSourceSize(IntPtr thumb, out SIZE size); + + [DllImport("dwmapi", SetLastError = true)] + public static extern HResult DwmUpdateThumbnailProperties(IntPtr hThumb, ref DWM_THUMBNAIL_PROPERTIES props); + + // Deprecated as of Windows 8 Release Preview + [DllImport("dwmapi", SetLastError = true)] + public static extern int DwmIsCompositionEnabled(out bool enabled); + + [DllImport("dwmapi", SetLastError = true)] + public static extern int DwmGetWindowAttribute(IntPtr hWnd, DWMWINDOWATTRIBUTE dwAttribute, out RECT lpRect, int size); + + [DllImport("dwmapi", SetLastError = true)] + public static extern int DwmGetWindowAttribute(IntPtr hWnd, DWMWINDOWATTRIBUTE dwAttribute, out bool pvAttribute, int cbAttribute); + + // Key to ColorizationColor for DWM + private const string COLORIZATION_COLOR_KEY = @"SOFTWARE\Microsoft\Windows\DWM"; + + /// + /// Checks if the window is cloaked, this should solve some issues with the window selection code + /// + /// IntPtr as hWmd + /// bool + public static bool IsWindowCloaked(IntPtr hWnd) + { + if (!WindowsVersion.IsWindows8OrLater) + { + return false; + } + + DwmGetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_CLOAKED, out bool isCloaked, Marshal.SizeOf(typeof(bool))); + return isCloaked; + } + + /// + /// Helper method for an easy DWM check + /// + /// bool true if DWM is available AND active + public static bool IsDwmEnabled + { + get + { + // According to: https://technet.microsoft.com/en-us/subscriptions/aa969538%28v=vs.85%29.aspx + // And: https://docs.microsoft.com/en-gb/windows/win32/api/dwmapi/nf-dwmapi-dwmenablecomposition + // DMW is always enabled on Windows 8! So return true and save a check! ;-) + if (WindowsVersion.IsWindows8OrLater) + { + return true; + } + + if (WindowsVersion.IsWindowsVistaOrLater) + { + DwmIsCompositionEnabled(out var dwmEnabled); + return dwmEnabled; + } + + return false; + } + } + + public static Color ColorizationColor + { + get + { + using (RegistryKey key = Registry.CurrentUser.OpenSubKey(COLORIZATION_COLOR_KEY, false)) + { + object dwordValue = key?.GetValue("ColorizationColor"); + if (dwordValue != null) + { + return Color.FromArgb((int) dwordValue); + } + } + + return Color.White; + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/Interfaces/IProvideDeviceDpi.cs b/src/Greenshot.Base/UnmanagedHelpers/EnumWindowsProc.cs similarity index 76% rename from src/Greenshot.Base/Interfaces/IProvideDeviceDpi.cs rename to src/Greenshot.Base/UnmanagedHelpers/EnumWindowsProc.cs index 73db77af6..9d5b10b92 100644 --- a/src/Greenshot.Base/Interfaces/IProvideDeviceDpi.cs +++ b/src/Greenshot.Base/UnmanagedHelpers/EnumWindowsProc.cs @@ -1,4 +1,4 @@ -/* +/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom * @@ -19,16 +19,15 @@ * along with this program. If not, see . */ -namespace Greenshot.Base.Interfaces +using System; + +namespace Greenshot.Base.UnmanagedHelpers { /// - /// IProvideDeviceDpi can provide the current DPI for a component + /// Used with EnumWindows or EnumChildWindows /// - public interface IProvideDeviceDpi - { - /// - /// A simple getter for the current DPI - /// - int DeviceDpi { get; } - } + /// IntPtr + /// int + /// int + public delegate int EnumWindowsProc(IntPtr hWnd, int lParam); } \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Enums/ClassLongIndex.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/ClassLongIndex.cs new file mode 100644 index 000000000..76676d157 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/ClassLongIndex.cs @@ -0,0 +1,32 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Diagnostics.CodeAnalysis; + +namespace Greenshot.Base.UnmanagedHelpers.Enums +{ + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum ClassLongIndex + { + GCL_HICON = -14, // a handle to the icon associated with the class. + GCL_HICONSM = -34, // a handle to the small icon associated with the class. + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Enums/DWMWINDOWATTRIBUTE.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/DWMWINDOWATTRIBUTE.cs new file mode 100644 index 000000000..5b6ee9900 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/DWMWINDOWATTRIBUTE.cs @@ -0,0 +1,46 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Diagnostics.CodeAnalysis; + +namespace Greenshot.Base.UnmanagedHelpers.Enums +{ + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum DWMWINDOWATTRIBUTE : uint + { + DWMWA_NCRENDERING_ENABLED = 1, + DWMWA_NCRENDERING_POLICY, + DWMWA_TRANSITIONS_FORCEDISABLED, + DWMWA_ALLOW_NCPAINT, + DWMWA_CAPTION_BUTTON_BOUNDS, + DWMWA_NONCLIENT_RTL_LAYOUT, + DWMWA_FORCE_ICONIC_REPRESENTATION, + DWMWA_FLIP3D_POLICY, + DWMWA_EXTENDED_FRAME_BOUNDS, // This is the one we need for retrieving the Window size since Windows Vista + DWMWA_HAS_ICONIC_BITMAP, // Since Windows 7 + DWMWA_DISALLOW_PEEK, // Since Windows 7 + DWMWA_EXCLUDED_FROM_PEEK, // Since Windows 7 + DWMWA_CLOAK, // Since Windows 8 + DWMWA_CLOAKED, // Since Windows 8 + DWMWA_FREEZE_REPRESENTATION, // Since Windows 8 + DWMWA_LAST + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Enums/DWM_THUMBNAIL_PROPERTIES.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/DWM_THUMBNAIL_PROPERTIES.cs new file mode 100644 index 000000000..615c2d7fa --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/DWM_THUMBNAIL_PROPERTIES.cs @@ -0,0 +1,111 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Runtime.InteropServices; +using Greenshot.Base.UnmanagedHelpers.Structs; + +namespace Greenshot.Base.UnmanagedHelpers.Enums +{ + /// + /// See DWM_THUMBNAIL_PROPERTIES + /// + [StructLayout(LayoutKind.Sequential)] + public struct DWM_THUMBNAIL_PROPERTIES + { + // A bitwise combination of DWM thumbnail constant values that indicates which members of this structure are set. + public int dwFlags; + + // The area in the destination window where the thumbnail will be rendered. + public RECT rcDestination; + + // The region of the source window to use as the thumbnail. By default, the entire window is used as the thumbnail. + public RECT rcSource; + + // The opacity with which to render the thumbnail. 0 is fully transparent while 255 is fully opaque. The default value is 255. + public byte opacity; + + // TRUE to make the thumbnail visible; otherwise, FALSE. The default is FALSE. + public bool fVisible; + + // TRUE to use only the thumbnail source's client area; otherwise, FALSE. The default is FALSE. + public bool fSourceClientAreaOnly; + + public RECT Destination + { + set + { + dwFlags |= DWM_TNP_RECTDESTINATION; + rcDestination = value; + } + } + + public RECT Source + { + set + { + dwFlags |= DWM_TNP_RECTSOURCE; + rcSource = value; + } + } + + public byte Opacity + { + set + { + dwFlags |= DWM_TNP_OPACITY; + opacity = value; + } + } + + public bool Visible + { + set + { + dwFlags |= DWM_TNP_VISIBLE; + fVisible = value; + } + } + + public bool SourceClientAreaOnly + { + set + { + dwFlags |= DWM_TNP_SOURCECLIENTAREAONLY; + fSourceClientAreaOnly = value; + } + } + + // A value for the rcDestination member has been specified. + public const int DWM_TNP_RECTDESTINATION = 0x00000001; + + // A value for the rcSource member has been specified. + public const int DWM_TNP_RECTSOURCE = 0x00000002; + + // A value for the opacity member has been specified. + public const int DWM_TNP_OPACITY = 0x00000004; + + // A value for the fVisible member has been specified. + public const int DWM_TNP_VISIBLE = 0x00000008; + + // A value for the fSourceClientAreaOnly member has been specified. + public const int DWM_TNP_SOURCECLIENTAREAONLY = 0x00000010; + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Enums/DesktopAccessRight.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/DesktopAccessRight.cs new file mode 100644 index 000000000..c99ddb149 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/DesktopAccessRight.cs @@ -0,0 +1,45 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Greenshot.Base.UnmanagedHelpers.Enums +{ + [Flags] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum DesktopAccessRight : uint + { + DESKTOP_READOBJECTS = 0x00000001, + DESKTOP_CREATEWINDOW = 0x00000002, + DESKTOP_CREATEMENU = 0x00000004, + DESKTOP_HOOKCONTROL = 0x00000008, + DESKTOP_JOURNALRECORD = 0x00000010, + DESKTOP_JOURNALPLAYBACK = 0x00000020, + DESKTOP_ENUMERATE = 0x00000040, + DESKTOP_WRITEOBJECTS = 0x00000080, + DESKTOP_SWITCHDESKTOP = 0x00000100, + + GENERIC_ALL = (DESKTOP_READOBJECTS | DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU | + DESKTOP_HOOKCONTROL | DESKTOP_JOURNALRECORD | DESKTOP_JOURNALPLAYBACK | + DESKTOP_ENUMERATE | DESKTOP_WRITEOBJECTS | DESKTOP_SWITCHDESKTOP) + }; +} \ No newline at end of file diff --git a/src/Greenshot.Editor/Helpers/ScaleOptions.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/DeviceCaps.cs similarity index 65% rename from src/Greenshot.Editor/Helpers/ScaleOptions.cs rename to src/Greenshot.Base/UnmanagedHelpers/Enums/DeviceCaps.cs index 1f47f5c02..2f2df7994 100644 --- a/src/Greenshot.Editor/Helpers/ScaleOptions.cs +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/DeviceCaps.cs @@ -1,4 +1,4 @@ -/* +/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom * @@ -19,26 +19,25 @@ * along with this program. If not, see . */ -using System; +using System.Diagnostics.CodeAnalysis; -namespace Greenshot.Editor.Helpers +namespace Greenshot.Base.UnmanagedHelpers.Enums { - [Flags] - public enum ScaleOptions + /// + /// Used by GDI32.GetDeviceCaps + /// See GetDeviceCaps + /// + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum DeviceCaps { /// - /// Default scale behavior. + /// Logical pixels inch in X /// - Default = 0x00, + LOGPIXELSX = 88, /// - /// Scale a rectangle in two our four directions, mirrored at it's center coordinates + /// Current vertical refresh rate of the display device (for displays only) in Hz /// - Centered = 0x01, - - /// - /// Scale a rectangle maintaining it's aspect ratio - /// - Rational = 0x02 + VREFRESH = 116 } } \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Enums/EventObjects.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/EventObjects.cs new file mode 100644 index 000000000..c9bf33cfc --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/EventObjects.cs @@ -0,0 +1,34 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Diagnostics.CodeAnalysis; + +namespace Greenshot.Base.UnmanagedHelpers.Enums +{ + /// + /// See Object Identifiers + /// + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum EventObjects + { + OBJID_WINDOW = 0 + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Enums/ExtendedWindowStyleFlags.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/ExtendedWindowStyleFlags.cs new file mode 100644 index 000000000..aaeb23fa9 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/ExtendedWindowStyleFlags.cs @@ -0,0 +1,71 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Greenshot.Base.UnmanagedHelpers.Enums +{ + [Flags] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum ExtendedWindowStyleFlags : uint + { + WS_EX_DLGMODALFRAME = 0x00000001, + WS_EX_NOPARENTNOTIFY = 0x00000004, + WS_EX_TOPMOST = 0x00000008, + WS_EX_ACCEPTFILES = 0x00000010, + WS_EX_TRANSPARENT = 0x00000020, + + //#if(WINVER >= 0x0400) + WS_EX_MDICHILD = 0x00000040, + WS_EX_TOOLWINDOW = 0x00000080, + WS_EX_WINDOWEDGE = 0x00000100, + WS_EX_CLIENTEDGE = 0x00000200, + WS_EX_CONTEXTHELP = 0x00000400, + + WS_EX_RIGHT = 0x00001000, + WS_EX_LEFT = 0x00000000, + WS_EX_RTLREADING = 0x00002000, + WS_EX_LTRREADING = 0x00000000, + WS_EX_LEFTSCROLLBAR = 0x00004000, + WS_EX_RIGHTSCROLLBAR = 0x00000000, + + WS_EX_CONTROLPARENT = 0x00010000, + WS_EX_STATICEDGE = 0x00020000, + WS_EX_APPWINDOW = 0x00040000, + + //WS_EX_OVERLAPPEDWINDOW = (WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE), + //WS_EX_PALETTEWINDOW = (WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST), + + WS_EX_LAYERED = 0x00080000, + WS_EX_NOINHERITLAYOUT = 0x00100000, // Disable inheritence of mirroring by children + WS_EX_NOREDIRECTIONBITMAP = 0x00200000, //The window does not render to a redirection surface. This is for windows that do not have visible content or that use mechanisms other than surfaces to provide their visual. + WS_EX_LAYOUTRTL = 0x00400000, // Right to left mirroring + /// + /// Paints all descendants of a window in bottom-to-top painting order using double-buffering. + /// Bottom-to-top painting order allows a descendent window to have translucency (alpha) and transparency (color-key) effects, but only if the descendent window also has the WS_EX_TRANSPARENT bit set. + /// Double-buffering allows the window and its descendents to be painted without flicker. + /// This cannot be used if the window has a class style of either CS_OWNDC or CS_CLASSDC. + /// + WS_EX_COMPOSITED = 0x02000000, + WS_EX_NOACTIVATE = 0x08000000 // A top-level window created with this style does not become the foreground window when the user clicks it. The system does not bring this window to the foreground when the user minimizes or closes the foreground window. + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Enums/ProcessAccessFlags.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/ProcessAccessFlags.cs new file mode 100644 index 000000000..c8795e33c --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/ProcessAccessFlags.cs @@ -0,0 +1,34 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Greenshot.Base.UnmanagedHelpers.Enums +{ + [Flags] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum ProcessAccessFlags : uint + { + VMRead = 0x00000010, + QueryInformation = 0x00000400, + } +} \ No newline at end of file diff --git a/src/Greenshot.Editor/Helpers/IDoubleProcessor.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/RegionResult.cs similarity index 79% rename from src/Greenshot.Editor/Helpers/IDoubleProcessor.cs rename to src/Greenshot.Base/UnmanagedHelpers/Enums/RegionResult.cs index beb426c68..013f70ef3 100644 --- a/src/Greenshot.Editor/Helpers/IDoubleProcessor.cs +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/RegionResult.cs @@ -1,4 +1,4 @@ -/* +/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom * @@ -19,10 +19,14 @@ * along with this program. If not, see . */ -namespace Greenshot.Editor.Helpers +using System.Diagnostics.CodeAnalysis; + +namespace Greenshot.Base.UnmanagedHelpers.Enums { - public interface IDoubleProcessor + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum RegionResult { - double Process(double d); + REGION_ERROR = 0, + REGION_NULLREGION = 1, } } \ No newline at end of file diff --git a/src/Greenshot.Editor/Helpers/LineAngleRoundBehavior.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/SendMessageTimeoutFlags.cs similarity index 72% rename from src/Greenshot.Editor/Helpers/LineAngleRoundBehavior.cs rename to src/Greenshot.Base/UnmanagedHelpers/Enums/SendMessageTimeoutFlags.cs index 6811f66cf..64e01979a 100644 --- a/src/Greenshot.Editor/Helpers/LineAngleRoundBehavior.cs +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/SendMessageTimeoutFlags.cs @@ -1,4 +1,4 @@ -/* +/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom * @@ -20,20 +20,17 @@ */ using System; +using System.Diagnostics.CodeAnalysis; -namespace Greenshot.Editor.Helpers +namespace Greenshot.Base.UnmanagedHelpers.Enums { - public class LineAngleRoundBehavior : IDoubleProcessor + /// + /// See: https://www.pinvoke.net/default.aspx/Enums/SendMessageTimeoutFlags.html + /// + [Flags] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum SendMessageTimeoutFlags : uint { - public static readonly LineAngleRoundBehavior INSTANCE = new(); - - private LineAngleRoundBehavior() - { - } - - public double Process(double angle) - { - return Math.Round(angle / 15) * 15; - } + SMTO_NORMAL = 0x0 } } \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Enums/ShowWindowCommand.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/ShowWindowCommand.cs new file mode 100644 index 000000000..5faf95e03 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/ShowWindowCommand.cs @@ -0,0 +1,110 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Diagnostics.CodeAnalysis; + +namespace Greenshot.Base.UnmanagedHelpers.Enums +{ + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum ShowWindowCommand : uint + { + /// + /// Hides the window and activates another window. + /// + Hide = 0, + + /// + /// Activates and displays a window. If the window is minimized or + /// maximized, the system restores it to its original size and position. + /// An application should specify this flag when displaying the window + /// for the first time. + /// + Normal = 1, + + /// + /// Activates the window and displays it as a minimized window. + /// + ShowMinimized = 2, + + /// + /// Maximizes the specified window. + /// + Maximize = 3, // is this the right value? + + /// + /// Activates the window and displays it as a maximized window. + /// + ShowMaximized = 3, + + /// + /// Displays a window in its most recent size and position. This value + /// is similar to , except + /// the window is not actived. + /// + ShowNoActivate = 4, + + /// + /// Activates the window and displays it in its current size and position. + /// + Show = 5, + + /// + /// Minimizes the specified window and activates the next top-level + /// window in the Z order. + /// + Minimize = 6, + + /// + /// Displays the window as a minimized window. This value is similar to + /// , except the + /// window is not activated. + /// + ShowMinNoActive = 7, + + /// + /// Displays the window in its current size and position. This value is + /// similar to , except the + /// window is not activated. + /// + ShowNA = 8, + + /// + /// Activates and displays the window. If the window is minimized or + /// maximized, the system restores it to its original size and position. + /// An application should specify this flag when restoring a minimized window. + /// + Restore = 9, + + /// + /// Sets the show state based on the SW_* value specified in the + /// STARTUPINFO structure passed to the CreateProcess function by the + /// program that started the application. + /// + ShowDefault = 10, + + /// + /// Windows 2000/XP: Minimizes a window, even if the thread + /// that owns the window is not responding. This flag should only be + /// used when minimizing windows from a different thread. + /// + ForceMinimize = 11 + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Enums/SoundFlags.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/SoundFlags.cs new file mode 100644 index 000000000..0ea597d08 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/SoundFlags.cs @@ -0,0 +1,39 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Greenshot.Base.UnmanagedHelpers.Enums +{ + /// + /// See: https://msdn.microsoft.com/en-us/library/aa909766.aspx + /// + [Flags] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum SoundFlags + { + SND_ASYNC = 0x0001, // play asynchronously + SND_MEMORY = 0x0004, // pszSound points to a memory file + SND_NOSTOP = 0x0010, // don't stop any currently playing sound + SND_NOWAIT = 0x00002000, // don't wait if the driver is busy + } +} \ No newline at end of file diff --git a/src/Greenshot.Editor/Helpers/IHaveScaleOptions.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/ThreadAccess.cs similarity index 86% rename from src/Greenshot.Editor/Helpers/IHaveScaleOptions.cs rename to src/Greenshot.Base/UnmanagedHelpers/Enums/ThreadAccess.cs index 3f1ed03ff..415e1f494 100644 --- a/src/Greenshot.Editor/Helpers/IHaveScaleOptions.cs +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/ThreadAccess.cs @@ -1,6 +1,6 @@ /* * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom * * For more information see: https://getgreenshot.org/ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot @@ -19,10 +19,13 @@ * along with this program. If not, see . */ -namespace Greenshot.Editor.Helpers +using System; + +namespace Greenshot.Base.UnmanagedHelpers.Enums { - public interface IHaveScaleOptions + [Flags] + public enum ThreadAccess : int { - ScaleOptions GetScaleOptions(); + SUSPEND_RESUME = (0x0002), } -} +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Enums/Win32Error.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/Win32Error.cs new file mode 100644 index 000000000..87259b3a9 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/Win32Error.cs @@ -0,0 +1,89 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace Greenshot.Base.UnmanagedHelpers.Enums +{ + /// + /// A Win32 error code. + /// + public enum Win32Error : uint + { + Success = 0x0, + InvalidFunction = 0x1, + FileNotFound = 0x2, + PathNotFound = 0x3, + TooManyOpenFiles = 0x4, + AccessDenied = 0x5, + InvalidHandle = 0x6, + ArenaTrashed = 0x7, + NotEnoughMemory = 0x8, + InvalidBlock = 0x9, + BadEnvironment = 0xa, + BadFormat = 0xb, + InvalidAccess = 0xc, + InvalidData = 0xd, + OutOfMemory = 0xe, + InvalidDrive = 0xf, + CurrentDirectory = 0x10, + NotSameDevice = 0x11, + NoMoreFiles = 0x12, + WriteProtect = 0x13, + BadUnit = 0x14, + NotReady = 0x15, + BadCommand = 0x16, + Crc = 0x17, + BadLength = 0x18, + Seek = 0x19, + NotDosDisk = 0x1a, + SectorNotFound = 0x1b, + OutOfPaper = 0x1c, + WriteFault = 0x1d, + ReadFault = 0x1e, + GenFailure = 0x1f, + SharingViolation = 0x20, + LockViolation = 0x21, + WrongDisk = 0x22, + SharingBufferExceeded = 0x24, + HandleEof = 0x26, + HandleDiskFull = 0x27, + NotSupported = 0x32, + RemNotList = 0x33, + DupName = 0x34, + BadNetPath = 0x35, + NetworkBusy = 0x36, + DevNotExist = 0x37, + TooManyCmds = 0x38, + FileExists = 0x50, + CannotMake = 0x52, + AlreadyAssigned = 0x55, + InvalidPassword = 0x56, + InvalidParameter = 0x57, + NetWriteFault = 0x58, + NoProcSlots = 0x59, + TooManySemaphores = 0x64, + ExclSemAlreadyOwned = 0x65, + SemIsSet = 0x66, + TooManySemRequests = 0x67, + InvalidAtInterruptTime = 0x68, + SemOwnerDied = 0x69, + SemUserLimit = 0x6a + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Enums/WinEvent.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/WinEvent.cs new file mode 100644 index 000000000..73b47c2aa --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/WinEvent.cs @@ -0,0 +1,37 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Diagnostics.CodeAnalysis; + +namespace Greenshot.Base.UnmanagedHelpers.Enums +{ + /// + /// Used for User32.SetWinEventHook + /// See MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/dd318066%28v=vs.85%29.aspx + /// + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum WinEvent : uint + { + EVENT_OBJECT_CREATE = 32768, + EVENT_OBJECT_DESTROY = 32769, + EVENT_OBJECT_NAMECHANGE = 32780, + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Enums/WinEventHookFlags.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/WinEventHookFlags.cs new file mode 100644 index 000000000..809cb5248 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/WinEventHookFlags.cs @@ -0,0 +1,18 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Greenshot.Base.UnmanagedHelpers.Enums +{ + /// + /// Used for User32.SetWinEventHook + /// See: https://msdn.microsoft.com/en-us/library/windows/desktop/dd373640%28v=vs.85%29.aspx + /// + [SuppressMessage("ReSharper", "InconsistentNaming"), Flags] + public enum WinEventHookFlags + { + WINEVENT_SKIPOWNTHREAD = 1, + WINEVENT_SKIPOWNPROCESS = 2, + WINEVENT_OUTOFCONTEXT = 0, + WINEVENT_INCONTEXT = 4 + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Enums/WindowLongIndex.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/WindowLongIndex.cs new file mode 100644 index 000000000..6af978b3e --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/WindowLongIndex.cs @@ -0,0 +1,32 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Diagnostics.CodeAnalysis; + +namespace Greenshot.Base.UnmanagedHelpers.Enums +{ + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum WindowLongIndex + { + GWL_EXSTYLE = -20, // Sets a new extended window style. + GWL_STYLE = -16, // Sets a new window style. + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Enums/WindowPlacementFlags.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/WindowPlacementFlags.cs new file mode 100644 index 000000000..e59c9c6c1 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/WindowPlacementFlags.cs @@ -0,0 +1,42 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Greenshot.Base.UnmanagedHelpers.Enums +{ + [Flags] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum WindowPlacementFlags : uint + { + // The coordinates of the minimized window may be specified. + // This flag must be specified if the coordinates are set in the ptMinPosition member. + WPF_SETMINPOSITION = 0x0001, + + // If the calling thread and the thread that owns the window are attached to different input queues, the system posts the request to the thread that owns the window. This prevents the calling thread from blocking its execution while other threads process the request. + WPF_ASYNCWINDOWPLACEMENT = 0x0004, + + // The restored window will be maximized, regardless of whether it was maximized before it was minimized. This setting is only valid the next time the window is restored. It does not change the default restoration behavior. + // This flag is only valid when the SW_SHOWMINIMIZED value is specified for the showCmd member. + WPF_RESTORETOMAXIMIZED = 0x0002 + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Enums/WindowPos.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/WindowPos.cs new file mode 100644 index 000000000..5d7a3caa2 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/WindowPos.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Greenshot.Base.UnmanagedHelpers.Enums +{ + [Flags] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum WindowPos + { + SWP_NOACTIVATE = + 0x0010, // Does not activate the window. If this flag is not set, the window is activated and moved to the top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter parameter). + SWP_NOMOVE = 0x0002, //Retains the current position (ignores X and Y parameters). + SWP_NOSIZE = 0x0001, // Retains the current size (ignores the cx and cy parameters). + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Enums/WindowStyleFlags.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/WindowStyleFlags.cs new file mode 100644 index 000000000..603e58196 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/WindowStyleFlags.cs @@ -0,0 +1,52 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Greenshot.Base.UnmanagedHelpers.Enums +{ + /// + /// Window Style Flags + /// + [Flags] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum WindowStyleFlags : int + { + //WS_OVERLAPPED = 0x00000000, + WS_POPUP = -2147483648, + WS_CHILD = 0x40000000, + WS_MINIMIZE = 0x20000000, + WS_VISIBLE = 0x10000000, + WS_DISABLED = 0x08000000, + WS_CLIPSIBLINGS = 0x04000000, + WS_CLIPCHILDREN = 0x02000000, + WS_MAXIMIZE = 0x01000000, + WS_BORDER = 0x00800000, + WS_DLGFRAME = 0x00400000, + WS_VSCROLL = 0x00200000, + WS_HSCROLL = 0x00100000, + WS_SYSMENU = 0x00080000, + WS_THICKFRAME = 0x00040000, + WS_GROUP = 0x00020000, + WS_TABSTOP = 0x00010000 + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Enums/WindowsMessages.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/WindowsMessages.cs new file mode 100644 index 000000000..2aff5fe19 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/WindowsMessages.cs @@ -0,0 +1,26 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Greenshot.Base.UnmanagedHelpers.Enums +{ + /// + /// All possible windows messages + /// See also here + /// + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum WindowsMessages : uint + { + WM_MOUSEACTIVATE = 0x0021, + WM_INPUTLANGCHANGEREQUEST = 0x0050, + WM_INPUTLANGCHANGE = 0x0051, + + + /// + /// Sent to a window to retrieve a handle to the large or small icon associated with a window. The system displays the large icon in the ALT+TAB dialog, and the small icon in the window caption. + /// A window receives this message through its WindowProc function. + /// WM_GETICON message + /// + WM_GETICON = 0x007F, + WM_CHAR = 0x0102, + WM_SYSCOMMAND = 0x0112 + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/GDI32.cs b/src/Greenshot.Base/UnmanagedHelpers/GDI32.cs new file mode 100644 index 000000000..3f483ff20 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/GDI32.cs @@ -0,0 +1,596 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Security; +using Greenshot.Base.UnmanagedHelpers.Enums; +using Microsoft.Win32.SafeHandles; + +namespace Greenshot.Base.UnmanagedHelpers +{ + public static class GDIExtensions + { + /// + /// Check if all the corners of the rectangle are visible in the specified region. + /// Not a perfect check, but this currently a workaround for checking if a window is completely visible + /// + /// + /// + /// + public static bool AreRectangleCornersVisisble(this Region region, Rectangle rectangle) + { + Point topLeft = new Point(rectangle.X, rectangle.Y); + Point topRight = new Point(rectangle.X + rectangle.Width, rectangle.Y); + Point bottomLeft = new Point(rectangle.X, rectangle.Y + rectangle.Height); + Point bottomRight = new Point(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height); + bool topLeftVisible = region.IsVisible(topLeft); + bool topRightVisible = region.IsVisible(topRight); + bool bottomLeftVisible = region.IsVisible(bottomLeft); + bool bottomRightVisible = region.IsVisible(bottomRight); + + return topLeftVisible && topRightVisible && bottomLeftVisible && bottomRightVisible; + } + + /// + /// Get a SafeHandle for the GetHdc, so one can use using to automatically cleanup the devicecontext + /// + /// + /// SafeDeviceContextHandle + public static SafeDeviceContextHandle GetSafeDeviceContext(this Graphics graphics) + { + return SafeDeviceContextHandle.FromGraphics(graphics); + } + } + + /// + /// Abstract class SafeObjectHandle which contains all handles that are cleaned with DeleteObject + /// + public abstract class SafeObjectHandle : SafeHandleZeroOrMinusOneIsInvalid + { + [DllImport("gdi32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DeleteObject(IntPtr hObject); + + protected SafeObjectHandle(bool ownsHandle) : base(ownsHandle) + { + } + + protected override bool ReleaseHandle() + { + return DeleteObject(handle); + } + } + + /// + /// A hbitmap SafeHandle implementation + /// + public class SafeHBitmapHandle : SafeObjectHandle + { + /// + /// Needed for marshalling return values + /// + [SecurityCritical] + public SafeHBitmapHandle() : base(true) + { + } + + [SecurityCritical] + public SafeHBitmapHandle(IntPtr preexistingHandle) : base(true) + { + SetHandle(preexistingHandle); + } + } + + /// + /// A hRegion SafeHandle implementation + /// + public class SafeRegionHandle : SafeObjectHandle + { + /// + /// Needed for marshalling return values + /// + [SecurityCritical] + public SafeRegionHandle() : base(true) + { + } + + [SecurityCritical] + public SafeRegionHandle(IntPtr preexistingHandle) : base(true) + { + SetHandle(preexistingHandle); + } + } + + /// + /// A dibsection SafeHandle implementation + /// + public class SafeDibSectionHandle : SafeObjectHandle + { + /// + /// Needed for marshalling return values + /// + [SecurityCritical] + public SafeDibSectionHandle() : base(true) + { + } + + [SecurityCritical] + public SafeDibSectionHandle(IntPtr preexistingHandle) : base(true) + { + SetHandle(preexistingHandle); + } + } + + /// + /// A select object safehandle implementation + /// This impl will select the passed SafeHandle to the HDC and replace the returned value when disposing + /// + public class SafeSelectObjectHandle : SafeHandleZeroOrMinusOneIsInvalid + { + [DllImport("gdi32", SetLastError = true)] + private static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject); + + private readonly SafeHandle _hdc; + + /// + /// Needed for marshalling return values + /// + [SecurityCritical] + public SafeSelectObjectHandle() : base(true) + { + } + + [SecurityCritical] + public SafeSelectObjectHandle(SafeDCHandle hdc, SafeHandle newHandle) : base(true) + { + _hdc = hdc; + SetHandle(SelectObject(hdc.DangerousGetHandle(), newHandle.DangerousGetHandle())); + } + + protected override bool ReleaseHandle() + { + SelectObject(_hdc.DangerousGetHandle(), handle); + return true; + } + } + + public abstract class SafeDCHandle : SafeHandleZeroOrMinusOneIsInvalid + { + protected SafeDCHandle(bool ownsHandle) : base(ownsHandle) + { + } + } + + /// + /// A CompatibleDC SafeHandle implementation + /// + public class SafeCompatibleDCHandle : SafeDCHandle + { + [DllImport("gdi32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DeleteDC(IntPtr hDC); + + /// + /// Needed for marshalling return values + /// + [SecurityCritical] + public SafeCompatibleDCHandle() : base(true) + { + } + + [SecurityCritical] + public SafeCompatibleDCHandle(IntPtr preexistingHandle) : base(true) + { + SetHandle(preexistingHandle); + } + + public SafeSelectObjectHandle SelectObject(SafeHandle newHandle) + { + return new SafeSelectObjectHandle(this, newHandle); + } + + protected override bool ReleaseHandle() + { + return DeleteDC(handle); + } + } + + /// + /// A DeviceContext SafeHandle implementation + /// + public class SafeDeviceContextHandle : SafeDCHandle + { + private readonly Graphics _graphics; + + /// + /// Needed for marshalling return values + /// + [SecurityCritical] + public SafeDeviceContextHandle() : base(true) + { + } + + [SecurityCritical] + public SafeDeviceContextHandle(Graphics graphics, IntPtr preexistingHandle) : base(true) + { + _graphics = graphics; + SetHandle(preexistingHandle); + } + + protected override bool ReleaseHandle() + { + _graphics.ReleaseHdc(handle); + return true; + } + + public static SafeDeviceContextHandle FromGraphics(Graphics graphics) + { + return new SafeDeviceContextHandle(graphics, graphics.GetHdc()); + } + } + + /// + /// GDI32 Helpers + /// + public static class GDI32 + { + [DllImport("gdi32", SetLastError = true)] + public static extern bool BitBlt(SafeHandle hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, SafeHandle hdcSrc, int nXSrc, int nYSrc, CopyPixelOperation dwRop); + + [DllImport("gdi32", SetLastError = true)] + public static extern SafeCompatibleDCHandle CreateCompatibleDC(SafeHandle hDC); + + [DllImport("gdi32", SetLastError = true)] + public static extern SafeDibSectionHandle CreateDIBSection(SafeHandle hdc, ref BITMAPINFOHEADERV5 bmi, uint usage, out IntPtr bits, IntPtr hSection, uint dwOffset); + + [DllImport("gdi32", SetLastError = true)] + public static extern SafeRegionHandle CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect); + + [DllImport("gdi32", SetLastError = true)] + public static extern uint GetPixel(SafeHandle hdc, int nXPos, int nYPos); + + [DllImport("gdi32", SetLastError = true)] + public static extern int GetDeviceCaps(SafeHandle hdc, DeviceCaps nIndex); + } + + [StructLayout(LayoutKind.Sequential, Pack = 2)] + public struct BITMAPFILEHEADER + { + public static readonly short BM = 0x4d42; // BM + public short bfType; + public int bfSize; + public short bfReserved1; + public short bfReserved2; + public int bfOffBits; + } + + [StructLayout(LayoutKind.Sequential)] + public struct BitfieldColorMask + { + public uint blue; + public uint green; + public uint red; + + public void InitValues() + { + red = (uint) 255 << 8; + green = (uint) 255 << 16; + blue = (uint) 255 << 24; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct CIEXYZ + { + public uint ciexyzX; //FXPT2DOT30 + public uint ciexyzY; //FXPT2DOT30 + public uint ciexyzZ; //FXPT2DOT30 + + public CIEXYZ(uint FXPT2DOT30) + { + ciexyzX = FXPT2DOT30; + ciexyzY = FXPT2DOT30; + ciexyzZ = FXPT2DOT30; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct RGBQUAD + { + public byte rgbBlue; + public byte rgbGreen; + public byte rgbRed; + public byte rgbReserved; + } + + [StructLayout(LayoutKind.Sequential)] + public struct CIEXYZTRIPLE + { + public CIEXYZ ciexyzRed; + public CIEXYZ ciexyzGreen; + public CIEXYZ ciexyzBlue; + } + + public enum BI_COMPRESSION : uint + { + BI_RGB = 0, // Uncompressed + BI_RLE8 = 1, // RLE 8BPP + BI_RLE4 = 2, // RLE 4BPP + + BI_BITFIELDS = + 3, // Specifies that the bitmap is not compressed and that the color table consists of three DWORD color masks that specify the red, green, and blue components, respectively, of each pixel. This is valid when used with 16- and 32-bpp bitmaps. + BI_JPEG = 4, // Indicates that the image is a JPEG image. + BI_PNG = 5 // Indicates that the image is a PNG image. + } + + [StructLayout(LayoutKind.Explicit)] + public struct BITMAPINFOHEADER + { + [FieldOffset(0)] public uint biSize; + [FieldOffset(4)] public int biWidth; + [FieldOffset(8)] public int biHeight; + [FieldOffset(12)] public ushort biPlanes; + [FieldOffset(14)] public ushort biBitCount; + [FieldOffset(16)] public BI_COMPRESSION biCompression; + [FieldOffset(20)] public uint biSizeImage; + [FieldOffset(24)] public int biXPelsPerMeter; + [FieldOffset(28)] public int biYPelsPerMeter; + [FieldOffset(32)] public uint biClrUsed; + [FieldOffset(36)] public uint biClrImportant; + + public const int DIB_RGB_COLORS = 0; + + public BITMAPINFOHEADER(int width, int height, ushort bpp) + { + biSize = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADER)); // BITMAPINFOHEADER < DIBV4 is 40 bytes + biPlanes = 1; // Should allways be 1 + biCompression = BI_COMPRESSION.BI_RGB; + biWidth = width; + biHeight = height; + biBitCount = bpp; + biSizeImage = (uint)(width * height * (bpp >> 3)); + biXPelsPerMeter = 0; + biYPelsPerMeter = 0; + biClrUsed = 0; + biClrImportant = 0; + } + + public bool IsDibV4 + { + get + { + uint sizeOfBMI = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADERV4)); + return biSize >= sizeOfBMI; + } + } + public bool IsDibV5 + { + get + { + uint sizeOfBMI = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADERV5)); + return biSize >= sizeOfBMI; + } + } + + public uint OffsetToPixels + { + get + { + if (biCompression == BI_COMPRESSION.BI_BITFIELDS) + { + // Add 3x4 bytes for the bitfield color mask + return biSize + 3 * 4; + } + + return biSize; + } + } + } + + [StructLayout(LayoutKind.Explicit)] + public struct BITMAPINFOHEADERV4 + { + [FieldOffset(0)] public uint biSize; + [FieldOffset(4)] public int biWidth; + [FieldOffset(8)] public int biHeight; + [FieldOffset(12)] public ushort biPlanes; + [FieldOffset(14)] public ushort biBitCount; + [FieldOffset(16)] public BI_COMPRESSION biCompression; + [FieldOffset(20)] public uint biSizeImage; + [FieldOffset(24)] public int biXPelsPerMeter; + [FieldOffset(28)] public int biYPelsPerMeter; + [FieldOffset(32)] public uint biClrUsed; + [FieldOffset(36)] public uint biClrImportant; + [FieldOffset(40)] public uint bV4RedMask; + [FieldOffset(44)] public uint bV4GreenMask; + [FieldOffset(48)] public uint bV4BlueMask; + [FieldOffset(52)] public uint bV4AlphaMask; + [FieldOffset(56)] public uint bV4CSType; + [FieldOffset(60)] public CIEXYZTRIPLE bV4Endpoints; + [FieldOffset(96)] public uint bV4GammaRed; + [FieldOffset(100)] public uint bV4GammaGreen; + [FieldOffset(104)] public uint bV4GammaBlue; + + public const int DIB_RGB_COLORS = 0; + + public BITMAPINFOHEADERV4(int width, int height, ushort bpp) + { + biSize = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADERV4)); // BITMAPINFOHEADER < DIBV5 is 40 bytes + biPlanes = 1; // Should allways be 1 + biCompression = BI_COMPRESSION.BI_RGB; + biWidth = width; + biHeight = height; + biBitCount = bpp; + biSizeImage = (uint)(width * height * (bpp >> 3)); + biXPelsPerMeter = 0; + biYPelsPerMeter = 0; + biClrUsed = 0; + biClrImportant = 0; + + // V4 + bV4RedMask = (uint)255 << 16; + bV4GreenMask = (uint)255 << 8; + bV4BlueMask = 255; + bV4AlphaMask = (uint)255 << 24; + bV4CSType = 0x73524742; // LCS_sRGB + bV4Endpoints = new CIEXYZTRIPLE + { + ciexyzBlue = new CIEXYZ(0), + ciexyzGreen = new CIEXYZ(0), + ciexyzRed = new CIEXYZ(0) + }; + bV4GammaRed = 0; + bV4GammaGreen = 0; + bV4GammaBlue = 0; + } + + public bool IsDibV4 + { + get + { + uint sizeOfBMI = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADERV4)); + return biSize >= sizeOfBMI; + } + } + public bool IsDibV5 + { + get + { + uint sizeOfBMI = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADERV5)); + return biSize >= sizeOfBMI; + } + } + + public uint OffsetToPixels + { + get + { + if (biCompression == BI_COMPRESSION.BI_BITFIELDS) + { + // Add 3x4 bytes for the bitfield color mask + return biSize + 3 * 4; + } + + return biSize; + } + } + } + + [StructLayout(LayoutKind.Explicit)] + public struct BITMAPINFOHEADERV5 + { + [FieldOffset(0)] public uint biSize; + [FieldOffset(4)] public int biWidth; + [FieldOffset(8)] public int biHeight; + [FieldOffset(12)] public ushort biPlanes; + [FieldOffset(14)] public ushort biBitCount; + [FieldOffset(16)] public BI_COMPRESSION biCompression; + [FieldOffset(20)] public uint biSizeImage; + [FieldOffset(24)] public int biXPelsPerMeter; + [FieldOffset(28)] public int biYPelsPerMeter; + [FieldOffset(32)] public uint biClrUsed; + [FieldOffset(36)] public uint biClrImportant; + [FieldOffset(40)] public uint bV4RedMask; + [FieldOffset(44)] public uint bV4GreenMask; + [FieldOffset(48)] public uint bV4BlueMask; + [FieldOffset(52)] public uint bV4AlphaMask; + [FieldOffset(56)] public uint bV4CSType; + [FieldOffset(60)] public CIEXYZTRIPLE bV4Endpoints; + [FieldOffset(96)] public uint bV4GammaRed; + [FieldOffset(100)] public uint bV4GammaGreen; + [FieldOffset(104)] public uint bV4GammaBlue; + [FieldOffset(108)] public uint bV5Intent; // Rendering intent for bitmap + [FieldOffset(112)] public uint bV5ProfileData; + [FieldOffset(116)] public uint bV5ProfileSize; + [FieldOffset(120)] public uint bV5Reserved; + + public const int DIB_RGB_COLORS = 0; + + public BITMAPINFOHEADERV5(int width, int height, ushort bpp) + { + biSize = (uint) Marshal.SizeOf(typeof(BITMAPINFOHEADERV5)); // BITMAPINFOHEADER < DIBV5 is 40 bytes + biPlanes = 1; // Should allways be 1 + biCompression = BI_COMPRESSION.BI_RGB; + biWidth = width; + biHeight = height; + biBitCount = bpp; + biSizeImage = (uint) (width * height * (bpp >> 3)); + biXPelsPerMeter = 0; + biYPelsPerMeter = 0; + biClrUsed = 0; + biClrImportant = 0; + + // V4 + bV4RedMask = (uint) 255 << 16; + bV4GreenMask = (uint) 255 << 8; + bV4BlueMask = 255; + bV4AlphaMask = (uint) 255 << 24; + bV4CSType = 0x73524742; // LCS_sRGB + bV4Endpoints = new CIEXYZTRIPLE + { + ciexyzBlue = new CIEXYZ(0), + ciexyzGreen = new CIEXYZ(0), + ciexyzRed = new CIEXYZ(0) + }; + bV4GammaRed = 0; + bV4GammaGreen = 0; + bV4GammaBlue = 0; + // V5 + bV5Intent = 4; + bV5ProfileData = 0; + bV5ProfileSize = 0; + bV5Reserved = 0; + } + + public bool IsDibV4 + { + get + { + uint sizeOfBMI = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADERV4)); + return biSize >= sizeOfBMI; + } + } + public bool IsDibV5 + { + get + { + uint sizeOfBMI = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADERV5)); + return biSize >= sizeOfBMI; + } + } + + public uint OffsetToPixels + { + get + { + if (biCompression == BI_COMPRESSION.BI_BITFIELDS) + { + // Add 3x4 bytes for the bitfield color mask + return biSize + 3 * 4; + } + + return biSize; + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/GDIplus.cs b/src/Greenshot.Base/UnmanagedHelpers/GDIplus.cs new file mode 100644 index 000000000..6bf30fdcc --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/GDIplus.cs @@ -0,0 +1,387 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Reflection; +using System.Runtime.InteropServices; +using Greenshot.Base.UnmanagedHelpers.Structs; +using log4net; + +namespace Greenshot.Base.UnmanagedHelpers +{ + /// + /// Contains members that specify the nature of a Gaussian blur. + /// + /// Cannot be pinned with GCHandle due to bool value. + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal struct BlurParams + { + /// + /// Real number that specifies the blur radius (the radius of the Gaussian convolution kernel) in + /// pixels. The radius must be in the range 0 through 255. As the radius increases, the resulting + /// bitmap becomes more blurry. + /// + public float Radius; + + /// + /// Boolean value that specifies whether the bitmap expands by an amount equal to the blur radius. + /// If TRUE, the bitmap expands by an amount equal to the radius so that it can have soft edges. + /// If FALSE, the bitmap remains the same size and the soft edges are clipped. + /// + public bool ExpandEdges; + } + + /// + /// GDI Plus unit description. + /// + public enum GpUnit + { + /// + /// World coordinate (non-physical unit). + /// + UnitWorld, + + /// + /// Variable - for PageTransform only. + /// + UnitDisplay, + + /// + /// Each unit is one device pixel. + /// + UnitPixel, + + /// + /// Each unit is a printer's point, or 1/72 inch. + /// + UnitPoint, + + /// + /// Each unit is 1 inch. + /// + UnitInch, + + /// + /// Each unit is 1/300 inch. + /// + UnitDocument, + + /// + /// Each unit is 1 millimeter. + /// + UnitMillimeter + } + + /// + /// GDIplus Helpers + /// + public static class GDIplus + { + private static readonly ILog Log = LogManager.GetLogger(typeof(GDIplus)); + + [DllImport("gdiplus.dll", SetLastError = true, ExactSpelling = true)] + private static extern int GdipBitmapApplyEffect(IntPtr bitmap, IntPtr effect, ref RECT rectOfInterest, bool useAuxData, IntPtr auxData, int auxDataSize); + + [DllImport("gdiplus.dll", SetLastError = true, ExactSpelling = true)] + private static extern int GdipDrawImageFX(IntPtr graphics, IntPtr bitmap, ref RECTF source, IntPtr matrix, IntPtr effect, IntPtr imageAttributes, GpUnit srcUnit); + + [DllImport("gdiplus.dll", SetLastError = true, ExactSpelling = true)] + private static extern int GdipSetEffectParameters(IntPtr effect, IntPtr parameters, uint size); + + [DllImport("gdiplus.dll", SetLastError = true, ExactSpelling = true)] + private static extern int GdipCreateEffect(Guid guid, out IntPtr effect); + + [DllImport("gdiplus.dll", SetLastError = true, ExactSpelling = true)] + private static extern int GdipDeleteEffect(IntPtr effect); + + private static readonly Guid BlurEffectGuid = new Guid("{633C80A4-1843-482B-9EF2-BE2834C5FDD4}"); + + // Constant "FieldInfo" for getting the nativeImage from the Bitmap + private static readonly FieldInfo FIELD_INFO_NATIVE_IMAGE = typeof(Bitmap).GetField("nativeImage", BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic); + + // Constant "FieldInfo" for getting the NativeGraphics from the Graphics + private static readonly FieldInfo FIELD_INFO_NATIVE_GRAPHICS = + typeof(Graphics).GetField("nativeGraphics", BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic); + + // Constant "FieldInfo" for getting the nativeMatrix from the Matrix + private static readonly FieldInfo FIELD_INFO_NATIVE_MATRIX = + typeof(Matrix).GetField("nativeMatrix", BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic); + + // Constant "FieldInfo" for getting the nativeImageAttributes from the ImageAttributes + private static readonly FieldInfo FIELD_INFO_NATIVE_IMAGEATTRIBUTES = + typeof(ImageAttributes).GetField("nativeImageAttributes", BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic); + + private static bool _isBlurEnabled = Environment.OSVersion.Version.Major >= 6; + + /// + /// Get the nativeImage field from the bitmap + /// + /// + /// IntPtr + private static IntPtr GetNativeImage(Bitmap bitmap) + { + if (bitmap == null) + { + return IntPtr.Zero; + } + + return (IntPtr) FIELD_INFO_NATIVE_IMAGE.GetValue(bitmap); + } + + /// + /// Get the NativeGraphics field from the graphics + /// + /// + /// IntPtr + private static IntPtr GetNativeGraphics(Graphics graphics) + { + if (graphics == null) + { + return IntPtr.Zero; + } + + return (IntPtr) FIELD_INFO_NATIVE_GRAPHICS.GetValue(graphics); + } + + /// + /// Get the nativeMatrix field from the matrix + /// + /// + /// IntPtr + private static IntPtr GetNativeMatrix(Matrix matrix) + { + if (matrix == null) + { + return IntPtr.Zero; + } + + return (IntPtr) FIELD_INFO_NATIVE_MATRIX.GetValue(matrix); + } + + /// + /// Get the nativeImageAttributes field from the ImageAttributes + /// + /// + /// IntPtr + private static IntPtr GetNativeImageAttributes(ImageAttributes imageAttributes) + { + if (imageAttributes == null) + { + return IntPtr.Zero; + } + + return (IntPtr) FIELD_INFO_NATIVE_IMAGEATTRIBUTES.GetValue(imageAttributes); + } + + /// + /// Returns if a GDIPlus blur can be made for the supplied radius. + /// This accounts for the "bug" I reported here: https://social.technet.microsoft.com/Forums/en/w8itprogeneral/thread/99ddbe9d-556d-475a-8bab-84e25aa13a2c + /// + /// + /// false if blur is not possible + public static bool IsBlurPossible(int radius) + { + if (!_isBlurEnabled) + { + return false; + } + + if (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor < 2) + { + return true; + } + + return Environment.OSVersion.Version.Major > 6 && radius >= 20; + } + + /// + /// Use the GDI+ blur effect on the bitmap + /// + /// Bitmap to apply the effect to + /// Rectangle to apply the blur effect to + /// 0-255 + /// bool true if the edges are expanded with the radius + /// false if there is no GDI+ available or an exception occurred + public static bool ApplyBlur(Bitmap destinationBitmap, Rectangle area, int radius, bool expandEdges) + { + if (!IsBlurPossible(radius)) + { + return false; + } + + IntPtr hBlurParams = IntPtr.Zero; + IntPtr hEffect = IntPtr.Zero; + + try + { + // Create the GDI+ BlurEffect, using the Guid + int status = GdipCreateEffect(BlurEffectGuid, out hEffect); + if (status != 0) + { + return false; + } + + // Create a BlurParams struct and set the values + var blurParams = new BlurParams + { + Radius = radius, + ExpandEdges = expandEdges + }; + + // Allocate space in unmanaged memory + hBlurParams = Marshal.AllocHGlobal(Marshal.SizeOf(blurParams)); + // Copy the structure to the unmanaged memory + Marshal.StructureToPtr(blurParams, hBlurParams, false); + + + // Set the blurParams to the effect + GdipSetEffectParameters(hEffect, hBlurParams, (uint) Marshal.SizeOf(blurParams)); + + // Somewhere it said we can use destinationBitmap.GetHbitmap(), this doesn't work!! + // Get the private nativeImage property from the Bitmap + IntPtr hBitmap = GetNativeImage(destinationBitmap); + + // Create a RECT from the Rectangle + RECT rec = new RECT(area); + // Apply the effect to the bitmap in the specified area + GdipBitmapApplyEffect(hBitmap, hEffect, ref rec, false, IntPtr.Zero, 0); + + // Everything worked, return true + return true; + } + catch (Exception ex) + { + _isBlurEnabled = false; + Log.Error("Problem using GdipBitmapApplyEffect: ", ex); + return false; + } + finally + { + try + { + if (hEffect != IntPtr.Zero) + { + // Delete the effect + GdipDeleteEffect(hEffect); + } + + if (hBlurParams != IntPtr.Zero) + { + // Free the memory + Marshal.FreeHGlobal(hBlurParams); + } + } + catch (Exception ex) + { + _isBlurEnabled = false; + Log.Error("Problem cleaning up ApplyBlur: ", ex); + } + } + } + + /// + /// Draw the image on the graphics with GDI+ blur effect + /// + /// false if there is no GDI+ available or an exception occurred + public static bool DrawWithBlur(Graphics graphics, Bitmap image, Rectangle source, Matrix transform, ImageAttributes imageAttributes, int radius, bool expandEdges) + { + if (!IsBlurPossible(radius)) + { + return false; + } + + IntPtr hBlurParams = IntPtr.Zero; + IntPtr hEffect = IntPtr.Zero; + + try + { + // Create the GDI+ BlurEffect, using the Guid + int status = GdipCreateEffect(BlurEffectGuid, out hEffect); + if (status != 0) + { + return false; + } + + // Create a BlurParams struct and set the values + var blurParams = new BlurParams + { + Radius = radius, + ExpandEdges = false + }; + //blurParams.Padding = radius; + + // Allocate space in unmanaged memory + hBlurParams = Marshal.AllocHGlobal(Marshal.SizeOf(blurParams)); + // Copy the structure to the unmanaged memory + Marshal.StructureToPtr(blurParams, hBlurParams, true); + + // Set the blurParams to the effect + GdipSetEffectParameters(hEffect, hBlurParams, (uint) Marshal.SizeOf(blurParams)); + + // Somewhere it said we can use destinationBitmap.GetHbitmap(), this doesn't work!! + // Get the private nativeImage property from the Bitmap + IntPtr hBitmap = GetNativeImage(image); + IntPtr hGraphics = GetNativeGraphics(graphics); + IntPtr hMatrix = GetNativeMatrix(transform); + IntPtr hAttributes = GetNativeImageAttributes(imageAttributes); + + // Create a RECT from the Rectangle + RECTF sourceRecf = new RECTF(source); + // Apply the effect to the bitmap in the specified area + GdipDrawImageFX(hGraphics, hBitmap, ref sourceRecf, hMatrix, hEffect, hAttributes, GpUnit.UnitPixel); + + // Everything worked, return true + return true; + } + catch (Exception ex) + { + _isBlurEnabled = false; + Log.Error("Problem using GdipDrawImageFX: ", ex); + return false; + } + finally + { + try + { + if (hEffect != IntPtr.Zero) + { + // Delete the effect + GdipDeleteEffect(hEffect); + } + + if (hBlurParams != IntPtr.Zero) + { + // Free the memory + Marshal.FreeHGlobal(hBlurParams); + } + } + catch (Exception ex) + { + _isBlurEnabled = false; + Log.Error("Problem cleaning up DrawWithBlur: ", ex); + } + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Kernel32.cs b/src/Greenshot.Base/UnmanagedHelpers/Kernel32.cs new file mode 100644 index 000000000..310864c75 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Kernel32.cs @@ -0,0 +1,135 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using System.Text; +using Greenshot.Base.UnmanagedHelpers.Enums; + +namespace Greenshot.Base.UnmanagedHelpers +{ + /// + /// Description of Kernel32. + /// + public class Kernel32 + { + public const uint ATTACHCONSOLE_ATTACHPARENTPROCESS = 0x0ffffffff; // default value if not specifing a process ID + + [DllImport("kernel32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool AttachConsole(uint dwProcessId); + + [DllImport("kernel32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool AllocConsole(); + + [DllImport("kernel32", SetLastError = true)] + public static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId); + + [DllImport("kernel32", SetLastError = true)] + public static extern uint SuspendThread(IntPtr hThread); + + [DllImport("kernel32", SetLastError = true)] + public static extern int ResumeThread(IntPtr hThread); + + [DllImport("kernel32", SetLastError = true)] + public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, bool bInheritHandle, int dwProcessId); + + [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool QueryFullProcessImageName(IntPtr hProcess, uint dwFlags, StringBuilder lpExeName, ref uint lpdwSize); + + [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern uint QueryDosDevice(string lpDeviceName, StringBuilder lpTargetPath, uint uuchMax); + + [DllImport("kernel32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CloseHandle(IntPtr hObject); + + [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)] + public static extern int GetPackageFullName(IntPtr hProcess, ref Int32 packageFullNameLength, StringBuilder fullName); + + + /// + /// Method to get the process path + /// + /// + /// + public static string GetProcessPath(int processid) + { + StringBuilder _PathBuffer = new StringBuilder(512); + // Try the GetModuleFileName method first since it's the fastest. + // May return ACCESS_DENIED (due to VM_READ flag) if the process is not owned by the current user. + // Will fail if we are compiled as x86 and we're trying to open a 64 bit process...not allowed. + IntPtr hprocess = OpenProcess(ProcessAccessFlags.QueryInformation | ProcessAccessFlags.VMRead, false, processid); + if (hprocess != IntPtr.Zero) + { + try + { + if (PsAPI.GetModuleFileNameEx(hprocess, IntPtr.Zero, _PathBuffer, (uint) _PathBuffer.Capacity) > 0) + { + return _PathBuffer.ToString(); + } + } + finally + { + CloseHandle(hprocess); + } + } + + hprocess = OpenProcess(ProcessAccessFlags.QueryInformation, false, processid); + if (hprocess != IntPtr.Zero) + { + try + { + // Try this method for Vista or higher operating systems + uint size = (uint) _PathBuffer.Capacity; + if ((Environment.OSVersion.Version.Major >= 6) && (QueryFullProcessImageName(hprocess, 0, _PathBuffer, ref size) && (size > 0))) + { + return _PathBuffer.ToString(); + } + + // Try the GetProcessImageFileName method + if (PsAPI.GetProcessImageFileName(hprocess, _PathBuffer, (uint) _PathBuffer.Capacity) > 0) + { + string dospath = _PathBuffer.ToString(); + foreach (string drive in Environment.GetLogicalDrives()) + { + if (QueryDosDevice(drive.TrimEnd('\\'), _PathBuffer, (uint) _PathBuffer.Capacity) > 0) + { + if (dospath.StartsWith(_PathBuffer.ToString())) + { + return drive + dospath.Remove(0, _PathBuffer.Length); + } + } + } + } + } + finally + { + CloseHandle(hprocess); + } + } + + return string.Empty; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/PsAPI.cs b/src/Greenshot.Base/UnmanagedHelpers/PsAPI.cs new file mode 100644 index 000000000..9ae782b86 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/PsAPI.cs @@ -0,0 +1,56 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; +using log4net; + +namespace Greenshot.Base.UnmanagedHelpers +{ + /// + /// Description of PsAPI. + /// + public class PsAPI + { + private static readonly ILog LOG = LogManager.GetLogger(typeof(PsAPI)); + + [DllImport("psapi", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, StringBuilder lpFilename, uint nSize); + + [DllImport("psapi", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern uint GetProcessImageFileName(IntPtr hProcess, StringBuilder lpImageFileName, uint nSize); + + [DllImport("psapi")] + private static extern int EmptyWorkingSet(IntPtr hwProc); + + /// + /// Make the process use less memory by emptying the working set + /// + public static void EmptyWorkingSet() + { + LOG.Info("Calling EmptyWorkingSet"); + using Process currentProcess = Process.GetCurrentProcess(); + EmptyWorkingSet(currentProcess.Handle); + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/SafeCurrentInputDesktopHandle.cs b/src/Greenshot.Base/UnmanagedHelpers/SafeCurrentInputDesktopHandle.cs new file mode 100644 index 000000000..113117eae --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/SafeCurrentInputDesktopHandle.cs @@ -0,0 +1,45 @@ +using System; +using System.Security.Permissions; +using Greenshot.Base.UnmanagedHelpers.Enums; +using log4net; +using Microsoft.Win32.SafeHandles; + +namespace Greenshot.Base.UnmanagedHelpers +{ + /// + /// A SafeHandle class implementation for the current input desktop + /// + public class SafeCurrentInputDesktopHandle : SafeHandleZeroOrMinusOneIsInvalid + { + private static readonly ILog LOG = LogManager.GetLogger(typeof(SafeCurrentInputDesktopHandle)); + + public SafeCurrentInputDesktopHandle() : base(true) + { + IntPtr hDesktop = User32.OpenInputDesktop(0, true, DesktopAccessRight.GENERIC_ALL); + if (hDesktop != IntPtr.Zero) + { + SetHandle(hDesktop); + if (User32.SetThreadDesktop(hDesktop)) + { + LOG.DebugFormat("Switched to desktop {0}", hDesktop); + } + else + { + LOG.WarnFormat("Couldn't switch to desktop {0}", hDesktop); + LOG.Error(User32.CreateWin32Exception("SetThreadDesktop")); + } + } + else + { + LOG.Warn("Couldn't get current desktop."); + LOG.Error(User32.CreateWin32Exception("OpenInputDesktop")); + } + } + + [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] + protected override bool ReleaseHandle() + { + return User32.CloseDesktop(handle); + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/SafeIconHandle.cs b/src/Greenshot.Base/UnmanagedHelpers/SafeIconHandle.cs new file mode 100644 index 000000000..acb280287 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/SafeIconHandle.cs @@ -0,0 +1,33 @@ +using System; +using System.Security; +using System.Security.Permissions; +using Microsoft.Win32.SafeHandles; + +namespace Greenshot.Base.UnmanagedHelpers +{ + /// + /// A SafeHandle class implementation for the hIcon + /// + public class SafeIconHandle : SafeHandleZeroOrMinusOneIsInvalid + { + /// + /// Needed for marshalling return values + /// + [SecurityCritical] + public SafeIconHandle() : base(true) + { + } + + + public SafeIconHandle(IntPtr hIcon) : base(true) + { + SetHandle(hIcon); + } + + [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] + protected override bool ReleaseHandle() + { + return User32.DestroyIcon(handle); + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/SafeWindowDcHandle.cs b/src/Greenshot.Base/UnmanagedHelpers/SafeWindowDcHandle.cs new file mode 100644 index 000000000..7ccdbf684 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/SafeWindowDcHandle.cs @@ -0,0 +1,66 @@ +using System; +using System.Runtime.InteropServices; +using System.Security; +using System.Security.Permissions; +using Microsoft.Win32.SafeHandles; + +namespace Greenshot.Base.UnmanagedHelpers +{ + /// + /// A WindowDC SafeHandle implementation + /// + public class SafeWindowDcHandle : SafeHandleZeroOrMinusOneIsInvalid + { + [DllImport("user32", SetLastError = true)] + private static extern IntPtr GetWindowDC(IntPtr hWnd); + + [DllImport("user32", SetLastError = true)] + private static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC); + + private readonly IntPtr _hWnd; + + /// + /// Needed for marshalling return values + /// + public SafeWindowDcHandle() : base(true) + { + } + + [SecurityCritical] + public SafeWindowDcHandle(IntPtr hWnd, IntPtr preexistingHandle) : base(true) + { + _hWnd = hWnd; + SetHandle(preexistingHandle); + } + + [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] + protected override bool ReleaseHandle() + { + bool returnValue = ReleaseDC(_hWnd, handle); + return returnValue; + } + + /// + /// Creates a DC as SafeWindowDcHandle for the whole of the specified hWnd + /// + /// IntPtr + /// SafeWindowDcHandle + public static SafeWindowDcHandle FromWindow(IntPtr hWnd) + { + if (hWnd == IntPtr.Zero) + { + return null; + } + + var hDcDesktop = GetWindowDC(hWnd); + return new SafeWindowDcHandle(hWnd, hDcDesktop); + } + + public static SafeWindowDcHandle FromDesktop() + { + IntPtr hWndDesktop = User32.GetDesktopWindow(); + IntPtr hDCDesktop = GetWindowDC(hWndDesktop); + return new SafeWindowDcHandle(hWndDesktop, hDCDesktop); + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Shell32.cs b/src/Greenshot.Base/UnmanagedHelpers/Shell32.cs new file mode 100644 index 000000000..528cf89eb --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Shell32.cs @@ -0,0 +1,123 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.IO; +using System.Runtime.InteropServices; + +namespace Greenshot.Base.UnmanagedHelpers +{ + /// + /// Description of Shell32. + /// + public static class Shell32 + { + [DllImport("shell32", CharSet = CharSet.Unicode)] + public static extern int ExtractIconEx(string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons); + + [DllImport("shell32", CharSet = CharSet.Unicode)] + private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, uint uFlags); + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + private struct SHFILEINFO + { + public readonly IntPtr hIcon; + public readonly int iIcon; + public readonly uint dwAttributes; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public readonly string szDisplayName; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)] + public readonly string szTypeName; + }; + + // Browsing for directory. + + private const uint SHGFI_ICON = 0x000000100; // get icon + private const uint SHGFI_LINKOVERLAY = 0x000008000; // put a link overlay on icon + private const uint SHGFI_LARGEICON = 0x000000000; // get large icon + private const uint SHGFI_SMALLICON = 0x000000001; // get small icon + private const uint SHGFI_USEFILEATTRIBUTES = 0x000000010; // use passed dwFileAttribute + private const uint FILE_ATTRIBUTE_NORMAL = 0x00000080; + + /// + /// Options to specify the size of icons to return. + /// + public enum IconSize + { + /// + /// Specify large icon - 32 pixels by 32 pixels. + /// + Large = 0, + + /// + /// Specify small icon - 16 pixels by 16 pixels. + /// + Small = 1 + } + + /// + /// Returns an icon for a given file extension - indicated by the name parameter. + /// See: https://msdn.microsoft.com/en-us/library/windows/desktop/bb762179(v=vs.85).aspx + /// + /// Filename + /// Large or small + /// Whether to include the link icon + /// System.Drawing.Icon + public static Icon GetFileIcon(string filename, IconSize size, bool linkOverlay) + { + SHFILEINFO shfi = new SHFILEINFO(); + // SHGFI_USEFILEATTRIBUTES makes it simulate, just gets the icon for the extension + uint flags = SHGFI_ICON | SHGFI_USEFILEATTRIBUTES; + + if (linkOverlay) + { + flags += SHGFI_LINKOVERLAY; + } + + // Check the size specified for return. + if (IconSize.Small == size) + { + flags += SHGFI_SMALLICON; + } + else + { + flags += SHGFI_LARGEICON; + } + + SHGetFileInfo(Path.GetFileName(filename), FILE_ATTRIBUTE_NORMAL, ref shfi, (uint) Marshal.SizeOf(shfi), flags); + + // Only return an icon if we really got one + if (shfi.hIcon != IntPtr.Zero) + { + // Copy (clone) the returned icon to a new object, thus allowing us to clean-up properly + Icon icon = (Icon) Icon.FromHandle(shfi.hIcon).Clone(); + // Cleanup + User32.DestroyIcon(shfi.hIcon); + return icon; + } + + return null; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/Core/Enums/ExifOrientations.cs b/src/Greenshot.Base/UnmanagedHelpers/Structs/CursorInfo.cs similarity index 76% rename from src/Greenshot.Base/Core/Enums/ExifOrientations.cs rename to src/Greenshot.Base/UnmanagedHelpers/Structs/CursorInfo.cs index b566619da..219a634f6 100644 --- a/src/Greenshot.Base/Core/Enums/ExifOrientations.cs +++ b/src/Greenshot.Base/UnmanagedHelpers/Structs/CursorInfo.cs @@ -19,18 +19,17 @@ * along with this program. If not, see . */ -namespace Greenshot.Base.Core.Enums +using System; +using System.Runtime.InteropServices; + +namespace Greenshot.Base.UnmanagedHelpers.Structs { - internal enum ExifOrientations : byte + [StructLayout(LayoutKind.Sequential)] + public struct CursorInfo { - Unknown = 0, - TopLeft = 1, - TopRight = 2, - BottomRight = 3, - BottomLeft = 4, - LeftTop = 5, - RightTop = 6, - RightBottom = 7, - LeftBottom = 8, + public int cbSize; + public int flags; + public IntPtr hCursor; + public POINT ptScreenPos; } } \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Structs/IconInfo.cs b/src/Greenshot.Base/UnmanagedHelpers/Structs/IconInfo.cs new file mode 100644 index 000000000..470862304 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Structs/IconInfo.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Runtime.InteropServices; + +namespace Greenshot.Base.UnmanagedHelpers.Structs +{ + [StructLayout(LayoutKind.Sequential)] + public struct IconInfo + { + public bool fIcon; + public int xHotspot; + public int yHotspot; + public IntPtr hbmMask; + public IntPtr hbmColor; + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Structs/POINT.cs b/src/Greenshot.Base/UnmanagedHelpers/Structs/POINT.cs new file mode 100644 index 000000000..fe65a6b8d --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Structs/POINT.cs @@ -0,0 +1,66 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Runtime.InteropServices; + +namespace Greenshot.Base.UnmanagedHelpers.Structs +{ + [StructLayout(LayoutKind.Sequential), Serializable()] + public struct POINT + { + public int X; + public int Y; + + public POINT(int x, int y) + { + X = x; + Y = y; + } + + public POINT(Point point) + { + X = point.X; + Y = point.Y; + } + + public static implicit operator Point(POINT p) + { + return new Point(p.X, p.Y); + } + + public static implicit operator POINT(Point p) + { + return new POINT(p.X, p.Y); + } + + public Point ToPoint() + { + return new Point(X, Y); + } + + public override string ToString() + { + return X + "," + Y; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Structs/RECT.cs b/src/Greenshot.Base/UnmanagedHelpers/Structs/RECT.cs new file mode 100644 index 000000000..d28f11d2b --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Structs/RECT.cs @@ -0,0 +1,176 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Runtime.InteropServices; + +namespace Greenshot.Base.UnmanagedHelpers.Structs +{ + [StructLayout(LayoutKind.Sequential), Serializable()] + public struct RECT + { + private int _Left; + private int _Top; + private int _Right; + private int _Bottom; + + public RECT(RECT rectangle) + : this(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom) + { + } + + public RECT(Rectangle rectangle) + : this(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom) + { + } + + public RECT(int left, int top, int right, int bottom) + { + _Left = left; + _Top = top; + _Right = right; + _Bottom = bottom; + } + + public int X + { + get { return _Left; } + set { _Left = value; } + } + + public int Y + { + get { return _Top; } + set { _Top = value; } + } + + public int Left + { + get { return _Left; } + set { _Left = value; } + } + + public int Top + { + get { return _Top; } + set { _Top = value; } + } + + public int Right + { + get { return _Right; } + set { _Right = value; } + } + + public int Bottom + { + get { return _Bottom; } + set { _Bottom = value; } + } + + public int Height + { + get { return _Bottom - _Top; } + set { _Bottom = value - _Top; } + } + + public int Width + { + get { return _Right - _Left; } + set { _Right = value + _Left; } + } + + public Point Location + { + get { return new Point(Left, Top); } + set + { + _Left = value.X; + _Top = value.Y; + } + } + + public Size Size + { + get { return new Size(Width, Height); } + set + { + _Right = value.Width + _Left; + _Bottom = value.Height + _Top; + } + } + + public static implicit operator Rectangle(RECT rectangle) + { + return new Rectangle(rectangle.Left, rectangle.Top, rectangle.Width, rectangle.Height); + } + + public static implicit operator RECT(Rectangle rectangle) + { + return new RECT(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom); + } + + public static bool operator ==(RECT rectangle1, RECT rectangle2) + { + return rectangle1.Equals(rectangle2); + } + + public static bool operator !=(RECT rectangle1, RECT rectangle2) + { + return !rectangle1.Equals(rectangle2); + } + + public override string ToString() + { + return "{Left: " + _Left + "; " + "Top: " + _Top + "; Right: " + _Right + "; Bottom: " + _Bottom + "}"; + } + + public override int GetHashCode() + { + return ToString().GetHashCode(); + } + + public bool Equals(RECT rectangle) + { + return rectangle.Left == _Left && rectangle.Top == _Top && rectangle.Right == _Right && rectangle.Bottom == _Bottom; + } + + public Rectangle ToRectangle() + { + return new Rectangle(Left, Top, Width, Height); + } + + public override bool Equals(object Object) + { + if (Object is RECT) + { + return Equals((RECT) Object); + } + else if (Object is Rectangle) + { + return Equals(new RECT((Rectangle) Object)); + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Structs/RECTF.cs b/src/Greenshot.Base/UnmanagedHelpers/Structs/RECTF.cs new file mode 100644 index 000000000..26be5eb63 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Structs/RECTF.cs @@ -0,0 +1,131 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Drawing; +using System.Runtime.InteropServices; + +namespace Greenshot.Base.UnmanagedHelpers.Structs +{ + /// + /// A floating point GDI Plus width/hight based rectangle. + /// + [StructLayout(LayoutKind.Sequential)] + public struct RECTF + { + /// + /// The X corner location of the rectangle. + /// + public float X; + + /// + /// The Y corner location of the rectangle. + /// + public float Y; + + /// + /// The width of the rectangle. + /// + public float Width; + + /// + /// The height of the rectangle. + /// + public float Height; + + /// + /// Creates a new GDI Plus rectangle. + /// + /// The X corner location of the rectangle. + /// The Y corner location of the rectangle. + /// The width of the rectangle. + /// The height of the rectangle. + public RECTF(float x, float y, float width, float height) + { + X = x; + Y = y; + Width = width; + Height = height; + } + + /// + /// Creates a new GDI Plus rectangle from a System.Drawing.RectangleF. + /// + /// The rectangle to base this GDI Plus rectangle on. + public RECTF(RectangleF rect) + { + X = rect.X; + Y = rect.Y; + Width = rect.Width; + Height = rect.Height; + } + + /// + /// Creates a new GDI Plus rectangle from a System.Drawing.Rectangle. + /// + /// The rectangle to base this GDI Plus rectangle on. + public RECTF(Rectangle rect) + { + X = rect.X; + Y = rect.Y; + Width = rect.Width; + Height = rect.Height; + } + + /// + /// Returns a RectangleF for this GDI Plus rectangle. + /// + /// A System.Drawing.RectangleF structure. + public RectangleF ToRectangle() + { + return new RectangleF(X, Y, Width, Height); + } + + /// + /// Returns a RectangleF for a GDI Plus rectangle. + /// + /// The GDI Plus rectangle to get the RectangleF for. + /// A System.Drawing.RectangleF structure. + public static RectangleF ToRectangle(RECTF rect) + { + return rect.ToRectangle(); + } + + /// + /// Returns a GDI Plus rectangle for a RectangleF structure. + /// + /// The RectangleF to get the GDI Plus rectangle for. + /// A GDI Plus rectangle structure. + public static RECTF FromRectangle(RectangleF rect) + { + return new RECTF(rect); + } + + /// + /// Returns a GDI Plus rectangle for a Rectangle structure. + /// + /// The Rectangle to get the GDI Plus rectangle for. + /// A GDI Plus rectangle structure. + public static RECTF FromRectangle(Rectangle rect) + { + return new RECTF(rect); + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Editor/Helpers/ShapeAngleRoundBehavior.cs b/src/Greenshot.Base/UnmanagedHelpers/Structs/SCROLLINFO.cs similarity index 71% rename from src/Greenshot.Editor/Helpers/ShapeAngleRoundBehavior.cs rename to src/Greenshot.Base/UnmanagedHelpers/Structs/SCROLLINFO.cs index ba173df9f..cf9eaa937 100644 --- a/src/Greenshot.Editor/Helpers/ShapeAngleRoundBehavior.cs +++ b/src/Greenshot.Base/UnmanagedHelpers/Structs/SCROLLINFO.cs @@ -1,39 +1,38 @@ -/* +/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * + * * For more information see: https://getgreenshot.org/ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 1 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ using System; +using System.Runtime.InteropServices; -namespace Greenshot.Editor.Helpers +namespace Greenshot.Base.UnmanagedHelpers.Structs { - public class ShapeAngleRoundBehavior : IDoubleProcessor + [Serializable, StructLayout(LayoutKind.Sequential)] + public struct SCROLLINFO { - public static readonly ShapeAngleRoundBehavior INSTANCE = new(); - - private ShapeAngleRoundBehavior() - { - } - - public double Process(double angle) - { - return Math.Round((angle + 45) / 90) * 90 - 45; - } + public int cbSize; + public int fMask; + public int nMin; + public int nMax; + public int nPage; + public int nPos; + public int nTrackPos; } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Confluence/Properties/AssemblyInfo.cs b/src/Greenshot.Base/UnmanagedHelpers/Structs/SIZE.cs similarity index 62% rename from src/Greenshot.Plugin.Confluence/Properties/AssemblyInfo.cs rename to src/Greenshot.Base/UnmanagedHelpers/Structs/SIZE.cs index 8d2833e17..a5a59b63f 100644 --- a/src/Greenshot.Plugin.Confluence/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Base/UnmanagedHelpers/Structs/SIZE.cs @@ -1,6 +1,6 @@ /* * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom * * For more information see: https://getgreenshot.org/ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot @@ -19,16 +19,33 @@ * along with this program. If not, see . */ -using System.Reflection; +using System; +using System.Drawing; using System.Runtime.InteropServices; -using Greenshot.Base.Interfaces.Plugin; -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyDescription("A plugin to upload images to Confluence")] -[assembly: AssemblyPluginIdentifier("Confluence Plugin")] +namespace Greenshot.Base.UnmanagedHelpers.Structs +{ + [StructLayout(LayoutKind.Sequential), Serializable()] + public struct SIZE + { + public int Width; + public int Height; -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] \ No newline at end of file + public SIZE(Size size) : this(size.Width, size.Height) + { + } + + public SIZE(int width, int height) + { + Width = width; + Height = height; + } + + public Size ToSize() + { + return new Size(Width, Height); + } + + public bool IsEmpty => Width * Height == 0; + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Structs/WindowInfo.cs b/src/Greenshot.Base/UnmanagedHelpers/Structs/WindowInfo.cs new file mode 100644 index 000000000..f0b85cf2e --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Structs/WindowInfo.cs @@ -0,0 +1,52 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Runtime.InteropServices; + +namespace Greenshot.Base.UnmanagedHelpers.Structs +{ + /// + /// The structure for the WindowInfo + /// See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms632610%28v=vs.85%29.aspx + /// + [StructLayout(LayoutKind.Sequential), Serializable] + public struct WindowInfo + { + public uint cbSize; + public RECT rcWindow; + public RECT rcClient; + public uint dwStyle; + public uint dwExStyle; + public uint dwWindowStatus; + public uint cxWindowBorders; + public uint cyWindowBorders; + public ushort atomWindowType; + + public ushort wCreatorVersion; + + // Allows automatic initialization of "cbSize" with "new WINDOWINFO(null/true/false)". + public WindowInfo(bool? filler) : this() + { + cbSize = (uint) (Marshal.SizeOf(typeof(WindowInfo))); + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Structs/WindowPlacement.cs b/src/Greenshot.Base/UnmanagedHelpers/Structs/WindowPlacement.cs new file mode 100644 index 000000000..a542a8cf0 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Structs/WindowPlacement.cs @@ -0,0 +1,80 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using Greenshot.Base.UnmanagedHelpers.Enums; + +namespace Greenshot.Base.UnmanagedHelpers.Structs +{ + /// + /// Contains information about the placement of a window on the screen. + /// + [StructLayout(LayoutKind.Sequential), Serializable()] + public struct WindowPlacement + { + /// + /// The length of the structure, in bytes. Before calling the GetWindowPlacement or SetWindowPlacement functions, set this member to sizeof(WINDOWPLACEMENT). + /// + /// GetWindowPlacement and SetWindowPlacement fail if this member is not set correctly. + /// + /// + public int Length; + + /// + /// Specifies flags that control the position of the minimized window and the method by which the window is restored. + /// + public WindowPlacementFlags Flags; + + /// + /// The current show state of the window. + /// + public ShowWindowCommand ShowCmd; + + /// + /// The coordinates of the window's upper-left corner when the window is minimized. + /// + public POINT MinPosition; + + /// + /// The coordinates of the window's upper-left corner when the window is maximized. + /// + public POINT MaxPosition; + + /// + /// The window's coordinates when the window is in the restored position. + /// + public RECT NormalPosition; + + /// + /// Gets the default (empty) value. + /// + public static WindowPlacement Default + { + get + { + WindowPlacement result = new WindowPlacement(); + result.Length = Marshal.SizeOf(result); + return result; + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/User32.cs b/src/Greenshot.Base/UnmanagedHelpers/User32.cs new file mode 100644 index 000000000..9764c3a89 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/User32.cs @@ -0,0 +1,333 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Text; +using System.Windows.Forms; +using Greenshot.Base.Core.Enums; +using Greenshot.Base.UnmanagedHelpers.Enums; +using Greenshot.Base.UnmanagedHelpers.Structs; +using log4net; + +namespace Greenshot.Base.UnmanagedHelpers +{ + /// + /// User32 Wrappers + /// + public static class User32 + { + private static readonly ILog LOG = LogManager.GetLogger(typeof(User32)); + private static bool _CanCallGetPhysicalCursorPos = true; + public const int SC_RESTORE = 0xF120; + public const int SC_MAXIMIZE = 0xF030; + public const int SC_MINIMIZE = 0xF020; + + // For MonitorFromWindow + public const int MONITOR_DEFAULTTONULL = 0; + public const int MONITOR_DEFAULTTONEAREST = 2; + public const int CURSOR_SHOWING = 0x00000001; + + /// + /// Determines whether the specified window handle identifies an existing window. + /// + /// A handle to the window to be tested. + /// + /// If the window handle identifies an existing window, the return value is true. + /// If the window handle does not identify an existing window, the return value is false. + /// + [DllImport("user32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool IsWindow(IntPtr hWnd); + + [DllImport("user32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool IsWindowVisible(IntPtr hWnd); + + [DllImport("user32", SetLastError = true)] + public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int processId); + + [DllImport("user32", SetLastError = true)] + public static extern int GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId); + + [DllImport("user32")] + public static extern IntPtr AttachThreadInput(int idAttach, int idAttachTo, int fAttach); + + [DllImport("user32", SetLastError = true)] + public static extern IntPtr GetParent(IntPtr hWnd); + + [DllImport("user32", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int cch); + + [DllImport("user32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool BringWindowToTop(IntPtr hWnd); + + [DllImport("user32", SetLastError = true)] + public static extern IntPtr GetForegroundWindow(); + + [DllImport("user32", SetLastError = true)] + public static extern IntPtr GetDesktopWindow(); + + [DllImport("user32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetForegroundWindow(IntPtr hWnd); + + [DllImport("user32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetWindowPlacement(IntPtr hWnd, ref WindowPlacement lpwndpl); + + [DllImport("user32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WindowPlacement lpwndpl); + + [DllImport("user32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool IsIconic(IntPtr hWnd); + + [DllImport("user32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool IsZoomed(IntPtr hWnd); + + [DllImport("user32", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + + [DllImport("user32", SetLastError = true)] + public static extern uint GetClassLong(IntPtr hWnd, int nIndex); + + [DllImport("user32", SetLastError = true, EntryPoint = "GetClassLongPtr")] + public static extern IntPtr GetClassLongPtr(IntPtr hWnd, int nIndex); + + [DllImport("user32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool PrintWindow(IntPtr hWnd, IntPtr hDc, PrintWindowFlags pwFlags); + + [DllImport("user32", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern IntPtr SendMessage(IntPtr hWnd, uint wMsg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32", SetLastError = true, EntryPoint = "GetWindowLong")] + public static extern int GetWindowLong(IntPtr hWnd, int index); + + [DllImport("user32", SetLastError = true, EntryPoint = "GetWindowLongPtr")] + public static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex); + + [DllImport("user32", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern int SetWindowLong(IntPtr hWnd, int index, int styleFlags); + + [DllImport("user32", SetLastError = true, EntryPoint = "SetWindowLongPtr")] + public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int index, IntPtr styleFlags); + + [DllImport("user32", SetLastError = true)] + public static extern IntPtr MonitorFromWindow(IntPtr hWnd, MonitorFrom dwFlags); + + [DllImport("user32", SetLastError = true)] + public static extern IntPtr MonitorFromRect([In] ref RECT lprc, uint dwFlags); + + [DllImport("user32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetWindowInfo(IntPtr hWnd, ref WindowInfo pwi); + + [DllImport("user32", SetLastError = true)] + public static extern int EnumChildWindows(IntPtr hWndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam); + + [DllImport("user32", SetLastError = true)] + public static extern RegionResult GetWindowRgn(IntPtr hWnd, SafeHandle hRgn); + + [DllImport("user32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, WindowPos uFlags); + + [DllImport("user32", SetLastError = true)] + public static extern IntPtr GetClipboardOwner(); + + // Added for finding Metro apps, Greenshot 1.1 + [DllImport("user32", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + + [DllImport("user32", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern IntPtr FindWindowEx(IntPtr hWndParent, IntPtr hWndChildAfter, string lpszClass, string lpszWindow); + + /// uiFlags: 0 - Count of GDI objects + /// uiFlags: 1 - Count of USER objects + /// - Win32 GDI objects (pens, brushes, fonts, palettes, regions, device contexts, bitmap headers) + /// - Win32 USER objects: + /// - WIN32 resources (accelerator tables, bitmap resources, dialog box templates, font resources, menu resources, raw data resources, string table entries, message table entries, cursors/icons) + /// - Other USER objects (windows, menus) + /// + [DllImport("user32", SetLastError = true)] + public static extern uint GetGuiResources(IntPtr hProcess, uint uiFlags); + + [DllImport("user32", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern uint RegisterWindowMessage(string lpString); + + [DllImport("user32", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam, SendMessageTimeoutFlags fuFlags, uint uTimeout, out UIntPtr lpdwResult); + + [DllImport("user32", SetLastError = true)] + private static extern bool GetPhysicalCursorPos(out POINT cursorLocation); + + /// + /// The following is used for Icon handling + /// + /// + /// + [DllImport("user32", SetLastError = true)] + public static extern SafeIconHandle CopyIcon(IntPtr hIcon); + + [DllImport("user32", SetLastError = true)] + public static extern bool DestroyIcon(IntPtr hIcon); + + [DllImport("user32", SetLastError = true)] + public static extern bool GetCursorInfo(out CursorInfo cursorInfo); + + [DllImport("user32", SetLastError = true)] + public static extern bool GetIconInfo(SafeIconHandle iconHandle, out IconInfo iconInfo); + + [DllImport("user32", SetLastError = true)] + public static extern IntPtr SetCapture(IntPtr hWnd); + + [DllImport("user32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ReleaseCapture(); + + [DllImport("user32", SetLastError = true)] + public static extern IntPtr CreateIconIndirect(ref IconInfo icon); + + [DllImport("user32", SetLastError = true)] + public static extern IntPtr OpenInputDesktop(uint dwFlags, bool fInherit, DesktopAccessRight dwDesiredAccess); + + [DllImport("user32", SetLastError = true)] + public static extern bool SetThreadDesktop(IntPtr hDesktop); + + [DllImport("user32", SetLastError = true)] + public static extern bool CloseDesktop(IntPtr hDesktop); + + /// + /// Retrieves the cursor location safely, accounting for DPI settings in Vista/Windows 7. + /// + /// Point with cursor location, relative to the origin of the monitor setup + /// (i.e. negative coordinates arepossible in multiscreen setups) + public static Point GetCursorLocation() + { + if (Environment.OSVersion.Version.Major >= 6 && _CanCallGetPhysicalCursorPos) + { + try + { + if (GetPhysicalCursorPos(out var cursorLocation)) + { + return new Point(cursorLocation.X, cursorLocation.Y); + } + + Win32Error error = Win32.GetLastErrorCode(); + LOG.ErrorFormat("Error retrieving PhysicalCursorPos : {0}", Win32.GetMessage(error)); + } + catch (Exception ex) + { + LOG.Error("Exception retrieving PhysicalCursorPos, no longer calling this. Cause :", ex); + _CanCallGetPhysicalCursorPos = false; + } + } + + return new Point(Cursor.Position.X, Cursor.Position.Y); + } + + /// + /// Wrapper for the GetClassLong which decides if the system is 64-bit or not and calls the right one. + /// + /// IntPtr + /// int + /// IntPtr + public static IntPtr GetClassLongWrapper(IntPtr hWnd, int nIndex) + { + if (IntPtr.Size > 4) + { + return GetClassLongPtr(hWnd, nIndex); + } + else + { + return new IntPtr(GetClassLong(hWnd, nIndex)); + } + } + + /// + /// Wrapper for the GetWindowLong which decides if the system is 64-bit or not and calls the right one. + /// + /// + /// + /// + public static long GetWindowLongWrapper(IntPtr hWnd, int nIndex) + { + if (IntPtr.Size == 8) + { + return GetWindowLongPtr(hWnd, nIndex).ToInt64(); + } + else + { + return GetWindowLong(hWnd, nIndex); + } + } + + /// + /// Wrapper for the SetWindowLong which decides if the system is 64-bit or not and calls the right one. + /// + /// + /// + /// + public static void SetWindowLongWrapper(IntPtr hWnd, int nIndex, IntPtr styleFlags) + { + if (IntPtr.Size == 8) + { + SetWindowLongPtr(hWnd, nIndex, styleFlags); + } + else + { + SetWindowLong(hWnd, nIndex, styleFlags.ToInt32()); + } + } + + public static uint GetGuiResourcesGDICount() + { + using var currentProcess = Process.GetCurrentProcess(); + return GetGuiResources(currentProcess.Handle, 0); + } + + public static uint GetGuiResourcesUserCount() + { + using var currentProcess = Process.GetCurrentProcess(); + return GetGuiResources(currentProcess.Handle, 1); + } + + /// + /// Helper method to create a Win32 exception with the windows message in it + /// + /// string with current method + /// Exception + public static Exception CreateWin32Exception(string method) + { + var exceptionToThrow = new Win32Exception(); + exceptionToThrow.Data.Add("Method", method); + return exceptionToThrow; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/Win32.cs b/src/Greenshot.Base/UnmanagedHelpers/Win32.cs new file mode 100644 index 000000000..d5b3def78 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/Win32.cs @@ -0,0 +1,69 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Runtime.InteropServices; +using System.Text; +using Greenshot.Base.UnmanagedHelpers.Enums; + +namespace Greenshot.Base.UnmanagedHelpers +{ + public static class Win32 + { + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + private static extern uint FormatMessage(uint dwFlags, IntPtr lpSource, uint dwMessageId, uint dwLanguageId, [Out] StringBuilder lpBuffer, int nSize, IntPtr arguments); + + [DllImport("kernel32.dll")] + public static extern void SetLastError(uint dwErrCode); + + public static Win32Error GetLastErrorCode() + { + return (Win32Error) Marshal.GetLastWin32Error(); + } + + public static string GetMessage(Win32Error errorCode) + { + var buffer = new StringBuilder(0x100); + + if (FormatMessage(0x3200, IntPtr.Zero, (uint) errorCode, 0, buffer, buffer.Capacity, IntPtr.Zero) == 0) + { + return "Unknown error (0x" + ((int) errorCode).ToString("x") + ")"; + } + + var result = new StringBuilder(); + int i = 0; + + while (i < buffer.Length) + { + if (!char.IsLetterOrDigit(buffer[i]) && + !char.IsPunctuation(buffer[i]) && + !char.IsSymbol(buffer[i]) && + !char.IsWhiteSpace(buffer[i])) + break; + + result.Append(buffer[i]); + i++; + } + + return result.ToString().Replace("\r\n", string.Empty); + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/UnmanagedHelpers/WinEventDelegate.cs b/src/Greenshot.Base/UnmanagedHelpers/WinEventDelegate.cs new file mode 100644 index 000000000..bd54923f7 --- /dev/null +++ b/src/Greenshot.Base/UnmanagedHelpers/WinEventDelegate.cs @@ -0,0 +1,17 @@ +using System; +using Greenshot.Base.UnmanagedHelpers.Enums; + +namespace Greenshot.Base.UnmanagedHelpers +{ + /// + /// Used with SetWinEventHook + /// + /// + /// + /// + /// + /// + /// + /// + public delegate void WinEventDelegate(IntPtr hWinEventHook, WinEvent eventType, IntPtr hWnd, EventObjects idObject, int idChild, uint dwEventThread, uint dwmsEventTime); +} \ No newline at end of file diff --git a/src/Greenshot.Base/Interfaces/Drawing/IFieldAggregator.cs b/src/Greenshot.Base/UnmanagedHelpers/WinMM.cs similarity index 69% rename from src/Greenshot.Base/Interfaces/Drawing/IFieldAggregator.cs rename to src/Greenshot.Base/UnmanagedHelpers/WinMM.cs index 5af83b39e..bee586bda 100644 --- a/src/Greenshot.Base/Interfaces/Drawing/IFieldAggregator.cs +++ b/src/Greenshot.Base/UnmanagedHelpers/WinMM.cs @@ -19,18 +19,20 @@ * along with this program. If not, see . */ -namespace Greenshot.Base.Interfaces.Drawing +using System; +using System.Runtime.InteropServices; + +namespace Greenshot.Base.UnmanagedHelpers { /// - /// The IFieldAggregator defines the connections between fields and containers + /// Windows Media /// - public interface IFieldAggregator + public class WinMM { - void UnbindElement(IDrawableContainer dc); - void BindElements(IDrawableContainerList dcs); - void BindElement(IDrawableContainer dc); - IField GetField(IFieldType fieldType); + [DllImport("winmm.dll", SetLastError = true)] + public static extern bool PlaySound(byte[] ptrToSound, UIntPtr hmod, uint fdwSound); - event FieldChangedEventHandler FieldChanged; + [DllImport("winmm.dll", SetLastError = true)] + public static extern bool PlaySound(IntPtr ptrToSound, UIntPtr hmod, uint fdwSound); } -} +} \ No newline at end of file diff --git a/src/Greenshot.Editor/Configuration/EditorConfiguration.cs b/src/Greenshot.Editor/Configuration/EditorConfiguration.cs index 3a516797c..c36794a5d 100644 --- a/src/Greenshot.Editor/Configuration/EditorConfiguration.cs +++ b/src/Greenshot.Editor/Configuration/EditorConfiguration.cs @@ -22,12 +22,11 @@ using System; using System.Collections.Generic; using System.Drawing; -using Dapplo.Windows.Common.Structs; -using Dapplo.Windows.User32.Enums; -using Dapplo.Windows.User32.Structs; using Greenshot.Base.Effects; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Base.UnmanagedHelpers.Enums; +using Greenshot.Base.UnmanagedHelpers.Structs; using Greenshot.Editor.Drawing.Fields; namespace Greenshot.Editor.Configuration @@ -51,21 +50,24 @@ namespace Greenshot.Editor.Configuration public WindowPlacementFlags WindowPlacementFlags { get; set; } [IniProperty("WindowShowCommand", Description = "Show command", DefaultValue = "Normal")] - public ShowWindowCommands ShowWindowCommand { get; set; } + public ShowWindowCommand ShowWindowCommand { get; set; } [IniProperty("WindowMinPosition", Description = "Position of minimized window", DefaultValue = "-1,-1")] - public NativePoint WindowMinPosition { get; set; } + public Point WindowMinPosition { get; set; } [IniProperty("WindowMaxPosition", Description = "Position of maximized window", DefaultValue = "-1,-1")] - public NativePoint WindowMaxPosition { get; set; } + public Point WindowMaxPosition { get; set; } [IniProperty("WindowNormalPosition", Description = "Position of normal window", DefaultValue = "100,100,400,400")] - public NativeRect WindowNormalPosition { get; set; } + public Rectangle WindowNormalPosition { get; set; } [IniProperty("ReuseEditor", Description = "Reuse already open editor", DefaultValue = "false")] public bool ReuseEditor { get; set; } - [IniProperty("FreehandSensitivity", Description = "The smaller this number, the less smoothing is used. Decrease for detailed drawing, e.g. when using a pen. Increase for smoother lines. e.g. when you want to draw a smooth line. Minimal value is 1, max is 2147483647.", DefaultValue = "3")] + [IniProperty("FreehandSensitivity", + Description = + "The smaller this number, the less smoothing is used. Decrease for detailed drawing, e.g. when using a pen. Increase for smoother lines. e.g. when you want to draw a smooth line.", + DefaultValue = "3")] public int FreehandSensitivity { get; set; } [IniProperty("SuppressSaveDialogAtClose", Description = "Suppressed the 'do you want to save' dialog when closing the editor.", DefaultValue = "False")] @@ -78,17 +80,15 @@ namespace Greenshot.Editor.Configuration public TornEdgeEffect TornEdgeEffectSettings { get; set; } [IniProperty("DefaultEditorSize", Description = "The size for the editor when it's opened without a capture", DefaultValue = "500,500")] - public NativeSize DefaultEditorSize { get; set; } + public Size DefaultEditorSize { get; set; } public override void AfterLoad() { base.AfterLoad(); - RecentColors ??= new List(); - - if (FreehandSensitivity < 1) + if (RecentColors == null) { - FreehandSensitivity = 1; + RecentColors = new List(); } } @@ -137,7 +137,10 @@ namespace Greenshot.Editor.Configuration { string requestedField = field.Scope + "." + field.FieldType.Name; // Check if the configuration exists - LastUsedFieldValues ??= new Dictionary(); + if (LastUsedFieldValues == null) + { + LastUsedFieldValues = new Dictionary(); + } // check if settings for the requesting type exist, if not create! if (LastUsedFieldValues.ContainsKey(requestedField)) @@ -152,19 +155,19 @@ namespace Greenshot.Editor.Configuration public void ResetEditorPlacement() { - WindowNormalPosition = new NativeRect(100, 100, 400, 400); - WindowMaxPosition = new NativePoint(-1, -1); - WindowMinPosition = new NativePoint(-1, -1); + WindowNormalPosition = new Rectangle(100, 100, 400, 400); + WindowMaxPosition = new Point(-1, -1); + WindowMinPosition = new Point(-1, -1); WindowPlacementFlags = 0; - ShowWindowCommand = ShowWindowCommands.Normal; + ShowWindowCommand = ShowWindowCommand.Normal; } public WindowPlacement GetEditorPlacement() { - WindowPlacement placement = WindowPlacement.Create(); - placement.NormalPosition = WindowNormalPosition; - placement.MaxPosition = WindowMaxPosition; - placement.MinPosition = WindowMinPosition; + WindowPlacement placement = WindowPlacement.Default; + placement.NormalPosition = new RECT(WindowNormalPosition); + placement.MaxPosition = new POINT(WindowMaxPosition); + placement.MinPosition = new POINT(WindowMinPosition); placement.ShowCmd = ShowWindowCommand; placement.Flags = WindowPlacementFlags; return placement; @@ -172,9 +175,9 @@ namespace Greenshot.Editor.Configuration public void SetEditorPlacement(WindowPlacement placement) { - WindowNormalPosition = placement.NormalPosition; - WindowMaxPosition = placement.MaxPosition; - WindowMinPosition = placement.MinPosition; + WindowNormalPosition = placement.NormalPosition.ToRectangle(); + WindowMaxPosition = placement.MaxPosition.ToPoint(); + WindowMinPosition = placement.MinPosition.ToPoint(); ShowWindowCommand = placement.ShowCmd; WindowPlacementFlags = placement.Flags; } diff --git a/src/Greenshot.Editor/Configuration/LanguageKeys.cs b/src/Greenshot.Editor/Configuration/LanguageKeys.cs index b1f410e66..f15b113d4 100644 --- a/src/Greenshot.Editor/Configuration/LanguageKeys.cs +++ b/src/Greenshot.Editor/Configuration/LanguageKeys.cs @@ -33,7 +33,6 @@ namespace Greenshot.Editor.Configuration contextmenu_capturefullscreen_right, contextmenu_capturefullscreen_bottom, contextmenu_captureie, - editor_autocrop_not_possible, editor_clipboardfailed, editor_close_on_save, editor_close_on_save_title, diff --git a/src/Greenshot.Editor/Controls/ColorButton.cs b/src/Greenshot.Editor/Controls/ColorButton.cs index 9e968afbc..849395330 100644 --- a/src/Greenshot.Editor/Controls/ColorButton.cs +++ b/src/Greenshot.Editor/Controls/ColorButton.cs @@ -24,7 +24,6 @@ using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Controls; using ColorDialog = Greenshot.Editor.Forms.ColorDialog; @@ -66,7 +65,7 @@ namespace Greenshot.Editor.Controls if (Image != null) { using Graphics graphics = Graphics.FromImage(Image); - graphics.FillRectangle(brush, new NativeRect(4, 17, 16, 3)); + graphics.FillRectangle(brush, new Rectangle(4, 17, 16, 3)); } // cleanup GDI Object diff --git a/src/Greenshot.Editor/Controls/FontFamilyComboBox.cs b/src/Greenshot.Editor/Controls/FontFamilyComboBox.cs index cabb40e49..b57d52e75 100644 --- a/src/Greenshot.Editor/Controls/FontFamilyComboBox.cs +++ b/src/Greenshot.Editor/Controls/FontFamilyComboBox.cs @@ -23,7 +23,6 @@ using System; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; -using Dapplo.Windows.Common.Structs; namespace Greenshot.Editor.Controls { @@ -114,12 +113,12 @@ namespace Greenshot.Editor.Controls /// /// Helper method to draw the string /// - /// Graphics - /// FontFamily - /// FontStyle - /// NativeRect - /// string - private void DrawText(Graphics graphics, FontFamily fontFamily, FontStyle fontStyle, NativeRect bounds, string text) + /// + /// + /// + /// + /// + private void DrawText(Graphics graphics, FontFamily fontFamily, FontStyle fontStyle, Rectangle bounds, string text) { using Font font = new Font(fontFamily, Font.Size + 5, fontStyle, GraphicsUnit.Pixel); // Make sure the text is visible by centering it in the line diff --git a/src/Greenshot.Editor/Controls/MenuStripEx.cs b/src/Greenshot.Editor/Controls/MenuStripEx.cs index c140416f6..9af1da2cf 100644 --- a/src/Greenshot.Editor/Controls/MenuStripEx.cs +++ b/src/Greenshot.Editor/Controls/MenuStripEx.cs @@ -21,7 +21,7 @@ using System; using System.Windows.Forms; -using Dapplo.Windows.Messages.Enumerations; +using Greenshot.Base.UnmanagedHelpers.Enums; namespace Greenshot.Editor.Controls { diff --git a/src/Greenshot.Editor/Controls/Pipette.cs b/src/Greenshot.Editor/Controls/Pipette.cs index dc1ae1046..d31017b92 100644 --- a/src/Greenshot.Editor/Controls/Pipette.cs +++ b/src/Greenshot.Editor/Controls/Pipette.cs @@ -23,11 +23,8 @@ using System; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; -using Dapplo.Windows.Common.Structs; -using Dapplo.Windows.Icons; -using Dapplo.Windows.Icons.SafeHandles; -using Dapplo.Windows.Messages.Enumerations; -using Dapplo.Windows.User32; +using Greenshot.Base.UnmanagedHelpers; +using Greenshot.Base.UnmanagedHelpers.Enums; using Greenshot.Editor.Forms; using ColorDialog = Greenshot.Editor.Forms.ColorDialog; @@ -68,10 +65,11 @@ namespace Greenshot.Editor.Controls private static Cursor CreateCursor(Bitmap bitmap, int hotspotX, int hotspotY) { using SafeIconHandle iconHandle = new SafeIconHandle(bitmap.GetHicon()); - NativeIconMethods.GetIconInfo(iconHandle, out var iconInfo); - iconInfo.Hotspot = new NativePoint(hotspotX, hotspotY); - iconInfo.IsIcon = false; - var icon = NativeIconMethods.CreateIconIndirect(ref iconInfo); + User32.GetIconInfo(iconHandle, out var iconInfo); + iconInfo.xHotspot = hotspotX; + iconInfo.yHotspot = hotspotY; + iconInfo.fIcon = false; + var icon = User32.CreateIconIndirect(ref iconInfo); return new Cursor(icon); } @@ -112,7 +110,7 @@ namespace Greenshot.Editor.Controls { if (e.Button == MouseButtons.Left) { - User32Api.SetCapture(Handle); + User32.SetCapture(Handle); _movableShowColorForm.MoveTo(PointToScreen(new Point(e.X, e.Y))); } @@ -128,8 +126,8 @@ namespace Greenshot.Editor.Controls if (e.Button == MouseButtons.Left) { //Release Capture should consume MouseUp when canceled with the escape key - User32Api.ReleaseCapture(); - PipetteUsed?.Invoke(this, new PipetteUsedArgs(_movableShowColorForm.Color)); + User32.ReleaseCapture(); + PipetteUsed?.Invoke(this, new PipetteUsedArgs(_movableShowColorForm.color)); } base.OnMouseUp(e); @@ -185,7 +183,7 @@ namespace Greenshot.Editor.Controls { if ((int) m.WParam == VkEsc) { - User32Api.ReleaseCapture(); + User32.ReleaseCapture(); } } } diff --git a/src/Greenshot.Editor/Controls/ToolStripColorButton.cs b/src/Greenshot.Editor/Controls/ToolStripColorButton.cs index eb05a0fcc..91386b4f4 100644 --- a/src/Greenshot.Editor/Controls/ToolStripColorButton.cs +++ b/src/Greenshot.Editor/Controls/ToolStripColorButton.cs @@ -24,7 +24,6 @@ using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Controls; using ColorDialog = Greenshot.Editor.Forms.ColorDialog; @@ -64,7 +63,7 @@ namespace Greenshot.Editor.Controls if (Image != null) { using Graphics graphics = Graphics.FromImage(Image); - graphics.FillRectangle(brush, new NativeRect(0, 13, 16, 3)); + graphics.FillRectangle(brush, new Rectangle(0, 13, 16, 3)); } // cleanup GDI Object diff --git a/src/Greenshot.Editor/Destinations/EditorDestination.cs b/src/Greenshot.Editor/Destinations/EditorDestination.cs index bfc143d88..326397ec9 100644 --- a/src/Greenshot.Editor/Destinations/EditorDestination.cs +++ b/src/Greenshot.Editor/Destinations/EditorDestination.cs @@ -64,7 +64,7 @@ namespace Greenshot.Editor.Destinations return Language.GetString(LangKey.settings_destination_editor); } - return Language.GetString(LangKey.settings_destination_editor) + " - " + editor.CaptureDetails.Title?.Substring(0, Math.Min(20, editor.CaptureDetails.Title.Length)); + return Language.GetString(LangKey.settings_destination_editor) + " - " + editor.CaptureDetails.Title; } } diff --git a/src/Greenshot.Editor/Drawing/Adorners/AbstractAdorner.cs b/src/Greenshot.Editor/Drawing/Adorners/AbstractAdorner.cs index a2dd980a6..028306e24 100644 --- a/src/Greenshot.Editor/Drawing/Adorners/AbstractAdorner.cs +++ b/src/Greenshot.Editor/Drawing/Adorners/AbstractAdorner.cs @@ -22,9 +22,7 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; -using Dapplo.Windows.Dpi; +using Greenshot.Base.Core; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Base.Interfaces.Drawing.Adorners; @@ -34,17 +32,12 @@ namespace Greenshot.Editor.Drawing.Adorners { public virtual EditStatus EditStatus { get; protected set; } = EditStatus.IDLE; - private static readonly NativeSize DefaultSize = new(6, 6); - - protected NativeSize Size - { - get; - set; - } + private static readonly Size DefaultSize = new Size(6, 6); + protected Size _size; public AbstractAdorner(IDrawableContainer owner) { - Size = DpiCalculator.ScaleWithDpi(DefaultSize, owner?.Parent?.CurrentDpi ?? 96); + _size = DpiHelper.ScaleWithDpi(DefaultSize, 0); Owner = owner; } @@ -56,16 +49,17 @@ namespace Greenshot.Editor.Drawing.Adorners get { return Cursors.SizeAll; } } - public IDrawableContainer Owner { get; set; } + public virtual IDrawableContainer Owner { get; set; } /// /// Test if the point is inside the adorner /// - /// NativePoint - /// bool - public virtual bool HitTest(NativePoint point) + /// + /// + public virtual bool HitTest(Point point) { - NativeRect hitBounds = Bounds.Inflate(3, 3); + Rectangle hitBounds = Bounds; + hitBounds.Inflate(3, 3); return hitBounds.Contains(point); } @@ -100,29 +94,29 @@ namespace Greenshot.Editor.Drawing.Adorners /// /// Return the location of the adorner /// - public virtual NativePoint Location { get; set; } + public virtual Point Location { get; set; } /// /// Return the bounds of the Adorner /// - public virtual NativeRect Bounds + public virtual Rectangle Bounds { get { - NativePoint location = Location; - return new NativeRect(location.X - (Size.Width / 2), location.Y - (Size.Height / 2), Size.Width, Size.Height); + Point location = Location; + return new Rectangle(location.X - (_size.Width / 2), location.Y - (_size.Height / 2), _size.Width, _size.Height); } } /// /// Return the bounds of the Adorner as displayed on the parent Surface /// - protected virtual NativeRect BoundsOnSurface + protected virtual Rectangle BoundsOnSurface { get { - NativePoint displayLocation = Owner.Parent.ToSurfaceCoordinates(Location); - return new NativeRect(displayLocation.X - Size.Width / 2, displayLocation.Y - Size.Height / 2, Size.Width, Size.Height); + Point displayLocation = Owner.Parent.ToSurfaceCoordinates(Location); + return new Rectangle(displayLocation.X - _size.Width / 2, displayLocation.Y - _size.Height / 2, _size.Width, _size.Height); } } @@ -138,41 +132,17 @@ namespace Greenshot.Editor.Drawing.Adorners /// Adjust UI elements to the supplied DPI settings /// /// uint - public void AdjustToDpi(int dpi) + public void AdjustToDpi(uint dpi) { - Size = DpiCalculator.ScaleWithDpi(DefaultSize, dpi); + _size = DpiHelper.ScaleWithDpi(DefaultSize, dpi); } - public Color OutlineColor { get; set; } = Color.White; - public Color FillColor { get; set; } = Color.Black; - /// /// Draw the adorner /// /// PaintEventArgs public virtual void Paint(PaintEventArgs paintEventArgs) { - Graphics targetGraphics = paintEventArgs.Graphics; - - var bounds = BoundsOnSurface; - GraphicsState state = targetGraphics.Save(); - - targetGraphics.CompositingMode = CompositingMode.SourceCopy; - - try - { - using var fillBrush = new SolidBrush(FillColor); - targetGraphics.FillRectangle(fillBrush, bounds); - using var lineBrush = new SolidBrush(OutlineColor); - using var pen = new Pen(lineBrush); - targetGraphics.DrawRectangle(pen, bounds); - } - catch - { - // Ignore, BUG-2065 - } - - targetGraphics.Restore(state); } /// @@ -182,10 +152,5 @@ namespace Greenshot.Editor.Drawing.Adorners public virtual void Transform(Matrix matrix) { } - - /// - /// This is to TAG the adorner so we know the type - /// - public string Tag { get; set; } } } \ No newline at end of file diff --git a/src/Greenshot.Editor/Drawing/Adorners/MoveAdorner.cs b/src/Greenshot.Editor/Drawing/Adorners/MoveAdorner.cs index 61fc8de11..ad1355ced 100644 --- a/src/Greenshot.Editor/Drawing/Adorners/MoveAdorner.cs +++ b/src/Greenshot.Editor/Drawing/Adorners/MoveAdorner.cs @@ -20,8 +20,8 @@ */ using System.Drawing; +using System.Drawing.Drawing2D; using System.Windows.Forms; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Helpers; @@ -32,8 +32,8 @@ namespace Greenshot.Editor.Drawing.Adorners /// public class MoveAdorner : AbstractAdorner { - private NativeRect _boundsBeforeResize = NativeRect.Empty; - private NativeRectFloat _boundsAfterResize = NativeRectFloat.Empty; + private Rectangle _boundsBeforeResize = Rectangle.Empty; + private RectangleF _boundsAfterResize = RectangleF.Empty; public Positions Position { get; private set; } @@ -55,7 +55,7 @@ namespace Greenshot.Editor.Drawing.Adorners public override void MouseDown(object sender, MouseEventArgs mouseEventArgs) { EditStatus = EditStatus.RESIZING; - _boundsBeforeResize = new NativeRect(Owner.Left, Owner.Top, Owner.Width, Owner.Height); + _boundsBeforeResize = new Rectangle(Owner.Left, Owner.Top, Owner.Width, Owner.Height); _boundsAfterResize = _boundsBeforeResize; } @@ -75,10 +75,13 @@ namespace Greenshot.Editor.Drawing.Adorners Owner.MakeBoundsChangeUndoable(false); // reset "workbench" rectangle to current bounds - _boundsAfterResize = _boundsBeforeResize; + _boundsAfterResize.X = _boundsBeforeResize.X; + _boundsAfterResize.Y = _boundsBeforeResize.Y; + _boundsAfterResize.Width = _boundsBeforeResize.Width; + _boundsAfterResize.Height = _boundsBeforeResize.Height; // calculate scaled rectangle - _boundsAfterResize = ScaleHelper.Scale(_boundsAfterResize, Position, new PointF(mouseEventArgs.X, mouseEventArgs.Y), ScaleHelper.GetScaleOptions()); + ScaleHelper.Scale(ref _boundsAfterResize, Position, new PointF(mouseEventArgs.X, mouseEventArgs.Y), ScaleHelper.GetScaleOptions()); // apply scaled bounds to this DrawableContainer Owner.ApplyBounds(_boundsAfterResize); @@ -89,7 +92,7 @@ namespace Greenshot.Editor.Drawing.Adorners /// /// Return the location of the adorner /// - public override NativePoint Location + public override Point Location { get { @@ -130,8 +133,34 @@ namespace Greenshot.Editor.Drawing.Adorners break; } - return new NativePoint(x, y); + return new Point(x, y); } } + + /// + /// Draw the adorner + /// + /// PaintEventArgs + public override void Paint(PaintEventArgs paintEventArgs) + { + Graphics targetGraphics = paintEventArgs.Graphics; + + var bounds = BoundsOnSurface; + GraphicsState state = targetGraphics.Save(); + + targetGraphics.CompositingMode = CompositingMode.SourceCopy; + + try + { + targetGraphics.FillRectangle(Brushes.Black, bounds); + targetGraphics.DrawRectangle(new Pen(Brushes.White), bounds); + } + catch + { + // Ignore, BUG-2065 + } + + targetGraphics.Restore(state); + } } } \ No newline at end of file diff --git a/src/Greenshot.Editor/Drawing/Adorners/ResizeAdorner.cs b/src/Greenshot.Editor/Drawing/Adorners/ResizeAdorner.cs index 263d5a31e..052abaaa4 100644 --- a/src/Greenshot.Editor/Drawing/Adorners/ResizeAdorner.cs +++ b/src/Greenshot.Editor/Drawing/Adorners/ResizeAdorner.cs @@ -20,8 +20,8 @@ */ using System.Drawing; +using System.Drawing.Drawing2D; using System.Windows.Forms; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Helpers; @@ -32,8 +32,8 @@ namespace Greenshot.Editor.Drawing.Adorners /// public class ResizeAdorner : AbstractAdorner { - private NativeRect _boundsBeforeResize = NativeRect.Empty; - private NativeRectFloat _boundsAfterResize = NativeRectFloat.Empty; + private Rectangle _boundsBeforeResize = Rectangle.Empty; + private RectangleF _boundsAfterResize = RectangleF.Empty; public Positions Position { get; private set; } @@ -55,18 +55,23 @@ namespace Greenshot.Editor.Drawing.Adorners isNotSwitched = !isNotSwitched; } - return Position switch + switch (Position) { - Positions.TopLeft => isNotSwitched ? Cursors.SizeNWSE : Cursors.SizeNESW, - Positions.BottomRight => isNotSwitched ? Cursors.SizeNWSE : Cursors.SizeNESW, - Positions.TopRight => isNotSwitched ? Cursors.SizeNESW : Cursors.SizeNWSE, - Positions.BottomLeft => isNotSwitched ? Cursors.SizeNESW : Cursors.SizeNWSE, - Positions.MiddleLeft => Cursors.SizeWE, - Positions.MiddleRight => Cursors.SizeWE, - Positions.TopCenter => Cursors.SizeNS, - Positions.BottomCenter => Cursors.SizeNS, - _ => Cursors.SizeAll - }; + case Positions.TopLeft: + case Positions.BottomRight: + return isNotSwitched ? Cursors.SizeNWSE : Cursors.SizeNESW; + case Positions.TopRight: + case Positions.BottomLeft: + return isNotSwitched ? Cursors.SizeNESW : Cursors.SizeNWSE; + case Positions.MiddleLeft: + case Positions.MiddleRight: + return Cursors.SizeWE; + case Positions.TopCenter: + case Positions.BottomCenter: + return Cursors.SizeNS; + default: + return Cursors.SizeAll; + } } } @@ -78,7 +83,7 @@ namespace Greenshot.Editor.Drawing.Adorners public override void MouseDown(object sender, MouseEventArgs mouseEventArgs) { EditStatus = EditStatus.RESIZING; - _boundsBeforeResize = new NativeRect(Owner.Left, Owner.Top, Owner.Width, Owner.Height); + _boundsBeforeResize = new Rectangle(Owner.Left, Owner.Top, Owner.Width, Owner.Height); _boundsAfterResize = _boundsBeforeResize; } @@ -98,12 +103,13 @@ namespace Greenshot.Editor.Drawing.Adorners Owner.MakeBoundsChangeUndoable(false); // reset "workbench" rectangle to current bounds - _boundsAfterResize = _boundsBeforeResize; - - var scaleOptions = (Owner as IHaveScaleOptions)?.GetScaleOptions(); + _boundsAfterResize.X = _boundsBeforeResize.X; + _boundsAfterResize.Y = _boundsBeforeResize.Y; + _boundsAfterResize.Width = _boundsBeforeResize.Width; + _boundsAfterResize.Height = _boundsBeforeResize.Height; // calculate scaled rectangle - _boundsAfterResize = ScaleHelper.Scale(_boundsAfterResize, Position, new NativePointFloat(mouseEventArgs.X, mouseEventArgs.Y), scaleOptions); + ScaleHelper.Scale(ref _boundsAfterResize, Position, new PointF(mouseEventArgs.X, mouseEventArgs.Y), ScaleHelper.GetScaleOptions()); // apply scaled bounds to this DrawableContainer Owner.ApplyBounds(_boundsAfterResize); @@ -114,7 +120,7 @@ namespace Greenshot.Editor.Drawing.Adorners /// /// Return the location of the adorner /// - public override NativePoint Location + public override Point Location { get { @@ -158,5 +164,23 @@ namespace Greenshot.Editor.Drawing.Adorners return new Point(x, y); } } + + /// + /// Draw the adorner + /// + /// PaintEventArgs + public override void Paint(PaintEventArgs paintEventArgs) + { + Graphics targetGraphics = paintEventArgs.Graphics; + + var bounds = BoundsOnSurface; + GraphicsState state = targetGraphics.Save(); + + targetGraphics.CompositingMode = CompositingMode.SourceCopy; + + targetGraphics.FillRectangle(Brushes.Black, bounds); + targetGraphics.DrawRectangle(new Pen(Brushes.White), bounds); + targetGraphics.Restore(state); + } } } \ No newline at end of file diff --git a/src/Greenshot.Editor/Drawing/Adorners/TargetAdorner.cs b/src/Greenshot.Editor/Drawing/Adorners/TargetAdorner.cs index 864ff28c5..0dec40a5f 100644 --- a/src/Greenshot.Editor/Drawing/Adorners/TargetAdorner.cs +++ b/src/Greenshot.Editor/Drawing/Adorners/TargetAdorner.cs @@ -22,29 +22,25 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Interfaces.Drawing; namespace Greenshot.Editor.Drawing.Adorners { /// - /// This implements the special target "gripper", e.g. used for the Speech-Bubble tail + /// This implements the special "gripper" for the Speech-Bubble tail /// - public sealed class TargetAdorner : AbstractAdorner + public class TargetAdorner : AbstractAdorner { - public TargetAdorner(IDrawableContainer owner, Point location, Color? fillColor = null, Color? outlineColor = null) : base(owner) + public TargetAdorner(IDrawableContainer owner, Point location) : base(owner) { Location = location; - FillColor = fillColor ?? Color.Green; - OutlineColor = outlineColor ?? Color.White; } /// /// Handle the mouse down /// - /// object - /// MouseEventArgs + /// + /// public override void MouseDown(object sender, MouseEventArgs mouseEventArgs) { EditStatus = EditStatus.MOVING; @@ -53,8 +49,8 @@ namespace Greenshot.Editor.Drawing.Adorners /// /// Handle the mouse move /// - /// object - /// MouseEventArgs + /// + /// public override void MouseMove(object sender, MouseEventArgs mouseEventArgs) { if (EditStatus != EditStatus.MOVING) @@ -63,30 +59,30 @@ namespace Greenshot.Editor.Drawing.Adorners } Owner.Invalidate(); - NativePoint newGripperLocation = new NativePoint(mouseEventArgs.X, mouseEventArgs.Y); - NativeRect imageBounds = new NativeRect(0, 0, Owner.Parent.Image.Width, Owner.Parent.Image.Height); + Point newGripperLocation = new Point(mouseEventArgs.X, mouseEventArgs.Y); + Rectangle imageBounds = new Rectangle(0, 0, Owner.Parent.Image.Width, Owner.Parent.Image.Height); // Check if gripper inside the parent (surface), if not we need to move it inside // This was made for BUG-1682 if (!imageBounds.Contains(newGripperLocation)) { if (newGripperLocation.X > imageBounds.Right) { - newGripperLocation = newGripperLocation.ChangeX(imageBounds.Right - 5); + newGripperLocation.X = imageBounds.Right - 5; } if (newGripperLocation.X < imageBounds.Left) { - newGripperLocation = newGripperLocation.ChangeX(imageBounds.Left); + newGripperLocation.X = imageBounds.Left; } if (newGripperLocation.Y > imageBounds.Bottom) { - newGripperLocation = newGripperLocation.ChangeY(imageBounds.Bottom - 5); + newGripperLocation.Y = imageBounds.Bottom - 5; } if (newGripperLocation.Y < imageBounds.Top) { - newGripperLocation = newGripperLocation.ChangeY(imageBounds.Top); + newGripperLocation.Y = imageBounds.Top; } } @@ -94,6 +90,19 @@ namespace Greenshot.Editor.Drawing.Adorners Owner.Invalidate(); } + /// + /// Draw the adorner + /// + /// PaintEventArgs + public override void Paint(PaintEventArgs paintEventArgs) + { + Graphics targetGraphics = paintEventArgs.Graphics; + + var bounds = BoundsOnSurface; + targetGraphics.FillRectangle(Brushes.Green, bounds); + targetGraphics.DrawRectangle(new Pen(Brushes.White), bounds); + } + /// /// Made sure this adorner is transformed /// @@ -105,7 +114,7 @@ namespace Greenshot.Editor.Drawing.Adorners return; } - Point[] points = new Point[] + Point[] points = new[] { Location }; diff --git a/src/Greenshot.Editor/Drawing/ArrowContainer.cs b/src/Greenshot.Editor/Drawing/ArrowContainer.cs index 91c76366b..f285d28bc 100644 --- a/src/Greenshot.Editor/Drawing/ArrowContainer.cs +++ b/src/Greenshot.Editor/Drawing/ArrowContainer.cs @@ -22,9 +22,6 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; -using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; @@ -46,7 +43,7 @@ namespace Greenshot.Editor.Drawing private static readonly AdjustableArrowCap ARROW_CAP = new AdjustableArrowCap(4, 6); - public ArrowContainer(ISurface parent) : base(parent) + public ArrowContainer(Surface parent) : base(parent) { } @@ -68,40 +65,44 @@ namespace Greenshot.Editor.Drawing int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - if (lineThickness <= 0) return; - - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.None; - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - ArrowHeadCombination heads = (ArrowHeadCombination) GetFieldValue(FieldType.ARROWHEADS); - if (shadow) + if (lineThickness > 0) { - //draw shadow first - int basealpha = 100; - int alpha = basealpha; - int steps = 5; - int currentStep = 1; - while (currentStep <= steps) + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.None; + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + ArrowHeadCombination heads = (ArrowHeadCombination) GetFieldValue(FieldType.ARROWHEADS); + if (lineThickness > 0) { - using Pen shadowCapPen = new Pen(Color.FromArgb(alpha, 100, 100, 100), lineThickness); - SetArrowHeads(heads, shadowCapPen); + if (shadow) + { + //draw shadow first + int basealpha = 100; + int alpha = basealpha; + int steps = 5; + int currentStep = 1; + while (currentStep <= steps) + { + using Pen shadowCapPen = new Pen(Color.FromArgb(alpha, 100, 100, 100), lineThickness); + SetArrowHeads(heads, shadowCapPen); - graphics.DrawLine(shadowCapPen, - Left + currentStep, - Top + currentStep, - Left + currentStep + Width, - Top + currentStep + Height); + graphics.DrawLine(shadowCapPen, + Left + currentStep, + Top + currentStep, + Left + currentStep + Width, + Top + currentStep + Height); - currentStep++; - alpha -= basealpha / steps; + currentStep++; + alpha -= basealpha / steps; + } + } + + using Pen pen = new Pen(lineColor, lineThickness); + SetArrowHeads(heads, pen); + graphics.DrawLine(pen, Left, Top, Left + Width, Top + Height); } } - - using Pen pen = new Pen(lineColor, lineThickness); - SetArrowHeads(heads, pen); - graphics.DrawLine(pen, Left, Top, Left + Width, Top + Height); } private void SetArrowHeads(ArrowHeadCombination heads, Pen pen) @@ -117,7 +118,7 @@ namespace Greenshot.Editor.Drawing } } - public override NativeRect DrawingBounds + public override Rectangle DrawingBounds { get { @@ -132,11 +133,12 @@ namespace Greenshot.Editor.Drawing using GraphicsPath path = new GraphicsPath(); path.AddLine(Left, Top, Left + Width, Top + Height); using Matrix matrix = new Matrix(); - NativeRect drawingBounds = Rectangle.Round(path.GetBounds(matrix, pen)); - return drawingBounds.Inflate(2, 2); + Rectangle drawingBounds = Rectangle.Round(path.GetBounds(matrix, pen)); + drawingBounds.Inflate(2, 2); + return drawingBounds; } - return NativeRect.Empty; + return Rectangle.Empty; } } diff --git a/src/Greenshot.Editor/Drawing/CropContainer.cs b/src/Greenshot.Editor/Drawing/CropContainer.cs index eb2f7179e..1aa4e06c2 100644 --- a/src/Greenshot.Editor/Drawing/CropContainer.cs +++ b/src/Greenshot.Editor/Drawing/CropContainer.cs @@ -19,14 +19,9 @@ * along with this program. If not, see . */ - using System.Drawing; using System.Runtime.Serialization; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; -using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Editor.Drawing.Adorners; using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Helpers; @@ -37,30 +32,7 @@ namespace Greenshot.Editor.Drawing /// public class CropContainer : DrawableContainer { - /// - /// Available Crop modes - /// - public enum CropModes - { - /// - /// crop all outside the selection rectangle - /// - Default, - /// - /// like default, but initially creates the selection rectangle - /// - AutoCrop, - /// - /// crop all inside the selection, anchors the selection to the top and bottom edges - /// - Vertical, - /// - /// crop all inside the selection, anchors the selection to the left and right edges - /// - Horizontal - } - - public CropContainer(ISurface parent) : base(parent) + public CropContainer(Surface parent) : base(parent) { Init(); } @@ -73,65 +45,12 @@ namespace Greenshot.Editor.Drawing private void Init() { - switch (GetFieldValue(FieldType.CROPMODE)) - { - case CropModes.Horizontal: - { - InitHorizontalCropOutStyle(); - break; - } - case CropModes.Vertical: - { - InitVerticalCropOutStyle(); - break; - } - default: - { - CreateDefaultAdorners(); - break; - } - } - } - - private void InitHorizontalCropOutStyle() - { - const int defaultHeight = 25; - - if (_parent?.Image is { } image) - { - Size = new Size(image.Width, defaultHeight); - } - CreateTopBottomAdorners(); - } - - private void InitVerticalCropOutStyle() - { - const int defaultWidth = 25; - - if (_parent?.Image is { } image) - { - Size = new Size(defaultWidth, image.Height); - } - - CreateLeftRightAdorners(); - } - - private void CreateTopBottomAdorners() - { - Adorners.Add(new ResizeAdorner(this, Positions.TopCenter)); - Adorners.Add(new ResizeAdorner(this, Positions.BottomCenter)); - } - - private void CreateLeftRightAdorners() - { - Adorners.Add(new ResizeAdorner(this, Positions.MiddleLeft)); - Adorners.Add(new ResizeAdorner(this, Positions.MiddleRight)); + CreateDefaultAdorners(); } protected override void InitializeFields() { AddField(GetType(), FieldType.FLAGS, FieldFlag.CONFIRMABLE); - AddField(GetType(), FieldType.CROPMODE, CropModes.Default); } public override void Invalidate() @@ -143,16 +62,16 @@ namespace Greenshot.Editor.Drawing /// We need to override the DrawingBound, return a rectangle in the size of the image, to make sure this element is always draw /// (we create a transparent brown over the complete picture) /// - public override NativeRect DrawingBounds + public override Rectangle DrawingBounds { get { if (_parent?.Image is { } image) { - return new NativeRect(0, 0, image.Width, image.Height); + return new Rectangle(0, 0, image.Width, image.Height); } - return NativeRect.Empty; + return Rectangle.Empty; } } @@ -163,114 +82,27 @@ namespace Greenshot.Editor.Drawing return; } - using Brush cropBrush = new SolidBrush(Color.FromArgb(100, 150, 150, 100)); - var cropRectangle = new NativeRect(Left, Top, Width, Height).Normalize(); - var selectionRect = new NativeRect(cropRectangle.Left - 1, cropRectangle.Top - 1, cropRectangle.Width + 1, cropRectangle.Height + 1); + Rectangle cropRectangle = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + Rectangle selectionRect = new Rectangle(cropRectangle.Left - 1, cropRectangle.Top - 1, cropRectangle.Width + 1, cropRectangle.Height + 1); Size imageSize = _parent.Image.Size; DrawSelectionBorder(g, selectionRect); - switch (GetFieldValue(FieldType.CROPMODE)) - { - case CropModes.Horizontal: - case CropModes.Vertical: - { - //draw inside - g.FillRectangle(cropBrush, cropRectangle); - break; - } - default: - { - //draw outside - // top - g.FillRectangle(cropBrush, new Rectangle(0, 0, imageSize.Width, cropRectangle.Top)); - // left - g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top, cropRectangle.Left, cropRectangle.Height)); - // right - g.FillRectangle(cropBrush, new Rectangle(cropRectangle.Left + cropRectangle.Width, cropRectangle.Top, imageSize.Width - (cropRectangle.Left + cropRectangle.Width), cropRectangle.Height)); - // bottom - g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top + cropRectangle.Height, imageSize.Width, imageSize.Height - (cropRectangle.Top + cropRectangle.Height))); - break; - } - } - - + // top + g.FillRectangle(cropBrush, new Rectangle(0, 0, imageSize.Width, cropRectangle.Top)); + // left + g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top, cropRectangle.Left, cropRectangle.Height)); + // right + g.FillRectangle(cropBrush, + new Rectangle(cropRectangle.Left + cropRectangle.Width, cropRectangle.Top, imageSize.Width - (cropRectangle.Left + cropRectangle.Width), cropRectangle.Height)); + // bottom + g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top + cropRectangle.Height, imageSize.Width, imageSize.Height - (cropRectangle.Top + cropRectangle.Height))); } /// /// No context menu for the CropContainer /// public override bool HasContextMenu => false; - - public override bool HandleMouseDown(int x, int y) - { - return GetFieldValue(FieldType.CROPMODE) switch - { - //force horizontal crop to left edge - CropModes.Horizontal => base.HandleMouseDown(0, y), - //force vertical crop to top edge - CropModes.Vertical => base.HandleMouseDown(x, 0), - _ => base.HandleMouseDown(x, y), - }; - } - - public override bool HandleMouseMove(int x, int y) - { - Invalidate(); - - switch (GetFieldValue(FieldType.CROPMODE)) - { - case CropModes.Horizontal: - { - //stick on left and right - //allow only horizontal changes - if (_parent?.Image is { } image) - { - _boundsAfterResize = new NativeRectFloat(0, _boundsBeforeResize.Top, image.Width, y - _boundsAfterResize.Top); - } - break; - } - case CropModes.Vertical: - { - //stick on top and bottom - //allow only vertical changes - if (_parent?.Image is { } image) - { - _boundsAfterResize = new NativeRectFloat(_boundsBeforeResize.Left, 0, x - _boundsAfterResize.Left, image.Height); - } - break; - } - default: - { - // reset "workbench" rectangle to current bounds - _boundsAfterResize = new NativeRectFloat( - _boundsBeforeResize.Left, _boundsBeforeResize.Top, - x - _boundsAfterResize.Left, y - _boundsAfterResize.Top); - - _boundsAfterResize = ScaleHelper.Scale(_boundsAfterResize, x, y, GetAngleRoundProcessor()); - break; - } - } - - // apply scaled bounds to this DrawableContainer - ApplyBounds(_boundsAfterResize); - - Invalidate(); - return true; - } - /// - /// - /// - /// Make sure this container is not undoable - /// - public override bool IsUndoable => false; - - /// - /// - /// - /// See dedicated confirm method - /// - public override bool IsConfirmable => true; } -} +} \ No newline at end of file diff --git a/src/Greenshot.Editor/Drawing/CursorContainer.cs b/src/Greenshot.Editor/Drawing/CursorContainer.cs index e8914eb75..d2e19adba 100644 --- a/src/Greenshot.Editor/Drawing/CursorContainer.cs +++ b/src/Greenshot.Editor/Drawing/CursorContainer.cs @@ -25,8 +25,6 @@ using System.Drawing.Drawing2D; using System.IO; using System.Runtime.Serialization; using System.Windows.Forms; -using Dapplo.Windows.Common.Structs; -using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using log4net; @@ -42,7 +40,7 @@ namespace Greenshot.Editor.Drawing protected Cursor cursor; - public CursorContainer(ISurface parent) : base(parent) + public CursorContainer(Surface parent) : base(parent) { Init(); } @@ -58,7 +56,7 @@ namespace Greenshot.Editor.Drawing CreateDefaultAdorners(); } - public CursorContainer(ISurface parent, string filename) : this(parent) + public CursorContainer(Surface parent, string filename) : this(parent) { Load(filename); } @@ -125,6 +123,6 @@ namespace Greenshot.Editor.Drawing cursor.DrawStretched(graphics, Bounds); } - public override NativeSize DefaultSize => cursor?.Size ?? new NativeSize(16, 16); + public override Size DefaultSize => cursor?.Size ?? new Size(16, 16); } } \ No newline at end of file diff --git a/src/Greenshot.Editor/Drawing/DrawableContainer.cs b/src/Greenshot.Editor/Drawing/DrawableContainer.cs index cac23dc3a..029a48d43 100644 --- a/src/Greenshot.Editor/Drawing/DrawableContainer.cs +++ b/src/Greenshot.Editor/Drawing/DrawableContainer.cs @@ -26,9 +26,6 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.Linq; using System.Runtime.Serialization; -using System.Windows.Forms; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; @@ -129,17 +126,12 @@ namespace Greenshot.Editor.Drawing } } - [NonSerialized] internal ISurface _parent; + [NonSerialized] internal Surface _parent; public ISurface Parent { get => _parent; - set => SwitchParent(value); - } - - protected Surface InternalParent - { - get => (Surface)_parent; + set => SwitchParent((Surface) value); } [NonSerialized] private TargetAdorner _targetAdorner; @@ -230,9 +222,9 @@ namespace Greenshot.Editor.Drawing } } - public NativePoint Location + public Point Location { - get => new NativePoint(left, top); + get => new Point(left, top); set { left = value.X; @@ -240,9 +232,9 @@ namespace Greenshot.Editor.Drawing } } - public NativeSize Size + public Size Size { - get => new NativeSize(width, height); + get => new Size(width, height); set { width = value.Width; @@ -259,15 +251,15 @@ namespace Greenshot.Editor.Drawing [NonSerialized] // will store current bounds of this DrawableContainer before starting a resize - protected NativeRect _boundsBeforeResize = NativeRect.Empty; + protected Rectangle _boundsBeforeResize = Rectangle.Empty; [NonSerialized] // "workbench" rectangle - used for calculating bounds during resizing (to be applied to this DrawableContainer afterwards) - protected NativeRectFloat _boundsAfterResize = NativeRectFloat.Empty; + protected RectangleF _boundsAfterResize = RectangleF.Empty; - public NativeRect Bounds + public Rectangle Bounds { - get => new NativeRect(Left, Top, Width, Height).Normalize(); + get => GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); set { Left = Round(value.Left); @@ -277,7 +269,7 @@ namespace Greenshot.Editor.Drawing } } - public virtual void ApplyBounds(NativeRectFloat newBounds) + public virtual void ApplyBounds(RectangleF newBounds) { Left = Round(newBounds.Left); Top = Round(newBounds.Top); @@ -285,7 +277,7 @@ namespace Greenshot.Editor.Drawing Height = Round(newBounds.Height); } - public DrawableContainer(ISurface parent) + public DrawableContainer(Surface parent) { InitializeFields(); _parent = parent; @@ -310,7 +302,7 @@ namespace Greenshot.Editor.Drawing private bool accountForShadowChange; - public virtual NativeRect DrawingBounds + public virtual Rectangle DrawingBounds { get { @@ -318,7 +310,7 @@ namespace Greenshot.Editor.Drawing { if (filter.Invert) { - return new NativeRect(Point.Empty, _parent.Image.Size); + return new Rectangle(Point.Empty, _parent.Image.Size); } } @@ -342,7 +334,7 @@ namespace Greenshot.Editor.Drawing shadow += 10; } - return new NativeRect(Bounds.Left - offset, Bounds.Top - offset, Bounds.Width + lineThickness + shadow, Bounds.Height + lineThickness + shadow); + return new Rectangle(Bounds.Left - offset, Bounds.Top - offset, Bounds.Width + lineThickness + shadow, Bounds.Height + lineThickness + shadow); } } @@ -366,8 +358,7 @@ namespace Greenshot.Editor.Drawing /// /// Initialize a target gripper /// - /// NativePoint - protected void InitTargetAdorner(NativePoint location) + protected void InitAdorner(Color gripperColor, Point location) { _targetAdorner = new TargetAdorner(this, location); Adorners.Add(_targetAdorner); @@ -398,7 +389,7 @@ namespace Greenshot.Editor.Drawing public abstract void Draw(Graphics graphics, RenderMode renderMode); - public virtual void DrawContent(Graphics graphics, Bitmap bmp, RenderMode renderMode, NativeRect clipRectangle) + public virtual void DrawContent(Graphics graphics, Bitmap bmp, RenderMode renderMode, Rectangle clipRectangle) { if (Children.Count > 0) { @@ -418,7 +409,8 @@ namespace Greenshot.Editor.Drawing } else { - var drawingRect = new NativeRect(Bounds.Location, Bounds.Size).Intersect(clipRectangle); + Rectangle drawingRect = new Rectangle(Bounds.Location, Bounds.Size); + drawingRect.Intersect(clipRectangle); if (filter is MagnifierFilter) { // quick&dirty bugfix, because MagnifierFilter behaves differently when drawn only partially @@ -442,7 +434,7 @@ namespace Greenshot.Editor.Drawing /// Adjust UI elements to the supplied DPI settings /// /// uint with dpi value - public void AdjustToDpi(int dpi) + public void AdjustToDpi(uint dpi) { foreach (var adorner in Adorners) { @@ -450,12 +442,6 @@ namespace Greenshot.Editor.Drawing } } - /// - public virtual void AddContextMenuItems(ContextMenuStrip menu, ISurface surface, MouseEventArgs mouseEventArgs) - { - // Empty as we do not want to add something to the context menu for every element - } - public virtual bool Contains(int x, int y) { return Bounds.Contains(x, y); @@ -463,12 +449,12 @@ namespace Greenshot.Editor.Drawing public virtual bool ClickableAt(int x, int y) { - var r = new NativeRect(Left, Top, Width, Height).Normalize(); - r = r.Inflate(5, 5); + Rectangle r = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + r.Inflate(5, 5); return r.Contains(x, y); } - protected void DrawSelectionBorder(Graphics g, NativeRect rect) + protected void DrawSelectionBorder(Graphics g, Rectangle rect) { using Pen pen = new Pen(Color.MediumSeaGreen) { @@ -481,23 +467,13 @@ namespace Greenshot.Editor.Drawing g.DrawRectangle(pen, rect); } - /// - public virtual bool IsUndoable => true; - - /// - public virtual bool IsConfirmable => false; - /// - /// Make a following bounds change on this DrawableContainer undoable! + /// Make a following bounds change on this drawablecontainer undoable! /// /// true means allow the moves to be merged - public virtual void MakeBoundsChangeUndoable(bool allowMerge) + public void MakeBoundsChangeUndoable(bool allowMerge) { - if (!IsUndoable) - { - return; - } - _parent?.MakeUndoable(new DrawableContainerBoundsChangeMemento(this), allowMerge); + _parent.MakeUndoable(new DrawableContainerBoundsChangeMemento(this), allowMerge); } public void MoveBy(int dx, int dy) @@ -514,9 +490,8 @@ namespace Greenshot.Editor.Drawing /// true if the event is handled, false if the surface needs to handle it public virtual bool HandleMouseDown(int x, int y) { - _boundsBeforeResize = Bounds.MoveTo(x, y); - Left = x; - Top = y; + Left = _boundsBeforeResize.X = x; + Top = _boundsBeforeResize.Y = y; return true; } @@ -530,11 +505,13 @@ namespace Greenshot.Editor.Drawing { Invalidate(); - // reset "workbench" rectangle to current bounds - _boundsAfterResize = new NativeRectFloat(_boundsBeforeResize.Left, _boundsBeforeResize.Top, x - _boundsAfterResize.Left, y - _boundsAfterResize.Top); + // reset "workrbench" rectangle to current bounds + _boundsAfterResize.X = _boundsBeforeResize.Left; + _boundsAfterResize.Y = _boundsBeforeResize.Top; + _boundsAfterResize.Width = x - _boundsAfterResize.Left; + _boundsAfterResize.Height = y - _boundsAfterResize.Top; - var scaleOptions = (this as IHaveScaleOptions)?.GetScaleOptions(); - _boundsAfterResize = ScaleHelper.Scale(_boundsAfterResize, x, y, GetAngleRoundProcessor(), scaleOptions); + ScaleHelper.Scale(_boundsBeforeResize, x, y, ref _boundsAfterResize, GetAngleRoundProcessor()); // apply scaled bounds to this DrawableContainer ApplyBounds(_boundsAfterResize); @@ -552,7 +529,7 @@ namespace Greenshot.Editor.Drawing { } - protected virtual void SwitchParent(ISurface newParent) + protected virtual void SwitchParent(Surface newParent) { if (newParent == Parent) { @@ -570,10 +547,11 @@ namespace Greenshot.Editor.Drawing protected void OnPropertyChanged(string propertyName) { - if (_propertyChanged == null) return; - - _propertyChanged(this, new PropertyChangedEventArgs(propertyName)); - Invalidate(); + if (_propertyChanged != null) + { + _propertyChanged(this, new PropertyChangedEventArgs(propertyName)); + Invalidate(); + } } /// @@ -584,10 +562,7 @@ namespace Greenshot.Editor.Drawing /// The new value public virtual void BeforeFieldChange(IField fieldToBeChanged, object newValue) { - if (IsUndoable) - { - _parent?.MakeUndoable(new ChangeFieldHolderMemento(this, fieldToBeChanged), true); - } + _parent?.MakeUndoable(new ChangeFieldHolderMemento(this, fieldToBeChanged), true); Invalidate(); } @@ -667,16 +642,16 @@ namespace Greenshot.Editor.Drawing Height = points[1].Y - points[0].Y; } - protected virtual IDoubleProcessor GetAngleRoundProcessor() + protected virtual ScaleHelper.IDoubleProcessor GetAngleRoundProcessor() { - return ShapeAngleRoundBehavior.INSTANCE; + return ScaleHelper.ShapeAngleRoundBehavior.Instance; } public virtual bool HasContextMenu => true; public virtual bool HasDefaultSize => false; - public virtual NativeSize DefaultSize => throw new NotSupportedException("Object doesn't have a default size"); + public virtual Size DefaultSize => throw new NotSupportedException("Object doesn't have a default size"); /// /// Allows to override the initializing of the fields, so we can actually have our own defaults diff --git a/src/Greenshot.Editor/Drawing/DrawableContainerList.cs b/src/Greenshot.Editor/Drawing/DrawableContainerList.cs index ef30c037f..e89b78b70 100644 --- a/src/Greenshot.Editor/Drawing/DrawableContainerList.cs +++ b/src/Greenshot.Editor/Drawing/DrawableContainerList.cs @@ -24,11 +24,8 @@ using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; -using System.Linq; using System.Threading; using System.Windows.Forms; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Core; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; @@ -53,11 +50,6 @@ namespace Greenshot.Editor.Drawing { } - public DrawableContainerList(IEnumerable elements) - { - AddRange(elements); - } - public DrawableContainerList(Guid parentId) { ParentID = parentId; @@ -138,21 +130,17 @@ namespace Greenshot.Editor.Drawing } /// - /// Make a following bounds change on this DrawableContainerList undoable! + /// Make a following bounds change on this containerlist undoable! /// /// true means allow the moves to be merged public void MakeBoundsChangeUndoable(bool allowMerge) { - if (Count <= 0 || Parent == null) return; - // Take all containers to make undoable - var containersToClone = this.Where(c => c.IsUndoable).ToList(); - if (!containersToClone.Any()) + if (Count > 0 && Parent != null) { - return; + var clone = new DrawableContainerList(); + clone.AddRange(this); + Parent.MakeUndoable(new DrawableContainerBoundsChangeMemento(clone), allowMerge); } - var clone = new DrawableContainerList(); - clone.AddRange(containersToClone); - Parent.MakeUndoable(new DrawableContainerBoundsChangeMemento(clone), allowMerge); } /// @@ -260,7 +248,7 @@ namespace Greenshot.Editor.Drawing /// /// /// true if an filter intersects - public bool HasIntersectingFilters(NativeRect clipRectangle) + public bool HasIntersectingFilters(Rectangle clipRectangle) { foreach (var dc in this) { @@ -276,9 +264,9 @@ namespace Greenshot.Editor.Drawing /// /// Check if any of the drawableContainers are inside the rectangle /// - /// NativeRect + /// /// - public bool IntersectsWith(NativeRect clipRectangle) + public bool IntersectsWith(Rectangle clipRectangle) { foreach (var dc in this) { @@ -295,22 +283,24 @@ namespace Greenshot.Editor.Drawing /// A rectangle containing DrawingBounds of all drawableContainers in this list, /// or empty rectangle if nothing is there. /// - public NativeRect DrawingBounds + public Rectangle DrawingBounds { get { if (Count == 0) { - return NativeRect.Empty; + return Rectangle.Empty; } - - var result = this[0].DrawingBounds; - for (int i = 1; i < Count; i++) + else { - result = result.Union(this[i].DrawingBounds); - } + var result = this[0].DrawingBounds; + for (int i = 1; i < Count; i++) + { + result = Rectangle.Union(result, this[i].DrawingBounds); + } - return result; + return result; + } } } @@ -319,9 +309,9 @@ namespace Greenshot.Editor.Drawing /// /// the to the bitmap related Graphics object /// Bitmap to draw - /// the RenderMode in which the element is to be drawn - /// NativeRect - public void Draw(Graphics g, Bitmap bitmap, RenderMode renderMode, NativeRect clipRectangle) + /// the rendermode in which the element is to be drawn + /// + public void Draw(Graphics g, Bitmap bitmap, RenderMode renderMode, Rectangle clipRectangle) { if (Parent == null) { @@ -367,10 +357,10 @@ namespace Greenshot.Editor.Drawing return; } - NativeRect region = NativeRect.Empty; + Rectangle region = Rectangle.Empty; foreach (var dc in this) { - region = region.Union(dc.DrawingBounds); + region = Rectangle.Union(region, dc.DrawingBounds); } Parent.InvalidateElements(region); @@ -584,17 +574,18 @@ namespace Greenshot.Editor.Drawing return; } - (this[index1], this[index2]) = (this[index2], this[index1]); + var dc = this[index1]; + this[index1] = this[index2]; + this[index2] = dc; Parent.Modified = true; } /// /// Add items to a context menu for the selected item /// - /// ContextMenuStrip - /// ISurface - /// MouseEventArgs - public virtual void AddContextMenuItems(ContextMenuStrip menu, ISurface surface, MouseEventArgs mouseEventArgs) + /// + /// + public virtual void AddContextMenuItems(ContextMenuStrip menu, ISurface surface) { bool push = surface.Elements.CanPushDown(this); bool pull = surface.Elements.CanPullUp(this); @@ -681,7 +672,15 @@ namespace Greenshot.Editor.Drawing menu.Items.Add(item); // Reset - bool canReset = this.Cast().Any(container => container.HasDefaultSize); + bool canReset = false; + foreach (var drawableContainer in this) + { + var container = (DrawableContainer) drawableContainer; + if (container.HasDefaultSize) + { + canReset = true; + } + } if (canReset) { @@ -708,40 +707,48 @@ namespace Greenshot.Editor.Drawing }; menu.Items.Add(item); } - - // "ask" the containers to add to the context menu - foreach (var surfaceElement in surface.Elements) - { - surfaceElement.AddContextMenuItems(menu, surface, mouseEventArgs); - } } - public virtual void ShowContextMenu(MouseEventArgs mouseEventArgs, ISurface iSurface) + public virtual void ShowContextMenu(MouseEventArgs e, ISurface iSurface) { if (iSurface is not Surface surface) { return; } - bool hasMenu = this.Cast().Any(container => container.HasContextMenu); - - if (!hasMenu) return; - - ContextMenuStrip menu = new ContextMenuStrip(); - AddContextMenuItems(menu, surface, mouseEventArgs); - if (menu.Items.Count <= 0) return; - menu.Show(surface, surface.ToSurfaceCoordinates(mouseEventArgs.Location)); - while (true) + bool hasMenu = false; + foreach (var drawableContainer in this) { - if (menu.Visible) + var container = (DrawableContainer) drawableContainer; + if (!container.HasContextMenu) { - Application.DoEvents(); - Thread.Sleep(100); + continue; } - else + + hasMenu = true; + break; + } + + if (hasMenu) + { + ContextMenuStrip menu = new ContextMenuStrip(); + AddContextMenuItems(menu, surface); + if (menu.Items.Count > 0) { - menu.Dispose(); - break; + menu.Show(surface, surface.ToSurfaceCoordinates(e.Location)); + while (true) + { + if (menu.Visible) + { + Application.DoEvents(); + Thread.Sleep(100); + } + else + { + menu.Dispose(); + break; + } + } } } } @@ -750,16 +757,18 @@ namespace Greenshot.Editor.Drawing protected virtual void Dispose(bool disposing) { - if (_disposedValue) return; - if (disposing) + if (!_disposedValue) { - foreach (var drawableContainer in this) + if (disposing) { - drawableContainer.Dispose(); + foreach (var drawableContainer in this) + { + drawableContainer.Dispose(); + } } - } - _disposedValue = true; + _disposedValue = true; + } } // This code added to correctly implement the disposable pattern. @@ -772,8 +781,8 @@ namespace Greenshot.Editor.Drawing /// /// Adjust UI elements to the supplied DPI settings /// - /// int - public void AdjustToDpi(int dpi) + /// + public void AdjustToDpi(uint dpi) { foreach (var drawableContainer in this) { diff --git a/src/Greenshot.Editor/Drawing/EllipseContainer.cs b/src/Greenshot.Editor/Drawing/EllipseContainer.cs index c421515d9..f7799ac71 100644 --- a/src/Greenshot.Editor/Drawing/EllipseContainer.cs +++ b/src/Greenshot.Editor/Drawing/EllipseContainer.cs @@ -23,9 +23,6 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Runtime.Serialization; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; -using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Helpers; @@ -38,7 +35,7 @@ namespace Greenshot.Editor.Drawing [Serializable()] public class EllipseContainer : DrawableContainer { - public EllipseContainer(ISurface parent) : base(parent) + public EllipseContainer(Surface parent) : base(parent) { Init(); } @@ -73,7 +70,7 @@ namespace Greenshot.Editor.Drawing Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - var rect = new NativeRect(Left, Top, Width, Height).Normalize(); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); DrawEllipse(rect, graphics, renderMode, lineThickness, lineColor, fillColor, shadow); } @@ -87,7 +84,7 @@ namespace Greenshot.Editor.Drawing /// /// /// - public static void DrawEllipse(NativeRect rect, Graphics graphics, RenderMode renderMode, int lineThickness, Color lineColor, Color fillColor, bool shadow) + public static void DrawEllipse(Rectangle rect, Graphics graphics, RenderMode renderMode, int lineThickness, Color lineColor, Color fillColor, bool shadow) { bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); // draw shadow before anything else @@ -103,7 +100,7 @@ namespace Greenshot.Editor.Drawing { Width = lineVisible ? lineThickness : 1 }; - var shadowRect = new NativeRect(rect.Left + currentStep, rect.Top + currentStep, rect.Width, rect.Height).Normalize(); + Rectangle shadowRect = GuiRectangle.GetGuiRectangle(rect.Left + currentStep, rect.Top + currentStep, rect.Width, rect.Height); graphics.DrawEllipse(shadowPen, shadowRect); currentStep++; alpha -= basealpha / steps; @@ -148,11 +145,11 @@ namespace Greenshot.Editor.Drawing { int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 10; Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); - var rect = new NativeRect(Left, Top, Width, Height).Normalize(); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); return EllipseClickableAt(rect, lineThickness, fillColor, x, y); } - public static bool EllipseClickableAt(NativeRect rect, int lineThickness, Color fillColor, int x, int y) + public static bool EllipseClickableAt(Rectangle rect, int lineThickness, Color fillColor, int x, int y) { // If we clicked inside the rectangle and it's visible we are clickable at. if (!Color.Transparent.Equals(fillColor)) diff --git a/src/Greenshot.Editor/Drawing/Fields/FieldAggregator.cs b/src/Greenshot.Editor/Drawing/Fields/FieldAggregator.cs index 96a19b7b9..2df16d46f 100644 --- a/src/Greenshot.Editor/Drawing/Fields/FieldAggregator.cs +++ b/src/Greenshot.Editor/Drawing/Fields/FieldAggregator.cs @@ -42,7 +42,7 @@ namespace Greenshot.Editor.Drawing.Fields /// If the property values of the selected elements differ, the value of the last bound element wins. /// [Serializable] - public sealed class FieldAggregator : AbstractFieldHolder, IFieldAggregator + public sealed class FieldAggregator : AbstractFieldHolder { private readonly IDrawableContainerList _boundContainers; private bool _internalUpdateRunning; @@ -117,10 +117,11 @@ namespace Greenshot.Editor.Drawing.Fields public void UnbindElement(IDrawableContainer dc) { - if (!_boundContainers.Contains(dc)) return; - - _boundContainers.Remove(dc); - UpdateFromBoundElements(); + if (_boundContainers.Contains(dc)) + { + _boundContainers.Remove(dc); + UpdateFromBoundElements(); + } } public void Clear() diff --git a/src/Greenshot.Editor/Drawing/Fields/FieldType.cs b/src/Greenshot.Editor/Drawing/Fields/FieldType.cs index eead7452a..c3b732f4b 100644 --- a/src/Greenshot.Editor/Drawing/Fields/FieldType.cs +++ b/src/Greenshot.Editor/Drawing/Fields/FieldType.cs @@ -31,33 +31,31 @@ namespace Greenshot.Editor.Drawing.Fields [Serializable] public class FieldType : IFieldType { - public static readonly IFieldType ARROWHEADS = new FieldType(nameof(ARROWHEADS)); - public static readonly IFieldType BLUR_RADIUS = new FieldType(nameof(BLUR_RADIUS)); - public static readonly IFieldType BRIGHTNESS = new FieldType(nameof(BRIGHTNESS)); - public static readonly IFieldType FILL_COLOR = new FieldType(nameof(FILL_COLOR)); - public static readonly IFieldType FONT_BOLD = new FieldType(nameof(FONT_BOLD)); - public static readonly IFieldType FONT_FAMILY = new FieldType(nameof(FONT_FAMILY)); - public static readonly IFieldType FONT_ITALIC = new FieldType(nameof(FONT_ITALIC)); - public static readonly IFieldType FONT_SIZE = new FieldType(nameof(FONT_SIZE)); - public static readonly IFieldType TEXT_HORIZONTAL_ALIGNMENT = new FieldType(nameof(TEXT_HORIZONTAL_ALIGNMENT)); - public static readonly IFieldType TEXT_VERTICAL_ALIGNMENT = new FieldType(nameof(TEXT_VERTICAL_ALIGNMENT)); - public static readonly IFieldType HIGHLIGHT_COLOR = new FieldType(nameof(HIGHLIGHT_COLOR)); - public static readonly IFieldType LINE_COLOR = new FieldType(nameof(LINE_COLOR)); - public static readonly IFieldType LINE_THICKNESS = new FieldType(nameof(LINE_THICKNESS)); - public static readonly IFieldType MAGNIFICATION_FACTOR = new FieldType(nameof(MAGNIFICATION_FACTOR)); - public static readonly IFieldType PIXEL_SIZE = new FieldType(nameof(PIXEL_SIZE)); - public static readonly IFieldType PREVIEW_QUALITY = new FieldType(nameof(PREVIEW_QUALITY)); - public static readonly IFieldType SHADOW = new FieldType(nameof(SHADOW)); - public static readonly IFieldType PREPARED_FILTER_OBFUSCATE = new FieldType(nameof(PREPARED_FILTER_OBFUSCATE)); - public static readonly IFieldType PREPARED_FILTER_HIGHLIGHT = new FieldType(nameof(PREPARED_FILTER_HIGHLIGHT)); - public static readonly IFieldType FLAGS = new FieldType(nameof(FLAGS)); - public static readonly IFieldType CROPMODE = new FieldType(nameof(CROPMODE)); - + public static readonly IFieldType ARROWHEADS = new FieldType("ARROWHEADS"); + public static readonly IFieldType BLUR_RADIUS = new FieldType("BLUR_RADIUS"); + public static readonly IFieldType BRIGHTNESS = new FieldType("BRIGHTNESS"); + public static readonly IFieldType FILL_COLOR = new FieldType("FILL_COLOR"); + public static readonly IFieldType FONT_BOLD = new FieldType("FONT_BOLD"); + public static readonly IFieldType FONT_FAMILY = new FieldType("FONT_FAMILY"); + public static readonly IFieldType FONT_ITALIC = new FieldType("FONT_ITALIC"); + public static readonly IFieldType FONT_SIZE = new FieldType("FONT_SIZE"); + public static readonly IFieldType TEXT_HORIZONTAL_ALIGNMENT = new FieldType("TEXT_HORIZONTAL_ALIGNMENT"); + public static readonly IFieldType TEXT_VERTICAL_ALIGNMENT = new FieldType("TEXT_VERTICAL_ALIGNMENT"); + public static readonly IFieldType HIGHLIGHT_COLOR = new FieldType("HIGHLIGHT_COLOR"); + public static readonly IFieldType LINE_COLOR = new FieldType("LINE_COLOR"); + public static readonly IFieldType LINE_THICKNESS = new FieldType("LINE_THICKNESS"); + public static readonly IFieldType MAGNIFICATION_FACTOR = new FieldType("MAGNIFICATION_FACTOR"); + public static readonly IFieldType PIXEL_SIZE = new FieldType("PIXEL_SIZE"); + public static readonly IFieldType PREVIEW_QUALITY = new FieldType("PREVIEW_QUALITY"); + public static readonly IFieldType SHADOW = new FieldType("SHADOW"); + public static readonly IFieldType PREPARED_FILTER_OBFUSCATE = new FieldType("PREPARED_FILTER_OBFUSCATE"); + public static readonly IFieldType PREPARED_FILTER_HIGHLIGHT = new FieldType("PREPARED_FILTER_HIGHLIGHT"); + public static readonly IFieldType FLAGS = new FieldType("FLAGS"); public static IFieldType[] Values = { ARROWHEADS, BLUR_RADIUS, BRIGHTNESS, FILL_COLOR, FONT_BOLD, FONT_FAMILY, FONT_ITALIC, FONT_SIZE, TEXT_HORIZONTAL_ALIGNMENT, TEXT_VERTICAL_ALIGNMENT, HIGHLIGHT_COLOR, - LINE_COLOR, LINE_THICKNESS, MAGNIFICATION_FACTOR, PIXEL_SIZE, PREVIEW_QUALITY, SHADOW, PREPARED_FILTER_OBFUSCATE, PREPARED_FILTER_HIGHLIGHT, FLAGS, CROPMODE + LINE_COLOR, LINE_THICKNESS, MAGNIFICATION_FACTOR, PIXEL_SIZE, PREVIEW_QUALITY, SHADOW, PREPARED_FILTER_OBFUSCATE, PREPARED_FILTER_HIGHLIGHT, FLAGS }; public string Name { get; set; } diff --git a/src/Greenshot.Editor/Drawing/FilterContainer.cs b/src/Greenshot.Editor/Drawing/FilterContainer.cs index 4b846fe66..3bdb5a5fd 100644 --- a/src/Greenshot.Editor/Drawing/FilterContainer.cs +++ b/src/Greenshot.Editor/Drawing/FilterContainer.cs @@ -23,9 +23,6 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Runtime.Serialization; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; -using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Helpers; @@ -54,7 +51,7 @@ namespace Greenshot.Editor.Drawing MAGNIFICATION }; - public FilterContainer(ISurface parent) : base(parent) + public FilterContainer(Surface parent) : base(parent) { Init(); } @@ -81,36 +78,37 @@ namespace Greenshot.Editor.Drawing { int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); - if (!lineVisible) return; bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - - graphics.SmoothingMode = SmoothingMode.HighSpeed; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.None; - //draw shadow first - if (shadow) + bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); + if (lineVisible) { - int basealpha = 100; - int alpha = basealpha; - int steps = 5; - int currentStep = lineVisible ? 1 : 0; - while (currentStep <= steps) + graphics.SmoothingMode = SmoothingMode.HighSpeed; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.None; + //draw shadow first + if (shadow) { - using Pen shadowPen = new Pen(Color.FromArgb(alpha, 100, 100, 100), lineThickness); - var shadowRect = new NativeRect(Left + currentStep, Top + currentStep, Width, Height).Normalize(); - graphics.DrawRectangle(shadowPen, shadowRect); - currentStep++; - alpha -= basealpha / steps; + int basealpha = 100; + int alpha = basealpha; + int steps = 5; + int currentStep = lineVisible ? 1 : 0; + while (currentStep <= steps) + { + using Pen shadowPen = new Pen(Color.FromArgb(alpha, 100, 100, 100), lineThickness); + Rectangle shadowRect = GuiRectangle.GetGuiRectangle(Left + currentStep, Top + currentStep, Width, Height); + graphics.DrawRectangle(shadowPen, shadowRect); + currentStep++; + alpha -= basealpha / steps; + } } - } - if (lineThickness > 0) - { - var rect = new NativeRect(Left, Top, Width, Height).Normalize(); - using Pen pen = new Pen(lineColor, lineThickness); - graphics.DrawRectangle(pen, rect); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + if (lineThickness > 0) + { + using Pen pen = new Pen(lineColor, lineThickness); + graphics.DrawRectangle(pen, rect); + } } } } diff --git a/src/Greenshot.Editor/Drawing/Filters/AbstractFilter.cs b/src/Greenshot.Editor/Drawing/Filters/AbstractFilter.cs index c972ed77d..17517b971 100644 --- a/src/Greenshot.Editor/Drawing/Filters/AbstractFilter.cs +++ b/src/Greenshot.Editor/Drawing/Filters/AbstractFilter.cs @@ -22,7 +22,6 @@ using System; using System.ComponentModel; using System.Drawing; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; @@ -74,7 +73,7 @@ namespace Greenshot.Editor.Drawing.Filters return parent; } - public abstract void Apply(Graphics graphics, Bitmap applyBitmap, NativeRect rect, RenderMode renderMode); + public abstract void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode); protected void OnPropertyChanged(string propertyName) { diff --git a/src/Greenshot.Editor/Drawing/Filters/BlurFilter.cs b/src/Greenshot.Editor/Drawing/Filters/BlurFilter.cs index 010cfd2cb..ac0e26a64 100644 --- a/src/Greenshot.Editor/Drawing/Filters/BlurFilter.cs +++ b/src/Greenshot.Editor/Drawing/Filters/BlurFilter.cs @@ -22,10 +22,9 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; -using Dapplo.Windows.Common.Structs; -using Dapplo.Windows.Gdi32; using Greenshot.Base.Core; using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Base.UnmanagedHelpers; using Greenshot.Editor.Drawing.Fields; namespace Greenshot.Editor.Drawing.Filters @@ -51,10 +50,10 @@ namespace Greenshot.Editor.Drawing.Filters AddField(GetType(), FieldType.PREVIEW_QUALITY, 1.0d); } - public override void Apply(Graphics graphics, Bitmap applyBitmap, NativeRect rect, RenderMode renderMode) + public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) { int blurRadius = GetFieldValueAsInt(FieldType.BLUR_RADIUS); - var applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); + Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); if (applyRect.Width == 0 || applyRect.Height == 0) { return; @@ -67,9 +66,9 @@ namespace Greenshot.Editor.Drawing.Filters graphics.ExcludeClip(rect); } - if (GdiPlusApi.IsBlurPossible(blurRadius)) + if (GDIplus.IsBlurPossible(blurRadius)) { - GdiPlusApi.DrawWithBlur(graphics, applyBitmap, applyRect, null, null, blurRadius, false); + GDIplus.DrawWithBlur(graphics, applyBitmap, applyRect, null, null, blurRadius, false); } else { diff --git a/src/Greenshot.Editor/Drawing/Filters/BrightnessFilter.cs b/src/Greenshot.Editor/Drawing/Filters/BrightnessFilter.cs index 639b0e5ac..cfdf7b025 100644 --- a/src/Greenshot.Editor/Drawing/Filters/BrightnessFilter.cs +++ b/src/Greenshot.Editor/Drawing/Filters/BrightnessFilter.cs @@ -23,7 +23,6 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Core; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; @@ -43,11 +42,11 @@ namespace Greenshot.Editor.Drawing.Filters /// /// /// - /// NativeRect + /// /// - public override void Apply(Graphics graphics, Bitmap applyBitmap, NativeRect rect, RenderMode renderMode) + public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) { - var applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); + Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); if (applyRect.Width == 0 || applyRect.Height == 0) { diff --git a/src/Greenshot.Editor/Drawing/Filters/GrayscaleFilter.cs b/src/Greenshot.Editor/Drawing/Filters/GrayscaleFilter.cs index 441102980..5ed195fc7 100644 --- a/src/Greenshot.Editor/Drawing/Filters/GrayscaleFilter.cs +++ b/src/Greenshot.Editor/Drawing/Filters/GrayscaleFilter.cs @@ -23,14 +23,13 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Core; using Greenshot.Base.Interfaces.Drawing; namespace Greenshot.Editor.Drawing.Filters { /// - /// GrayscaleFilter makes the image grey + /// Description of GrayscaleFilter. /// [Serializable()] public class GrayscaleFilter : AbstractFilter @@ -39,9 +38,9 @@ namespace Greenshot.Editor.Drawing.Filters { } - public override void Apply(Graphics graphics, Bitmap applyBitmap, NativeRect rect, RenderMode renderMode) + public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) { - var applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); + Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); if (applyRect.Width == 0 || applyRect.Height == 0) { diff --git a/src/Greenshot.Editor/Drawing/Filters/HighlightFilter.cs b/src/Greenshot.Editor/Drawing/Filters/HighlightFilter.cs index 24624d193..32337fb98 100644 --- a/src/Greenshot.Editor/Drawing/Filters/HighlightFilter.cs +++ b/src/Greenshot.Editor/Drawing/Filters/HighlightFilter.cs @@ -22,16 +22,12 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Core; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; namespace Greenshot.Editor.Drawing.Filters { - /// - /// This filter highlights an area - /// [Serializable()] public class HighlightFilter : AbstractFilter { @@ -45,11 +41,11 @@ namespace Greenshot.Editor.Drawing.Filters /// /// /// - /// NativeRect + /// /// - public override void Apply(Graphics graphics, Bitmap applyBitmap, NativeRect rect, RenderMode renderMode) + public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) { - var applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); + Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); if (applyRect.Width == 0 || applyRect.Height == 0) { diff --git a/src/Greenshot.Editor/Drawing/Filters/IFilter.cs b/src/Greenshot.Editor/Drawing/Filters/IFilter.cs index 8c6d3d21d..144732761 100644 --- a/src/Greenshot.Editor/Drawing/Filters/IFilter.cs +++ b/src/Greenshot.Editor/Drawing/Filters/IFilter.cs @@ -21,7 +21,6 @@ using System.ComponentModel; using System.Drawing; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Interfaces.Drawing; namespace Greenshot.Editor.Drawing.Filters @@ -29,7 +28,7 @@ namespace Greenshot.Editor.Drawing.Filters public interface IFilter : INotifyPropertyChanged, IFieldHolder { DrawableContainer Parent { get; set; } - void Apply(Graphics graphics, Bitmap bmp, NativeRect rect, RenderMode renderMode); + void Apply(Graphics graphics, Bitmap bmp, Rectangle rect, RenderMode renderMode); DrawableContainer GetParent(); bool Invert { get; set; } } diff --git a/src/Greenshot.Editor/Drawing/Filters/MagnifierFilter.cs b/src/Greenshot.Editor/Drawing/Filters/MagnifierFilter.cs index 70d829a2e..f76640820 100644 --- a/src/Greenshot.Editor/Drawing/Filters/MagnifierFilter.cs +++ b/src/Greenshot.Editor/Drawing/Filters/MagnifierFilter.cs @@ -22,16 +22,12 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Core; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; namespace Greenshot.Editor.Drawing.Filters { - /// - /// Magnify an area - /// [Serializable] public class MagnifierFilter : AbstractFilter { @@ -40,9 +36,9 @@ namespace Greenshot.Editor.Drawing.Filters AddField(GetType(), FieldType.MAGNIFICATION_FACTOR, 2); } - public override void Apply(Graphics graphics, Bitmap applyBitmap, NativeRect rect, RenderMode renderMode) + public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) { - var applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); + Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); if (applyRect.Width == 0 || applyRect.Height == 0) { @@ -66,7 +62,7 @@ namespace Greenshot.Editor.Drawing.Filters int halfHeight = rect.Height / 2; int newWidth = rect.Width / magnificationFactor; int newHeight = rect.Height / magnificationFactor; - var source = new NativeRect(rect.X + halfWidth - newWidth / 2, rect.Y + halfHeight - newHeight / 2, newWidth, newHeight); + Rectangle source = new Rectangle(rect.X + halfWidth - newWidth / 2, rect.Y + halfHeight - newHeight / 2, newWidth, newHeight); graphics.DrawImage(applyBitmap, rect, source, GraphicsUnit.Pixel); graphics.Restore(state); } diff --git a/src/Greenshot.Editor/Drawing/Filters/PixelizationFilter.cs b/src/Greenshot.Editor/Drawing/Filters/PixelizationFilter.cs index fa677b128..d0b88d4f7 100644 --- a/src/Greenshot.Editor/Drawing/Filters/PixelizationFilter.cs +++ b/src/Greenshot.Editor/Drawing/Filters/PixelizationFilter.cs @@ -22,7 +22,6 @@ using System; using System.Collections.Generic; using System.Drawing; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Core; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; @@ -30,9 +29,6 @@ using Greenshot.Editor.Helpers; namespace Greenshot.Editor.Drawing.Filters { - /// - /// Pixelate an area - /// [Serializable()] public class PixelizationFilter : AbstractFilter { @@ -41,7 +37,7 @@ namespace Greenshot.Editor.Drawing.Filters AddField(GetType(), FieldType.PIXEL_SIZE, 5); } - public override void Apply(Graphics graphics, Bitmap applyBitmap, NativeRect rect, RenderMode renderMode) + public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) { int pixelSize = GetFieldValueAsInt(FieldType.PIXEL_SIZE); ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); diff --git a/src/Greenshot.Editor/Drawing/FreehandContainer.cs b/src/Greenshot.Editor/Drawing/FreehandContainer.cs index 9c66e1a8e..e37221551 100644 --- a/src/Greenshot.Editor/Drawing/FreehandContainer.cs +++ b/src/Greenshot.Editor/Drawing/FreehandContainer.cs @@ -1,325 +1,323 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Runtime.Serialization; -using Dapplo.Windows.Common.Structs; -using Greenshot.Base.Interfaces; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Editor.Drawing.Fields; -using Greenshot.Editor.Helpers; - -namespace Greenshot.Editor.Drawing -{ - /// - /// Description of PathContainer. - /// - [Serializable] - public class FreehandContainer : DrawableContainer - { - private static readonly float[] PointOffset = - { - 0.5f, 0.25f, 0.75f - }; - - [NonSerialized] - private GraphicsPath freehandPath = new GraphicsPath(); - - private Rectangle myBounds = NativeRect.Empty; - private Point lastMouse = NativePoint.Empty; - private List capturePoints = new List(); - private bool isRecalculated; - - /// - /// Constructor - /// - public FreehandContainer(ISurface parent) : base(parent) - { - Width = parent.Image.Width; - Height = parent.Image.Height; - Top = 0; - Left = 0; - } - - protected override void InitializeFields() - { - AddField(GetType(), FieldType.LINE_THICKNESS, 3); - AddField(GetType(), FieldType.LINE_COLOR, Color.Red); - } - - public override void Transform(Matrix matrix) - { - Point[] points = capturePoints.ToArray(); - - matrix.TransformPoints(points); - capturePoints.Clear(); - capturePoints.AddRange(points); - RecalculatePath(); - } - - protected override void OnDeserialized(StreamingContext context) - { - RecalculatePath(); - } - - /// - /// This Dispose is called from the Dispose and the Destructor. - /// - /// When disposing==true all non-managed resources should be freed too! - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - { - freehandPath?.Dispose(); - } - - freehandPath = null; - } - - /// - /// Called from Surface (the parent) when the drawing begins (mouse-down) - /// - /// true if the surface doesn't need to handle the event - public override bool HandleMouseDown(int mouseX, int mouseY) - { - lastMouse = new Point(mouseX, mouseY); - capturePoints.Add(lastMouse); - return true; - } - - /// - /// Called from Surface (the parent) if a mouse move is made while drawing - /// - /// true if the surface doesn't need to handle the event - public override bool HandleMouseMove(int mouseX, int mouseY) - { - Point previousPoint = capturePoints[capturePoints.Count - 1]; - - if (GeometryHelper.Distance2D(previousPoint.X, previousPoint.Y, mouseX, mouseY) >= 2 * EditorConfig.FreehandSensitivity) - { - capturePoints.Add(new Point(mouseX, mouseY)); - } - - if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) < EditorConfig.FreehandSensitivity) - { - return true; - } - - //path.AddCurve(new Point[]{lastMouse, new Point(mouseX, mouseY)}); - lastMouse = new Point(mouseX, mouseY); - freehandPath.AddLine(lastMouse, new Point(mouseX, mouseY)); - // Only re-calculate the bounds & redraw when we added something to the path - myBounds = Rectangle.Round(freehandPath.GetBounds()); - - Invalidate(); - return true; - } - - /// - /// Called when the surface finishes drawing the element - /// - public override void HandleMouseUp(int mouseX, int mouseY) - { - // Make sure we don't loose the ending point - if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) >= EditorConfig.FreehandSensitivity) - { - capturePoints.Add(new Point(mouseX, mouseY)); - } - - RecalculatePath(); - } - - /// - /// Here we recalculate the freehand path by smoothing out the lines with Beziers. - /// - private void RecalculatePath() - { - // Store the previous path, to dispose it later when we are finished - var previousFreehandPath = freehandPath; - var newFreehandPath = new GraphicsPath(); - - // Here we can put some cleanup... like losing all the uninteresting points. - if (capturePoints.Count >= 3) - { - int index = 0; - while ((capturePoints.Count - 1) % 3 != 0) - { - // duplicate points, first at 50% than 25% than 75% - capturePoints.Insert((int) (capturePoints.Count * PointOffset[index]), capturePoints[(int) (capturePoints.Count * PointOffset[index++])]); - } - - newFreehandPath.AddBeziers(capturePoints.ToArray()); - } - else if (capturePoints.Count == 2) - { - newFreehandPath.AddLine(capturePoints[0], capturePoints[1]); - } - - // Recalculate the bounds - myBounds = Rectangle.Round(newFreehandPath.GetBounds()); - - // assign - isRecalculated = true; - freehandPath = newFreehandPath; - - // dispose previous - previousFreehandPath?.Dispose(); - } - - /// - /// Do the drawing of the freehand "stroke" - /// - /// - /// - public override void Draw(Graphics graphics, RenderMode renderMode) - { - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - using var pen = new Pen(lineColor) - { - Width = lineThickness - }; - if (!(pen.Width > 0)) - { - return; - } - - // Make sure the lines are nicely rounded - pen.EndCap = LineCap.Round; - pen.StartCap = LineCap.Round; - pen.LineJoin = LineJoin.Round; - // Move to where we need to draw - graphics.TranslateTransform(Left, Top); - var currentFreehandPath = freehandPath; - if (currentFreehandPath != null) - { - if (isRecalculated && Selected && renderMode == RenderMode.EDIT) - { - isRecalculated = false; - DrawSelectionBorder(graphics, pen, currentFreehandPath); - } - - graphics.DrawPath(pen, currentFreehandPath); - } - - // Move back, otherwise everything is shifted - graphics.TranslateTransform(-Left, -Top); - } - - /// - /// Draw a selectionborder around the freehand path - /// - /// Graphics - /// Pen - /// GraphicsPath - protected static void DrawSelectionBorder(Graphics graphics, Pen linePen, GraphicsPath path) - { - using var selectionPen = (Pen) linePen.Clone(); - using var selectionPath = (GraphicsPath) path.Clone(); - selectionPen.Width += 5; - selectionPen.Color = Color.FromArgb(120, Color.LightSeaGreen); - graphics.DrawPath(selectionPen, selectionPath); - selectionPath.Widen(selectionPen); - selectionPen.DashPattern = new float[] - { - 2, 2 - }; - selectionPen.Color = Color.LightSeaGreen; - selectionPen.Width = 1; - graphics.DrawPath(selectionPen, selectionPath); - } - - /// - /// Get the bounds in which we have something drawn, plus safety margin, these are not the normal bounds... - /// - public override NativeRect DrawingBounds - { - get - { - if (!myBounds.IsEmpty) - { - int lineThickness = Math.Max(10, GetFieldValueAsInt(FieldType.LINE_THICKNESS)); - int safetyMargin = 10; - return new NativeRect(myBounds.Left + Left - (safetyMargin + lineThickness), myBounds.Top + Top - (safetyMargin + lineThickness), - myBounds.Width + 2 * (lineThickness + safetyMargin), myBounds.Height + 2 * (lineThickness + safetyMargin)); - } - - if (_parent?.Image is Image image) - { - return new NativeRect(0, 0, image.Width, image.Height); - } - - return NativeRect.Empty; - } - } - - /// - /// FreehandContainer are regarded equal if they are of the same type and their paths are equal. - /// - /// object - /// bool - public override bool Equals(object obj) - { - bool ret = false; - if (obj == null || GetType() != obj.GetType()) - { - return false; - } - - if (obj is FreehandContainer other && Equals(freehandPath, other.freehandPath)) - { - ret = true; - } - - return ret; - } - - public override int GetHashCode() - { - return freehandPath?.GetHashCode() ?? 0; - } - - public override bool ClickableAt(int x, int y) - { - bool returnValue = base.ClickableAt(x, y); - if (returnValue) - { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - using var pen = new Pen(Color.White) - { - Width = lineThickness + 10 - }; - returnValue = freehandPath.IsOutlineVisible(x - Left, y - Top, pen); - } - - return returnValue; - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Runtime.Serialization; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Helpers; + +namespace Greenshot.Editor.Drawing +{ + /// + /// Description of PathContainer. + /// + [Serializable] + public class FreehandContainer : DrawableContainer + { + private static readonly float[] PointOffset = + { + 0.5f, 0.25f, 0.75f + }; + + [NonSerialized] private GraphicsPath freehandPath = new GraphicsPath(); + private Rectangle myBounds = Rectangle.Empty; + private Point lastMouse = Point.Empty; + private readonly List capturePoints = new List(); + private bool isRecalculated; + + /// + /// Constructor + /// + public FreehandContainer(Surface parent) : base(parent) + { + Width = parent.Image.Width; + Height = parent.Image.Height; + Top = 0; + Left = 0; + } + + protected override void InitializeFields() + { + AddField(GetType(), FieldType.LINE_THICKNESS, 3); + AddField(GetType(), FieldType.LINE_COLOR, Color.Red); + } + + public override void Transform(Matrix matrix) + { + Point[] points = capturePoints.ToArray(); + + matrix.TransformPoints(points); + capturePoints.Clear(); + capturePoints.AddRange(points); + RecalculatePath(); + } + + protected override void OnDeserialized(StreamingContext context) + { + RecalculatePath(); + } + + /// + /// This Dispose is called from the Dispose and the Destructor. + /// + /// When disposing==true all non-managed resources should be freed too! + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + { + freehandPath?.Dispose(); + } + + freehandPath = null; + } + + /// + /// Called from Surface (the parent) when the drawing begins (mouse-down) + /// + /// true if the surface doesn't need to handle the event + public override bool HandleMouseDown(int mouseX, int mouseY) + { + lastMouse = new Point(mouseX, mouseY); + capturePoints.Add(lastMouse); + return true; + } + + /// + /// Called from Surface (the parent) if a mouse move is made while drawing + /// + /// true if the surface doesn't need to handle the event + public override bool HandleMouseMove(int mouseX, int mouseY) + { + Point previousPoint = capturePoints[capturePoints.Count - 1]; + + if (GeometryHelper.Distance2D(previousPoint.X, previousPoint.Y, mouseX, mouseY) >= 2 * EditorConfig.FreehandSensitivity) + { + capturePoints.Add(new Point(mouseX, mouseY)); + } + + if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) < EditorConfig.FreehandSensitivity) + { + return true; + } + + //path.AddCurve(new Point[]{lastMouse, new Point(mouseX, mouseY)}); + lastMouse = new Point(mouseX, mouseY); + freehandPath.AddLine(lastMouse, new Point(mouseX, mouseY)); + // Only re-calculate the bounds & redraw when we added something to the path + myBounds = Rectangle.Round(freehandPath.GetBounds()); + + Invalidate(); + return true; + } + + /// + /// Called when the surface finishes drawing the element + /// + public override void HandleMouseUp(int mouseX, int mouseY) + { + // Make sure we don't loose the ending point + if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) >= EditorConfig.FreehandSensitivity) + { + capturePoints.Add(new Point(mouseX, mouseY)); + } + + RecalculatePath(); + } + + /// + /// Here we recalculate the freehand path by smoothing out the lines with Beziers. + /// + private void RecalculatePath() + { + // Store the previous path, to dispose it later when we are finished + var previousFreehandPath = freehandPath; + var newFreehandPath = new GraphicsPath(); + + // Here we can put some cleanup... like losing all the uninteresting points. + if (capturePoints.Count >= 3) + { + int index = 0; + while ((capturePoints.Count - 1) % 3 != 0) + { + // duplicate points, first at 50% than 25% than 75% + capturePoints.Insert((int) (capturePoints.Count * PointOffset[index]), capturePoints[(int) (capturePoints.Count * PointOffset[index++])]); + } + + newFreehandPath.AddBeziers(capturePoints.ToArray()); + } + else if (capturePoints.Count == 2) + { + newFreehandPath.AddLine(capturePoints[0], capturePoints[1]); + } + + // Recalculate the bounds + myBounds = Rectangle.Round(newFreehandPath.GetBounds()); + + // assign + isRecalculated = true; + freehandPath = newFreehandPath; + + // dispose previous + previousFreehandPath?.Dispose(); + } + + /// + /// Do the drawing of the freehand "stroke" + /// + /// + /// + public override void Draw(Graphics graphics, RenderMode renderMode) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + using var pen = new Pen(lineColor) + { + Width = lineThickness + }; + if (!(pen.Width > 0)) + { + return; + } + + // Make sure the lines are nicely rounded + pen.EndCap = LineCap.Round; + pen.StartCap = LineCap.Round; + pen.LineJoin = LineJoin.Round; + // Move to where we need to draw + graphics.TranslateTransform(Left, Top); + var currentFreehandPath = freehandPath; + if (currentFreehandPath != null) + { + if (isRecalculated && Selected && renderMode == RenderMode.EDIT) + { + isRecalculated = false; + DrawSelectionBorder(graphics, pen, currentFreehandPath); + } + + graphics.DrawPath(pen, currentFreehandPath); + } + + // Move back, otherwise everything is shifted + graphics.TranslateTransform(-Left, -Top); + } + + /// + /// Draw a selectionborder around the freehand path + /// + /// Graphics + /// Pen + /// GraphicsPath + protected static void DrawSelectionBorder(Graphics graphics, Pen linePen, GraphicsPath path) + { + using var selectionPen = (Pen) linePen.Clone(); + using var selectionPath = (GraphicsPath) path.Clone(); + selectionPen.Width += 5; + selectionPen.Color = Color.FromArgb(120, Color.LightSeaGreen); + graphics.DrawPath(selectionPen, selectionPath); + selectionPath.Widen(selectionPen); + selectionPen.DashPattern = new float[] + { + 2, 2 + }; + selectionPen.Color = Color.LightSeaGreen; + selectionPen.Width = 1; + graphics.DrawPath(selectionPen, selectionPath); + } + + /// + /// Get the bounds in which we have something drawn, plus safety margin, these are not the normal bounds... + /// + public override Rectangle DrawingBounds + { + get + { + if (!myBounds.IsEmpty) + { + int lineThickness = Math.Max(10, GetFieldValueAsInt(FieldType.LINE_THICKNESS)); + int safetymargin = 10; + return new Rectangle(myBounds.Left + Left - (safetymargin + lineThickness), myBounds.Top + Top - (safetymargin + lineThickness), + myBounds.Width + 2 * (lineThickness + safetymargin), myBounds.Height + 2 * (lineThickness + safetymargin)); + } + + if (_parent?.Image is Image image) + { + return new Rectangle(0, 0, image.Width, image.Height); + } + else + { + return Rectangle.Empty; + } + } + } + + /// + /// FreehandContainer are regarded equal if they are of the same type and their paths are equal. + /// + /// object + /// bool + public override bool Equals(object obj) + { + bool ret = false; + if (obj == null || GetType() != obj.GetType()) + { + return false; + } + + if (obj is FreehandContainer other && Equals(freehandPath, other.freehandPath)) + { + ret = true; + } + + return ret; + } + + public override int GetHashCode() + { + return freehandPath?.GetHashCode() ?? 0; + } + + public override bool ClickableAt(int x, int y) + { + bool returnValue = base.ClickableAt(x, y); + if (returnValue) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + using var pen = new Pen(Color.White) + { + Width = lineThickness + 10 + }; + returnValue = freehandPath.IsOutlineVisible(x - Left, y - Top, pen); + } + + return returnValue; + } + } } \ No newline at end of file diff --git a/src/Greenshot.Editor/Drawing/HighlightContainer.cs b/src/Greenshot.Editor/Drawing/HighlightContainer.cs index 58cf792ba..417725322 100644 --- a/src/Greenshot.Editor/Drawing/HighlightContainer.cs +++ b/src/Greenshot.Editor/Drawing/HighlightContainer.cs @@ -21,7 +21,6 @@ using System; using System.Runtime.Serialization; -using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Drawing.Filters; @@ -34,7 +33,7 @@ namespace Greenshot.Editor.Drawing [Serializable] public class HighlightContainer : FilterContainer { - public HighlightContainer(ISurface parent) : base(parent) + public HighlightContainer(Surface parent) : base(parent) { Init(); } diff --git a/src/Greenshot.Editor/Drawing/IconContainer.cs b/src/Greenshot.Editor/Drawing/IconContainer.cs index 185c17cd2..e98b37f82 100644 --- a/src/Greenshot.Editor/Drawing/IconContainer.cs +++ b/src/Greenshot.Editor/Drawing/IconContainer.cs @@ -24,8 +24,6 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.IO; using System.Runtime.Serialization; -using Dapplo.Windows.Common.Structs; -using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using log4net; @@ -41,7 +39,7 @@ namespace Greenshot.Editor.Drawing protected Icon icon; - public IconContainer(ISurface parent) : base(parent) + public IconContainer(Surface parent) : base(parent) { Init(); } @@ -57,16 +55,11 @@ namespace Greenshot.Editor.Drawing CreateDefaultAdorners(); } - public IconContainer(ISurface parent, string filename) : base(parent) + public IconContainer(Surface parent, string filename) : base(parent) { Load(filename); } - public IconContainer(ISurface parent, Stream stream) : base(parent) - { - Load(stream); - } - public Icon Icon { set @@ -106,18 +99,6 @@ namespace Greenshot.Editor.Drawing Log.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width); } - public void Load(Stream iconStream) - { - if (iconStream == null) - { - return; - } - - using Icon fileIcon = new Icon(iconStream); - Icon = fileIcon; - Log.Debug("Loaded stream: with resolution: " + Height + "," + Width); - } - public override void Draw(Graphics graphics, RenderMode rm) { if (icon == null) @@ -134,6 +115,6 @@ namespace Greenshot.Editor.Drawing public override bool HasDefaultSize => true; - public override NativeSize DefaultSize => icon?.Size ?? new NativeSize(16, 16); + public override Size DefaultSize => icon?.Size ?? new Size(16, 16); } } \ No newline at end of file diff --git a/src/Greenshot.Editor/Drawing/ImageContainer.cs b/src/Greenshot.Editor/Drawing/ImageContainer.cs index 21f8d2fa6..a46b0dae5 100644 --- a/src/Greenshot.Editor/Drawing/ImageContainer.cs +++ b/src/Greenshot.Editor/Drawing/ImageContainer.cs @@ -24,10 +24,8 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.IO; using System.Runtime.Serialization; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Core; using Greenshot.Base.Effects; -using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; using log4net; @@ -42,7 +40,7 @@ namespace Greenshot.Editor.Drawing { private static readonly ILog Log = LogManager.GetLogger(typeof(ImageContainer)); - private Image _image; + private Image image; /// /// This is the shadow version of the bitmap, rendered once to save performance @@ -54,14 +52,14 @@ namespace Greenshot.Editor.Drawing /// This is the offset for the shadow version of the bitmap /// Do not serialize, as the offset is recreated /// - [NonSerialized] private NativePoint _shadowOffset = new NativePoint(-1, -1); + [NonSerialized] private Point _shadowOffset = new Point(-1, -1); - public ImageContainer(ISurface parent, string filename) : this(parent) + public ImageContainer(Surface parent, string filename) : this(parent) { Load(filename); } - public ImageContainer(ISurface parent) : base(parent) + public ImageContainer(Surface parent) : base(parent) { FieldChanged += BitmapContainer_OnFieldChanged; Init(); @@ -109,8 +107,8 @@ namespace Greenshot.Editor.Drawing } else { - Width = _image.Width; - Height = _image.Height; + Width = image.Width; + Height = image.Height; if (_shadowBitmap != null) { Left += _shadowOffset.X; @@ -126,13 +124,13 @@ namespace Greenshot.Editor.Drawing // Remove all current bitmaps DisposeImage(); DisposeShadow(); - _image = ImageHelper.Clone(value); + image = ImageHelper.Clone(value); bool shadow = GetFieldValueAsBool(FieldType.SHADOW); CheckShadow(shadow); if (!shadow) { - Width = _image.Width; - Height = _image.Height; + Width = image.Width; + Height = image.Height; } else { @@ -142,7 +140,7 @@ namespace Greenshot.Editor.Drawing Top -= _shadowOffset.Y; } } - get { return _image; } + get { return image; } } /// @@ -164,8 +162,8 @@ namespace Greenshot.Editor.Drawing private void DisposeImage() { - _image?.Dispose(); - _image = null; + image?.Dispose(); + image = null; } private void DisposeShadow() @@ -188,9 +186,9 @@ namespace Greenshot.Editor.Drawing Log.DebugFormat("Rotating element with {0} degrees.", rotateAngle); DisposeShadow(); using var tmpMatrix = new Matrix(); - using (_image) + using (image) { - _image = ImageHelper.ApplyEffect(_image, new RotateEffect(rotateAngle), tmpMatrix); + image = ImageHelper.ApplyEffect(image, new RotateEffect(rotateAngle), tmpMatrix); } } @@ -210,8 +208,7 @@ namespace Greenshot.Editor.Drawing // Always make sure ImageHelper.LoadBitmap results are disposed some time, // as we close the bitmap internally, we need to do it afterwards - // TODO: Replace with some other code, like the file format handler, or move it completely outside of this class - using (var tmpImage = ImageIO.LoadImage(filename)) + using (var tmpImage = ImageHelper.LoadImage(filename)) { Image = tmpImage; } @@ -231,7 +228,7 @@ namespace Greenshot.Editor.Drawing } using var matrix = new Matrix(); - _shadowBitmap = ImageHelper.ApplyEffect(_image, new DropShadowEffect(), matrix); + _shadowBitmap = ImageHelper.ApplyEffect(image, new DropShadowEffect(), matrix); } /// @@ -241,7 +238,7 @@ namespace Greenshot.Editor.Drawing /// public override void Draw(Graphics graphics, RenderMode rm) { - if (_image == null) + if (image == null) { return; } @@ -259,12 +256,12 @@ namespace Greenshot.Editor.Drawing } else { - graphics.DrawImage(_image, Bounds); + graphics.DrawImage(image, Bounds); } } public override bool HasDefaultSize => true; - public override NativeSize DefaultSize => _image?.Size ?? new NativeSize(32, 32); + public override Size DefaultSize => image?.Size ?? new Size(32, 32); } } \ No newline at end of file diff --git a/src/Greenshot.Editor/Drawing/LineContainer.cs b/src/Greenshot.Editor/Drawing/LineContainer.cs index f48dddec7..1bcf25a81 100644 --- a/src/Greenshot.Editor/Drawing/LineContainer.cs +++ b/src/Greenshot.Editor/Drawing/LineContainer.cs @@ -23,7 +23,6 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Runtime.Serialization; -using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Adorners; using Greenshot.Editor.Drawing.Fields; @@ -37,7 +36,7 @@ namespace Greenshot.Editor.Drawing [Serializable()] public class LineContainer : DrawableContainer { - public LineContainer(ISurface parent) : base(parent) + public LineContainer(Surface parent) : base(parent) { Init(); } @@ -62,40 +61,41 @@ namespace Greenshot.Editor.Drawing public override void Draw(Graphics graphics, RenderMode rm) { - - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - - if (lineThickness <= 0) return; graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.InterpolationMode = InterpolationMode.HighQualityBilinear; graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.PixelOffsetMode = PixelOffsetMode.None; - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - if (shadow) - { - //draw shadow first - int basealpha = 100; - int alpha = basealpha; - int steps = 5; - int currentStep = 1; - while (currentStep <= steps) - { - using Pen shadowCapPen = new Pen(Color.FromArgb(alpha, 100, 100, 100), lineThickness); - graphics.DrawLine(shadowCapPen, - Left + currentStep, - Top + currentStep, - Left + currentStep + Width, - Top + currentStep + Height); - - currentStep++; - alpha -= basealpha / steps; - } - } - + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - using Pen pen = new Pen(lineColor, lineThickness); - graphics.DrawLine(pen, Left, Top, Left + Width, Top + Height); + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + + if (lineThickness > 0) + { + if (shadow) + { + //draw shadow first + int basealpha = 100; + int alpha = basealpha; + int steps = 5; + int currentStep = 1; + while (currentStep <= steps) + { + using Pen shadowCapPen = new Pen(Color.FromArgb(alpha, 100, 100, 100), lineThickness); + graphics.DrawLine(shadowCapPen, + Left + currentStep, + Top + currentStep, + Left + currentStep + Width, + Top + currentStep + Height); + + currentStep++; + alpha -= basealpha / steps; + } + } + + using Pen pen = new Pen(lineColor, lineThickness); + graphics.DrawLine(pen, Left, Top, Left + Width, Top + Height); + } } public override bool ClickableAt(int x, int y) @@ -115,9 +115,9 @@ namespace Greenshot.Editor.Drawing return false; } - protected override IDoubleProcessor GetAngleRoundProcessor() + protected override ScaleHelper.IDoubleProcessor GetAngleRoundProcessor() { - return LineAngleRoundBehavior.INSTANCE; + return ScaleHelper.LineAngleRoundBehavior.Instance; } } } \ No newline at end of file diff --git a/src/Greenshot.Editor/Drawing/MetafileContainer.cs b/src/Greenshot.Editor/Drawing/MetafileContainer.cs deleted file mode 100644 index 3225c4700..000000000 --- a/src/Greenshot.Editor/Drawing/MetafileContainer.cs +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Drawing.Imaging; -using Dapplo.Windows.Common.Structs; -using Greenshot.Base.Core; -using Greenshot.Base.Interfaces; - -namespace Greenshot.Editor.Drawing -{ - /// - /// This provides a resizable SVG container, redrawing the SVG in the size the container takes. - /// - [Serializable] - public class MetafileContainer : VectorGraphicsContainer - { - private readonly Metafile _metafile; - - public MetafileContainer(Metafile metafile, ISurface parent) : base(parent) - { - _metafile = metafile; - Size = new NativeSize(metafile.Width/4, metafile.Height/4); - } - - protected override Image ComputeBitmap() - { - var image = ImageHelper.CreateEmpty(Width, Height, PixelFormat.Format32bppArgb, Color.Transparent); - - var dstRect = new NativeRect(0, 0, Width, Height); - using (Graphics graphics = Graphics.FromImage(image)) - { - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - graphics.DrawImage(_metafile, dstRect); - } - - if (RotationAngle == 0) return image; - - var newImage = image.Rotate(RotationAngle); - image.Dispose(); - return newImage; - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - _metafile?.Dispose(); - } - - base.Dispose(disposing); - } - - public override bool HasDefaultSize => true; - - public override NativeSize DefaultSize => new NativeSize(_metafile.Width, _metafile.Height); - } -} \ No newline at end of file diff --git a/src/Greenshot.Editor/Drawing/ObfuscateContainer.cs b/src/Greenshot.Editor/Drawing/ObfuscateContainer.cs index b0c890ffc..1754ba650 100644 --- a/src/Greenshot.Editor/Drawing/ObfuscateContainer.cs +++ b/src/Greenshot.Editor/Drawing/ObfuscateContainer.cs @@ -21,7 +21,6 @@ using System; using System.Runtime.Serialization; -using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Drawing.Filters; @@ -29,12 +28,12 @@ using Greenshot.Editor.Drawing.Filters; namespace Greenshot.Editor.Drawing { /// - /// This is a FilterContainer for the obfuscator filters like blur and pixelate. + /// Description of ObfuscateContainer. /// [Serializable] public class ObfuscateContainer : FilterContainer { - public ObfuscateContainer(ISurface parent) : base(parent) + public ObfuscateContainer(Surface parent) : base(parent) { Init(); } diff --git a/src/Greenshot.Editor/Drawing/RectangleContainer.cs b/src/Greenshot.Editor/Drawing/RectangleContainer.cs index 717e1a593..5e38fcf4b 100644 --- a/src/Greenshot.Editor/Drawing/RectangleContainer.cs +++ b/src/Greenshot.Editor/Drawing/RectangleContainer.cs @@ -23,9 +23,6 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Runtime.Serialization; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; -using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Helpers; @@ -38,7 +35,7 @@ namespace Greenshot.Editor.Drawing [Serializable] public class RectangleContainer : DrawableContainer { - public RectangleContainer(ISurface parent) : base(parent) + public RectangleContainer(Surface parent) : base(parent) { Init(); } @@ -72,7 +69,7 @@ namespace Greenshot.Editor.Drawing Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR, Color.Red); Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR, Color.Transparent); bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - var rect = new NativeRect(Left, Top, Width, Height).Normalize(); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); DrawRectangle(rect, graphics, rm, lineThickness, lineColor, fillColor, shadow); } @@ -80,14 +77,14 @@ namespace Greenshot.Editor.Drawing /// /// This method can also be used from other containers, if the right values are passed! /// - /// NativeRect + /// /// /// /// /// /// /// - public static void DrawRectangle(NativeRect rect, Graphics graphics, RenderMode rm, int lineThickness, Color lineColor, Color fillColor, bool shadow) + public static void DrawRectangle(Rectangle rect, Graphics graphics, RenderMode rm, int lineThickness, Color lineColor, Color fillColor, bool shadow) { graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; @@ -108,11 +105,11 @@ namespace Greenshot.Editor.Drawing { Width = lineVisible ? lineThickness : 1 }; - var shadowRect = new NativeRect( + Rectangle shadowRect = GuiRectangle.GetGuiRectangle( rect.Left + currentStep, rect.Top + currentStep, rect.Width, - rect.Height).Normalize(); + rect.Height); graphics.DrawRectangle(shadowPen, shadowRect); currentStep++; alpha -= basealpha / steps; @@ -136,14 +133,15 @@ namespace Greenshot.Editor.Drawing public override bool ClickableAt(int x, int y) { - var rect = new NativeRect(Left, Top, Width, Height).Normalize(); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 10; Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); return RectangleClickableAt(rect, lineThickness, fillColor, x, y); } - public static bool RectangleClickableAt(NativeRect rect, int lineThickness, Color fillColor, int x, int y) + + public static bool RectangleClickableAt(Rectangle rect, int lineThickness, Color fillColor, int x, int y) { // If we clicked inside the rectangle and it's visible we are clickable at. if (!Color.Transparent.Equals(fillColor)) diff --git a/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs b/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs index e1f37544b..2b189945d 100644 --- a/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs +++ b/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs @@ -24,9 +24,6 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Text; using System.Runtime.Serialization; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; -using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Helpers; @@ -64,10 +61,11 @@ namespace Greenshot.Editor.Drawing protected override void OnDeserialized(StreamingContext streamingContext) { base.OnDeserialized(streamingContext); - InitTargetAdorner(_storedTargetGripperLocation); + InitAdorner(Color.Green, _storedTargetGripperLocation); } - public SpeechbubbleContainer(ISurface parent) : base(parent) + public SpeechbubbleContainer(Surface parent) + : base(parent) { } @@ -96,8 +94,8 @@ namespace Greenshot.Editor.Drawing { if (TargetAdorner == null) { - _initialGripperPoint = new NativePoint(mouseX, mouseY); - InitTargetAdorner(_initialGripperPoint); + _initialGripperPoint = new Point(mouseX, mouseY); + InitAdorner(Color.Green, new Point(mouseX, mouseY)); } return base.HandleMouseDown(mouseX, mouseY); @@ -120,8 +118,8 @@ namespace Greenshot.Editor.Drawing int xOffset = leftAligned ? -20 : 20; int yOffset = topAligned ? -20 : 20; - NativePoint initialGripperPoint = _initialGripperPoint; - NativePoint newGripperLocation = initialGripperPoint.Offset(xOffset, yOffset); + Point newGripperLocation = _initialGripperPoint; + newGripperLocation.Offset(xOffset, yOffset); if (TargetAdorner.Location != newGripperLocation) { @@ -136,27 +134,23 @@ namespace Greenshot.Editor.Drawing /// /// The DrawingBound should be so close as possible to the shape, so we don't invalidate to much. /// - public override NativeRect DrawingBounds + public override Rectangle DrawingBounds { get { - if (Status == EditStatus.UNDRAWN) return NativeRect.Empty; - - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - using Pen pen = new Pen(lineColor, lineThickness); - int inflateValue = lineThickness + 2 + (shadow ? 6 : 0); - using GraphicsPath tailPath = CreateTail(); - - var bubbleBounds = new NativeRect(Left, Top, Width, Height).Normalize(); - using var matrix = new Matrix(); - - var tailBounds = Rectangle.Round(tailPath.GetBounds(matrix, pen)); - - var drawingBoundsWithoutSafety = bubbleBounds.Union(tailBounds); - return drawingBoundsWithoutSafety.Inflate(inflateValue, inflateValue); + if (Status != EditStatus.UNDRAWN) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + using Pen pen = new Pen(lineColor, lineThickness); + int inflateValue = lineThickness + 2 + (shadow ? 6 : 0); + using GraphicsPath tailPath = CreateTail(); + return Rectangle.Inflate(Rectangle.Union(Rectangle.Round(tailPath.GetBounds(new Matrix(), pen)), GuiRectangle.GetGuiRectangle(Left, Top, Width, Height)), + inflateValue, inflateValue); + } + return Rectangle.Empty; } } @@ -168,9 +162,9 @@ namespace Greenshot.Editor.Drawing private GraphicsPath CreateBubble(int lineThickness) { GraphicsPath bubble = new GraphicsPath(); - var rect = new NativeRect(Left, Top, Width, Height).Normalize(); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - var bubbleRect = new NativeRect(0, 0, rect.Width, rect.Height).Normalize(); + Rectangle bubbleRect = GuiRectangle.GetGuiRectangle(0, 0, rect.Width, rect.Height); // adapt corner radius to small rectangle dimensions int smallerSideLength = Math.Min(bubbleRect.Width, bubbleRect.Height); int cornerRadius = Math.Min(30, smallerSideLength / 2 - lineThickness); @@ -202,7 +196,7 @@ namespace Greenshot.Editor.Drawing /// private GraphicsPath CreateTail() { - var rect = new NativeRect(Left, Top, Width, Height).Normalize(); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); int tailLength = GeometryHelper.Distance2D(rect.Left + (rect.Width / 2), rect.Top + (rect.Height / 2), TargetAdorner.Location.X, TargetAdorner.Location.Y); int tailWidth = (Math.Abs(rect.Width) + Math.Abs(rect.Height)) / 20; @@ -252,7 +246,7 @@ namespace Greenshot.Editor.Drawing int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); - var rect = new NativeRect(Left, Top, Width, Height).Normalize(); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); if (Selected && renderMode == RenderMode.EDIT) { diff --git a/src/Greenshot.Editor/Drawing/StepLabelContainer.cs b/src/Greenshot.Editor/Drawing/StepLabelContainer.cs index 7edecc4f8..0e7c27516 100644 --- a/src/Greenshot.Editor/Drawing/StepLabelContainer.cs +++ b/src/Greenshot.Editor/Drawing/StepLabelContainer.cs @@ -24,12 +24,9 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Text; using System.Runtime.Serialization; -using System.Windows.Forms; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; -using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Helpers; namespace Greenshot.Editor.Drawing { @@ -44,9 +41,9 @@ namespace Greenshot.Editor.Drawing private readonly bool _drawAsRectangle = false; - public StepLabelContainer(ISurface parent) : base(parent) + public StepLabelContainer(Surface parent) : base(parent) { - InternalParent?.AddStepLabel(this); + parent.AddStepLabel(this); InitContent(); Init(); } @@ -75,10 +72,11 @@ namespace Greenshot.Editor.Drawing [OnSerializing] private void SetValuesOnSerializing(StreamingContext context) { - if (InternalParent == null) return; - - Number = InternalParent.CountStepLabels(this); - _counterStart = InternalParent.CounterStart; + if (Parent != null) + { + Number = ((Surface) Parent).CountStepLabels(this); + _counterStart = ((Surface) Parent).CounterStart; + } } /// @@ -99,26 +97,26 @@ namespace Greenshot.Editor.Drawing /// Add the StepLabel to the parent /// /// - protected override void SwitchParent(ISurface newParent) + protected override void SwitchParent(Surface newParent) { if (newParent == Parent) { return; } - if (newParent is not Surface newParentSurface) + ((Surface) Parent)?.RemoveStepLabel(this); + base.SwitchParent(newParent); + if (newParent == null) { return; } - InternalParent?.RemoveStepLabel(this); - base.SwitchParent(newParent); // Make sure the counter start is restored (this unfortunately happens multiple times... -> hack) - newParentSurface.CounterStart = _counterStart; - newParentSurface.AddStepLabel(this); + newParent.CounterStart = _counterStart; + newParent.AddStepLabel(this); } - public override NativeSize DefaultSize => new NativeSize(30, 30); + public override Size DefaultSize => new Size(30, 30); public override bool InitContent() { @@ -188,15 +186,13 @@ namespace Greenshot.Editor.Drawing /// public override void Draw(Graphics graphics, RenderMode rm) { - if (Width == 0 || Height == 0) { return; } - graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.PixelOffsetMode = PixelOffsetMode.None; graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; string text = ((Surface) Parent).CountStepLabels(this).ToString(); - var rect = new NativeRect(Left, Top, Width, Height).Normalize(); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); if (_drawAsRectangle) @@ -208,23 +204,15 @@ namespace Greenshot.Editor.Drawing EllipseContainer.DrawEllipse(rect, graphics, rm, 0, Color.Transparent, fillColor, false); } - using FontFamily fam = new(FontFamily.GenericSansSerif.Name); - - //calculate new font size based on ratio from text height and text width - float initialFontSize = Math.Min(Math.Abs(Width), Math.Abs(Height)); - using Font Measurefont = new(fam, initialFontSize, FontStyle.Bold, GraphicsUnit.Pixel); - var fontSize = initialFontSize * TextRenderer.MeasureText(text, Measurefont).Height / TextRenderer.MeasureText(text, Measurefont).Width; - - //static scale for optimal fit - fontSize *= 0.7f; - - using Font font = new(fam, fontSize, FontStyle.Bold, GraphicsUnit.Pixel); + float fontSize = Math.Min(Width, Height) / 1.4f; + using FontFamily fam = new FontFamily(FontFamily.GenericSansSerif.Name); + using Font font = new Font(fam, fontSize, FontStyle.Bold, GraphicsUnit.Pixel); TextContainer.DrawText(graphics, rect, 0, lineColor, false, _stringFormat, text, font); } public override bool ClickableAt(int x, int y) { - var rect = new NativeRect(Left, Top, Width, Height).Normalize(); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); if (_drawAsRectangle) { diff --git a/src/Greenshot.Editor/Drawing/Surface.cs b/src/Greenshot.Editor/Drawing/Surface.cs index 6d47a67a6..5093ed201 100644 --- a/src/Greenshot.Editor/Drawing/Surface.cs +++ b/src/Greenshot.Editor/Drawing/Surface.cs @@ -26,12 +26,8 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IO; -using System.Linq; using System.Runtime.Serialization.Formatters.Binary; -using System.ServiceModel.Security; using System.Windows.Forms; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Controls; using Greenshot.Base.Core; using Greenshot.Base.Effects; @@ -41,7 +37,7 @@ using Greenshot.Base.Interfaces.Drawing; using Greenshot.Base.Interfaces.Drawing.Adorners; using Greenshot.Editor.Configuration; using Greenshot.Editor.Drawing.Fields; -using Greenshot.Editor.Helpers; +using Greenshot.Editor.Helpers; using Greenshot.Editor.Memento; using log4net; @@ -53,6 +49,7 @@ namespace Greenshot.Editor.Drawing public sealed class Surface : Control, ISurface, INotifyPropertyChanged { private static readonly ILog LOG = LogManager.GetLogger(typeof(Surface)); + public static int Count; private static readonly CoreConfiguration conf = IniConfig.GetIniSection(); // Property to identify the Surface ID @@ -147,17 +144,6 @@ namespace Greenshot.Editor.Drawing remove => _shadowChanged -= value; } - - [NonSerialized] private int _currentDpi = 96; - /// - /// The most recent DPI value that was used - /// - public int CurrentDpi - { - get => _currentDpi; - set => _currentDpi = value; - } - /// /// inUndoRedo makes sure we don't undo/redo while in a undo/redo action /// @@ -221,7 +207,7 @@ namespace Greenshot.Editor.Drawing [NonSerialized] private IDrawableContainer _undrawnElement; /// - /// the crop container, when cropping this is set, do not serialize + /// the cropcontainer, when cropping this is set, do not serialize /// [NonSerialized] private IDrawableContainer _cropContainer; @@ -308,7 +294,7 @@ namespace Greenshot.Editor.Drawing /// /// all elements on the surface, needed with serialization /// - private IFieldAggregator _fieldAggregator; + private FieldAggregator _fieldAggregator; /// /// the cursor container, needed with serialization as we need a direct acces to it. @@ -369,7 +355,7 @@ namespace Greenshot.Editor.Drawing /// The field aggregator is that which is used to have access to all the fields inside the currently selected elements. /// e.g. used to decided if and which line thickness is shown when multiple elements are selected. /// - public IFieldAggregator FieldAggregator + public FieldAggregator FieldAggregator { get => _fieldAggregator; set => _fieldAggregator = value; @@ -467,9 +453,8 @@ namespace Greenshot.Editor.Drawing /// Adjust UI elements to the supplied DPI settings /// /// - public void AdjustToDpi(int dpi) + public void AdjustToDpi(uint dpi) { - CurrentDpi = dpi; foreach (var element in this._elements) { element.AdjustToDpi(dpi); @@ -482,6 +467,7 @@ namespace Greenshot.Editor.Drawing public Surface() { _fieldAggregator = new FieldAggregator(this); + Count++; _elements = new DrawableContainerList(_uniqueId); selectedElements = new DrawableContainerList(_uniqueId); LOG.Debug("Creating surface!"); @@ -544,8 +530,8 @@ namespace Greenshot.Editor.Drawing // check if cursor is captured, and visible if (capture.Cursor != null && capture.CursorVisible) { - var cursorRect = new NativeRect(capture.CursorLocation, capture.Cursor.Size); - var captureRect = new NativeRect(NativePoint.Empty, capture.Image.Size); + Rectangle cursorRect = new Rectangle(capture.CursorLocation, capture.Cursor.Size); + Rectangle captureRect = new Rectangle(Point.Empty, capture.Image.Size); // check if cursor is on the capture, otherwise we leave it out. if (cursorRect.IntersectsWith(captureRect)) { @@ -564,6 +550,7 @@ namespace Greenshot.Editor.Drawing { if (disposing) { + Count--; LOG.Debug("Disposing surface!"); if (_buffer != null) { @@ -724,20 +711,15 @@ namespace Greenshot.Editor.Drawing try { BinaryFormatter binaryRead = new BinaryFormatter(); - binaryRead.Binder = new BinaryFormatterHelper(); IDrawableContainerList loadedElements = (IDrawableContainerList) binaryRead.Deserialize(streamRead); loadedElements.Parent = this; - // Make sure the steplabels are sorted according to their number + // Make sure the steplabels are sorted accoring to their number _stepLabels.Sort((p1, p2) => p1.Number.CompareTo(p2.Number)); DeselectAllElements(); AddElements(loadedElements); SelectElements(loadedElements); FieldAggregator.BindElements(loadedElements); } - catch (SecurityAccessDeniedException) - { - throw; - } catch (Exception e) { LOG.Error("Error serializing elements from stream.", e); @@ -931,27 +913,6 @@ namespace Greenshot.Editor.Drawing } } - /// - /// This will help to fit the container to the surface - /// - /// IDrawableContainer - private void FitContainer(IDrawableContainer drawableContainer) - { - double factor = 1; - if (drawableContainer.Width > this.Width) - { - factor = drawableContainer.Width / (double)Width; - } - if (drawableContainer.Height > this.Height) - { - var otherFactor = drawableContainer.Height / (double)Height; - factor = Math.Max(factor, otherFactor); - } - - drawableContainer.Width = (int)(drawableContainer.Width / factor); - drawableContainer.Height = (int)(drawableContainer.Height / factor); - } - /// /// Handle the drag/drop /// @@ -959,32 +920,27 @@ namespace Greenshot.Editor.Drawing /// private void OnDragDrop(object sender, DragEventArgs e) { - NativePoint mouse = PointToClient(new NativePoint(e.X, e.Y)); + Point mouse = PointToClient(new Point(e.X, e.Y)); if (e.Data.GetDataPresent("Text")) { string possibleUrl = ClipboardHelper.GetText(e.Data); // Test if it's an url and try to download the image so we have it in the original form if (possibleUrl != null && possibleUrl.StartsWith("http")) { - var drawableContainer = NetworkHelper.DownloadImageAsDrawableContainer(possibleUrl); - if (drawableContainer != null) + using Image image = NetworkHelper.DownloadImage(possibleUrl); + if (image != null) { - drawableContainer.Left = Location.X; - drawableContainer.Top = Location.Y; - FitContainer(drawableContainer); - AddElement(drawableContainer); + AddImageContainer(image, mouse.X, mouse.Y); return; } } } - foreach (var drawableContainer in ClipboardHelper.GetDrawables(e.Data)) + foreach (Image image in ClipboardHelper.GetImages(e.Data)) { - drawableContainer.Left = mouse.X; - drawableContainer.Top = mouse.Y; - FitContainer(drawableContainer); - AddElement(drawableContainer); - mouse = mouse.Offset(10, 10); + AddImageContainer(image, mouse.X, mouse.Y); + mouse.Offset(10, 10); + image.Dispose(); } } @@ -993,17 +949,16 @@ namespace Greenshot.Editor.Drawing /// /// Auto crop the image /// - /// NativeRect with optional area to find a crop region /// true if cropped - public bool AutoCrop(NativeRect? cropArea = null) + public bool AutoCrop() { - NativeRect cropRectangle; + Rectangle cropRectangle; using (Image tmpImage = GetImageForExport()) { - cropRectangle = ImageHelper.FindAutoCropRectangle(tmpImage, conf.AutoCropDifference, cropArea); + cropRectangle = ImageHelper.FindAutoCropRectangle(tmpImage, conf.AutoCropDifference); } - if (!IsCropPossible(ref cropRectangle, CropContainer.CropModes.AutoCrop)) + if (!IsCropPossible(ref cropRectangle)) { return false; } @@ -1031,12 +986,14 @@ namespace Greenshot.Editor.Drawing public void Clear(Color newColor) { //create a blank bitmap the same size as original - Bitmap newBitmap = ImageHelper.CreateEmptyLike(Image, newColor); - if (newBitmap == null) return; - // Make undoable - MakeUndoable(new SurfaceBackgroundChangeMemento(this, null), false); - SetImage(newBitmap, false); - Invalidate(); + Bitmap newBitmap = ImageHelper.CreateEmptyLike(Image, Color.Empty); + if (newBitmap != null) + { + // Make undoable + MakeUndoable(new SurfaceBackgroundChangeMemento(this, null), false); + SetImage(newBitmap, false); + Invalidate(); + } } /// @@ -1050,7 +1007,7 @@ namespace Greenshot.Editor.Drawing Application.DoEvents(); try { - var imageRectangle = new NativeRect(NativePoint.Empty, Image.Size); + Rectangle imageRectangle = new Rectangle(Point.Empty, Image.Size); Matrix matrix = new Matrix(); Image newImage = ImageHelper.ApplyEffect(Image, effect, matrix); if (newImage != null) @@ -1061,7 +1018,7 @@ namespace Greenshot.Editor.Drawing MakeUndoable(new SurfaceBackgroundChangeMemento(this, matrix), false); SetImage(newImage, false); Invalidate(); - if (_surfaceSizeChanged != null && !imageRectangle.Equals(new NativeRect(NativePoint.Empty, newImage.Size))) + if (_surfaceSizeChanged != null && !imageRectangle.Equals(new Rectangle(Point.Empty, newImage.Size))) { _surfaceSizeChanged(this, null); } @@ -1082,48 +1039,31 @@ namespace Greenshot.Editor.Drawing /// /// check if a crop is possible /// - /// Rectangle adapted to the dimensions of the image - /// CropModes + /// /// true if this is possible - public bool IsCropPossible(ref NativeRect cropRectangle, CropContainer.CropModes cropMode) + public bool IsCropPossible(ref Rectangle cropRectangle) { - cropRectangle = new NativeRect(cropRectangle.Left, cropRectangle.Top, cropRectangle.Width, cropRectangle.Height).Normalize(); - //Fitting the rectangle to the dimensions of the image + cropRectangle = GuiRectangle.GetGuiRectangle(cropRectangle.Left, cropRectangle.Top, cropRectangle.Width, cropRectangle.Height); if (cropRectangle.Left < 0) { - cropRectangle = new NativeRect(0, cropRectangle.Top, cropRectangle.Width + cropRectangle.Left, cropRectangle.Height); + cropRectangle = new Rectangle(0, cropRectangle.Top, cropRectangle.Width + cropRectangle.Left, cropRectangle.Height); } if (cropRectangle.Top < 0) { - cropRectangle = new NativeRect(cropRectangle.Left, 0, cropRectangle.Width, cropRectangle.Height + cropRectangle.Top); + cropRectangle = new Rectangle(cropRectangle.Left, 0, cropRectangle.Width, cropRectangle.Height + cropRectangle.Top); } if (cropRectangle.Left + cropRectangle.Width > Image.Width) { - cropRectangle = new NativeRect(cropRectangle.Left, cropRectangle.Top, Image.Width - cropRectangle.Left, cropRectangle.Height); + cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, Image.Width - cropRectangle.Left, cropRectangle.Height); } if (cropRectangle.Top + cropRectangle.Height > Image.Height) { - cropRectangle = new NativeRect(cropRectangle.Left, cropRectangle.Top, cropRectangle.Width, Image.Height - cropRectangle.Top); + cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, cropRectangle.Width, Image.Height - cropRectangle.Top); } - // special condition for vertical - if(cropMode == CropContainer.CropModes.Vertical && cropRectangle.Width == Image.Width) - { - //crop out the hole image is not allowed - return false; - } - - // special condition for vertical - if (cropMode == CropContainer.CropModes.Horizontal && cropRectangle.Height == Image.Height) - { - //crop out the hole image is not allowed - return false; - } - - //condition for all other crop modes if (cropRectangle.Height > 0 && cropRectangle.Width > 0) { return true; @@ -1140,15 +1080,16 @@ namespace Greenshot.Editor.Drawing /// Message itself public void SendMessageEvent(object source, SurfaceMessageTyp messageType, string message) { - if (_surfaceMessage == null) return; - - var eventArgs = new SurfaceMessageEventArgs + if (_surfaceMessage != null) { - Message = message, - MessageType = messageType, - Surface = this - }; - _surfaceMessage(source, eventArgs); + var eventArgs = new SurfaceMessageEventArgs + { + Message = message, + MessageType = messageType, + Surface = this + }; + _surfaceMessage(source, eventArgs); + } } /// @@ -1156,15 +1097,16 @@ namespace Greenshot.Editor.Drawing /// /// Who send /// new color - private void UpdateForegroundColorEvent(object source, Color color) + public void UpdateForegroundColorEvent(object source, Color color) { - if (_foregroundColorChanged == null) return; - - var eventArgs = new SurfaceForegroundColorEventArgs + if (_foregroundColorChanged != null) { - Color = color, - }; - _foregroundColorChanged(source, eventArgs); + var eventArgs = new SurfaceForegroundColorEventArgs + { + Color = color, + }; + _foregroundColorChanged(source, eventArgs); + } } /// @@ -1172,15 +1114,16 @@ namespace Greenshot.Editor.Drawing /// /// Who send /// new color - private void UpdateBackgroundColorEvent(object source, Color color) + public void UpdateBackgroundColorEvent(object source, Color color) { - if (_lineThicknessChanged == null) return; - - var eventArgs = new SurfaceBackgroundColorEventArgs + if (_lineThicknessChanged != null) { - Color = color, - }; - _backgroundColorChanged(source, eventArgs); + var eventArgs = new SurfaceBackgroundColorEventArgs + { + Color = color, + }; + _backgroundColorChanged(source, eventArgs); + } } /// @@ -1188,15 +1131,16 @@ namespace Greenshot.Editor.Drawing /// /// Who send /// new thickness - private void UpdateLineThicknessEvent(object source, int thickness) + public void UpdateLineThicknessEvent(object source, int thickness) { - if (_lineThicknessChanged == null) return; - - var eventArgs = new SurfaceLineThicknessEventArgs + if (_lineThicknessChanged != null) { - Thickness = thickness, - }; - _lineThicknessChanged(source, eventArgs); + var eventArgs = new SurfaceLineThicknessEventArgs + { + Thickness = thickness, + }; + _lineThicknessChanged(source, eventArgs); + } } /// @@ -1204,173 +1148,61 @@ namespace Greenshot.Editor.Drawing /// /// Who send /// has shadow - private void UpdateShadowEvent(object source, bool hasShadow) + public void UpdateShadowEvent(object source, bool hasShadow) { - if (_shadowChanged == null) return; - - var eventArgs = new SurfaceShadowEventArgs - { - HasShadow = hasShadow, - }; - _shadowChanged(source, eventArgs); + if (_shadowChanged != null) + { + var eventArgs = new SurfaceShadowEventArgs + { + HasShadow = hasShadow, + }; + _shadowChanged(source, eventArgs); + } } /// /// Crop the surface /// - /// NativeRect that remains - /// bool - public bool ApplyCrop(NativeRect cropRectangle) + /// + /// + public bool ApplyCrop(Rectangle cropRectangle) { - if (!IsCropPossible(ref cropRectangle, CropContainer.CropModes.Default)) return false; - - var imageRectangle = new NativeRect(NativePoint.Empty, Image.Size); - Bitmap tmpImage; - // Make sure we have information, this this fails - try + if (IsCropPossible(ref cropRectangle)) { - tmpImage = ImageHelper.CloneArea(Image, cropRectangle, PixelFormat.DontCare); - } - catch (Exception ex) - { - ex.Data.Add("CropRectangle", cropRectangle); - ex.Data.Add("Width", Image.Width); - ex.Data.Add("Height", Image.Height); - ex.Data.Add("Pixelformat", Image.PixelFormat); - throw; - } - - var matrix = new Matrix(); - matrix.Translate(-cropRectangle.Left, -cropRectangle.Top, MatrixOrder.Append); - // Make undoable - MakeUndoable(new SurfaceBackgroundChangeMemento(this, matrix), false); - - // Do not dispose otherwise we can't undo the image! - SetImage(tmpImage, false); - _elements.Transform(matrix); - if (_surfaceSizeChanged != null && !imageRectangle.Equals(new NativeRect(NativePoint.Empty, tmpImage.Size))) - { - _surfaceSizeChanged(this, null); - } - - Invalidate(); - return true; - } - - /// - /// Crop out the surface - /// Splits the image in 3 parts(top, middle, bottom). Crop out the middle and joins top and bottom. - /// - /// NativeRect of the middle part - /// bool - private bool ApplyHorizontalCrop(NativeRect cropRectangle) - { - if (!IsCropPossible(ref cropRectangle, CropContainer.CropModes.Horizontal)) return false; - - var imageRectangle = new NativeRect(NativePoint.Empty, Image.Size); - var topRectangle = new NativeRect(0, 0, Image.Size.Width, cropRectangle.Top); - var bottomRectangle = new NativeRect(0, cropRectangle.Top + cropRectangle.Height, Image.Size.Width, Image.Size.Height - cropRectangle.Top - cropRectangle.Height); - - Bitmap newImage; - try - { - newImage = new Bitmap(Image.Size.Width, Image.Size.Height - cropRectangle.Height); - - using var graphics = Graphics.FromImage(newImage); - - var insertPositionTop = 0; - if (topRectangle.Height > 0) + Rectangle imageRectangle = new Rectangle(Point.Empty, Image.Size); + Bitmap tmpImage; + // Make sure we have information, this this fails + try { - graphics.DrawImage(Image, new NativeRect(0, insertPositionTop, topRectangle.Width, topRectangle.Height), topRectangle, GraphicsUnit.Pixel); - insertPositionTop += topRectangle.Height; + tmpImage = ImageHelper.CloneArea(Image, cropRectangle, PixelFormat.DontCare); } - if (bottomRectangle.Height > 0) + catch (Exception ex) { - graphics.DrawImage(Image, new NativeRect(0, insertPositionTop, bottomRectangle.Width, bottomRectangle.Height), bottomRectangle, GraphicsUnit.Pixel); + ex.Data.Add("CropRectangle", cropRectangle); + ex.Data.Add("Width", Image.Width); + ex.Data.Add("Height", Image.Height); + ex.Data.Add("Pixelformat", Image.PixelFormat); + throw; } - } - catch (Exception ex) - { - ex.Data.Add("CropRectangle", cropRectangle); - ex.Data.Add("Width", Image.Width); - ex.Data.Add("Height", Image.Height); - ex.Data.Add("Pixelformat", Image.PixelFormat); - throw; - } - var matrix = new Matrix(); - matrix.Translate(0, -(cropRectangle.Top + cropRectangle.Height), MatrixOrder.Append); - // Make undoable - MakeUndoable(new SurfaceBackgroundChangeMemento(this, matrix), false); - // Do not dispose otherwise we can't undo the image! - SetImage(newImage, false); + Matrix matrix = new Matrix(); + matrix.Translate(-cropRectangle.Left, -cropRectangle.Top, MatrixOrder.Append); + // Make undoable + MakeUndoable(new SurfaceBackgroundChangeMemento(this, matrix), false); - _elements.Transform(matrix); - if (_surfaceSizeChanged != null && !imageRectangle.Equals(new NativeRect(NativePoint.Empty, newImage.Size))) - { - _surfaceSizeChanged(this, null); - } - - Invalidate(); - return true; - } - - /// - /// Crop out the surface - /// Splits the image in 3 parts(left, middle, right). Crop out the middle and joins top and bottom. - /// - /// NativeRect of the middle part - /// bool - private bool ApplyVerticalCrop(NativeRect cropRectangle) - { - if (!IsCropPossible(ref cropRectangle, CropContainer.CropModes.Vertical)) return false; - - var imageRectangle = new NativeRect(NativePoint.Empty, Image.Size); - var leftRectangle = new NativeRect(0, 0, cropRectangle.Left, Image.Size.Height); - var rightRectangle = new NativeRect(cropRectangle.Left + cropRectangle.Width, 0, Image.Size.Width - cropRectangle.Width - cropRectangle.Left, Image.Size.Height); - Bitmap newImage; - try - { - newImage = new Bitmap(Image.Size.Width - cropRectangle.Width, Image.Size.Height); - - using var graphics = Graphics.FromImage(newImage); - - var insertPositionLeft = 0; - if (leftRectangle.Width > 0) + // Do not dispose otherwise we can't undo the image! + SetImage(tmpImage, false); + _elements.Transform(matrix); + if (_surfaceSizeChanged != null && !imageRectangle.Equals(new Rectangle(Point.Empty, tmpImage.Size))) { - graphics.DrawImage(Image, new NativeRect(insertPositionLeft, 0, leftRectangle.Width, leftRectangle.Height), leftRectangle , GraphicsUnit.Pixel); - insertPositionLeft += leftRectangle.Width; + _surfaceSizeChanged(this, null); } - - if (rightRectangle.Width > 0) - { - graphics.DrawImage(Image, new NativeRect(insertPositionLeft, 0, rightRectangle.Width, rightRectangle.Height), rightRectangle, GraphicsUnit.Pixel); - } - } - catch (Exception ex) - { - ex.Data.Add("CropRectangle", cropRectangle); - ex.Data.Add("Width", Image.Width); - ex.Data.Add("Height", Image.Height); - ex.Data.Add("Pixelformat", Image.PixelFormat); - throw; - } - var matrix = new Matrix(); - matrix.Translate(-cropRectangle.Left - cropRectangle.Width, 0, MatrixOrder.Append); - // Make undoable - MakeUndoable(new SurfaceBackgroundChangeMemento(this, matrix), false); - // Do not dispose otherwise we can't undo the image! - SetImage(newImage, false); - - _elements.Transform(matrix); - if (_surfaceSizeChanged != null && !imageRectangle.Equals(new NativeRect(NativePoint.Empty, newImage.Size))) - { - _surfaceSizeChanged(this, null); + Invalidate(); + return true; } - Invalidate(); - return true; + return false; } /// @@ -1402,14 +1234,15 @@ namespace Greenshot.Editor.Drawing { foreach (IAdorner adorner in drawableContainer.Adorners) { - if (!adorner.IsActive && !adorner.HitTest(mouseEventArgs.Location)) continue; - - if (adorner.Cursor != null) + if (adorner.IsActive || adorner.HitTest(mouseEventArgs.Location)) { - Cursor = adorner.Cursor; - } + if (adorner.Cursor != null) + { + Cursor = adorner.Cursor; + } - return adorner; + return adorner; + } } } @@ -1640,47 +1473,48 @@ namespace Greenshot.Editor.Drawing Cursor = DrawingMode != DrawingModes.None ? Cursors.Cross : Cursors.Default; - if (!_mouseDown) return; - - if (_mouseDownElement != null) + if (_mouseDown) { - // an element is currently dragged - _mouseDownElement.Invalidate(); - selectedElements.Invalidate(); - // Move the element - if (_mouseDownElement.Selected) + if (_mouseDownElement != null) { - if (!_isSurfaceMoveMadeUndoable) + // an element is currently dragged + _mouseDownElement.Invalidate(); + selectedElements.Invalidate(); + // Move the element + if (_mouseDownElement.Selected) { - // Only allow one undoable per mouse-down/move/up "cycle" - _isSurfaceMoveMadeUndoable = true; - selectedElements.MakeBoundsChangeUndoable(false); + if (!_isSurfaceMoveMadeUndoable) + { + // Only allow one undoable per mouse-down/move/up "cycle" + _isSurfaceMoveMadeUndoable = true; + selectedElements.MakeBoundsChangeUndoable(false); + } + + // dragged element has been selected before -> move all + selectedElements.MoveBy(currentMouse.X - _mouseStart.X, currentMouse.Y - _mouseStart.Y); + } + else + { + if (!_isSurfaceMoveMadeUndoable) + { + // Only allow one undoable per mouse-down/move/up "cycle" + _isSurfaceMoveMadeUndoable = true; + _mouseDownElement.MakeBoundsChangeUndoable(false); + } + + // dragged element is not among selected elements -> just move dragged one + _mouseDownElement.MoveBy(currentMouse.X - _mouseStart.X, currentMouse.Y - _mouseStart.Y); } - // dragged element has been selected before -> move all - selectedElements.MoveBy(currentMouse.X - _mouseStart.X, currentMouse.Y - _mouseStart.Y); + _mouseStart = currentMouse; + _mouseDownElement.Invalidate(); + _modified = true; } - else + else if (_drawingElement != null) { - if (!_isSurfaceMoveMadeUndoable) - { - // Only allow one undoable per mouse-down/move/up "cycle" - _isSurfaceMoveMadeUndoable = true; - _mouseDownElement.MakeBoundsChangeUndoable(false); - } - - // dragged element is not among selected elements -> just move dragged one - _mouseDownElement.MoveBy(currentMouse.X - _mouseStart.X, currentMouse.Y - _mouseStart.Y); + _drawingElement.HandleMouseMove(currentMouse.X, currentMouse.Y); + _modified = true; } - - _mouseStart = currentMouse; - _mouseDownElement.Invalidate(); - _modified = true; - } - else if (_drawingElement != null) - { - _drawingElement.HandleMouseMove(currentMouse.X, currentMouse.Y); - _modified = true; } } @@ -1727,9 +1561,9 @@ namespace Greenshot.Editor.Drawing return GetImage(RenderMode.EXPORT); } - private static NativeRect ZoomClipRectangle(NativeRect rc, double scale, int inflateAmount = 0) + private static Rectangle ZoomClipRectangle(Rectangle rc, double scale, int inflateAmount = 0) { - rc = new NativeRect( + rc = new Rectangle( (int) (rc.X * scale), (int) (rc.Y * scale), (int) (rc.Width * scale) + 1, @@ -1740,10 +1574,11 @@ namespace Greenshot.Editor.Drawing inflateAmount = (int) (inflateAmount * scale); } - return rc.Inflate(inflateAmount, inflateAmount); + rc.Inflate(inflateAmount, inflateAmount); + return rc; } - public void InvalidateElements(NativeRect rc) + public void InvalidateElements(Rectangle rc) => Invalidate(ZoomClipRectangle(rc, _zoomFactor, 1)); /// @@ -1754,8 +1589,8 @@ namespace Greenshot.Editor.Drawing private void SurfacePaint(object sender, PaintEventArgs paintEventArgs) { Graphics targetGraphics = paintEventArgs.Graphics; - NativeRect targetClipRectangle = paintEventArgs.ClipRectangle; - if (targetClipRectangle.IsEmpty) + Rectangle targetClipRectangle = paintEventArgs.ClipRectangle; + if (Rectangle.Empty.Equals(targetClipRectangle)) { LOG.Debug("Empty cliprectangle??"); return; @@ -1769,16 +1604,18 @@ namespace Greenshot.Editor.Drawing int verticalCorrection = targetClipRectangle.Top % (int) _zoomFactor.Numerator; if (horizontalCorrection != 0) { - targetClipRectangle = targetClipRectangle.ChangeX(-horizontalCorrection).ChangeWidth(horizontalCorrection); + targetClipRectangle.X -= horizontalCorrection; + targetClipRectangle.Width += horizontalCorrection; } if (verticalCorrection != 0) { - targetClipRectangle = targetClipRectangle.ChangeY(-verticalCorrection).ChangeHeight(verticalCorrection); + targetClipRectangle.Y -= verticalCorrection; + targetClipRectangle.Height += verticalCorrection; } } - NativeRect imageClipRectangle = ZoomClipRectangle(targetClipRectangle, _zoomFactor.Inverse(), 2); + Rectangle imageClipRectangle = ZoomClipRectangle(targetClipRectangle, _zoomFactor.Inverse(), 2); if (_elements.HasIntersectingFilters(imageClipRectangle) || _zoomFactor > Fraction.Identity) { @@ -1859,7 +1696,7 @@ namespace Greenshot.Editor.Drawing } } - private void DrawSmoothImage(Graphics targetGraphics, Image image, NativeRect imageClipRectangle) + private void DrawSmoothImage(Graphics targetGraphics, Image image, Rectangle imageClipRectangle) { var state = targetGraphics.Save(); targetGraphics.SmoothingMode = SmoothingMode.HighQuality; @@ -1872,7 +1709,7 @@ namespace Greenshot.Editor.Drawing targetGraphics.Restore(state); } - private void DrawSharpImage(Graphics targetGraphics, Image image, NativeRect imageClipRectangle) + private void DrawSharpImage(Graphics targetGraphics, Image image, Rectangle imageClipRectangle) { var state = targetGraphics.Save(); targetGraphics.SmoothingMode = SmoothingMode.None; @@ -1885,7 +1722,7 @@ namespace Greenshot.Editor.Drawing targetGraphics.Restore(state); } - private void DrawBackground(Graphics targetGraphics, NativeRect clipRectangle) + private void DrawBackground(Graphics targetGraphics, Rectangle clipRectangle) { // check if we need to draw the checkerboard if (Image.IsAlphaPixelFormat(Image.PixelFormat) && _transparencyBackgroundBrush != null) @@ -1937,7 +1774,7 @@ namespace Greenshot.Editor.Drawing element.Invalidate(); } - if (makeUndoable && element.IsUndoable) + if (makeUndoable) { MakeUndoable(new AddElementMemento(this, element), false); } @@ -1953,17 +1790,11 @@ namespace Greenshot.Editor.Drawing public void RemoveElements(IDrawableContainerList elementsToRemove, bool makeUndoable = true) { // fix potential issues with iterating a changing list - DrawableContainerList cloned = new DrawableContainerList(elementsToRemove); - + DrawableContainerList cloned = new DrawableContainerList(); + cloned.AddRange(elementsToRemove); if (makeUndoable) { - // Take all containers to make undoable - var undoableContainers = elementsToRemove.Where(c => c.IsUndoable).ToList(); - if (undoableContainers.Any()) - { - var undoableContainerList = new DrawableContainerList(undoableContainers); - MakeUndoable(new DeleteElementsMemento(this, undoableContainerList), false); - } + MakeUndoable(new DeleteElementsMemento(this, cloned), false); } SuspendLayout(); @@ -2011,7 +1842,7 @@ namespace Greenshot.Editor.Drawing Invalidate(); } - if (makeUndoable && elementToRemove is { IsUndoable: true }) + if (makeUndoable) { MakeUndoable(new DeleteElementMemento(this, elementToRemove), false); } @@ -2027,16 +1858,11 @@ namespace Greenshot.Editor.Drawing public void AddElements(IDrawableContainerList elementsToAdd, bool makeUndoable = true) { // fix potential issues with iterating a changing list - DrawableContainerList cloned = new DrawableContainerList(elementsToAdd); + DrawableContainerList cloned = new DrawableContainerList(); + cloned.AddRange(elementsToAdd); if (makeUndoable) { - // Take all containers to make undoable - var undoableContainers = elementsToAdd.Where(c => c.IsUndoable).ToList(); - if (undoableContainers.Any()) - { - var undoableContainerList = new DrawableContainerList(undoableContainers); - MakeUndoable(new AddElementsMemento(this, undoableContainerList), false); - } + MakeUndoable(new AddElementsMemento(this, cloned), false); } SuspendLayout(); @@ -2053,27 +1879,23 @@ namespace Greenshot.Editor.Drawing /// /// Returns if this surface has selected elements /// - /// bool - public bool HasSelectedElements => selectedElements is { Count: > 0 }; - - /// - /// Provides the selected elements - /// - public IDrawableContainerList SelectedElements => selectedElements; + /// + public bool HasSelectedElements => (selectedElements != null && selectedElements.Count > 0); /// /// Remove all the selected elements /// public void RemoveSelectedElements() { - if (!HasSelectedElements) return; - - // As RemoveElement will remove the element from the selectedElements list we need to copy the element to another list. - RemoveElements(selectedElements); - if (_movingElementChanged != null) + if (HasSelectedElements) { - SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs(); - _movingElementChanged(this, eventArgs); + // As RemoveElement will remove the element from the selectedElements list we need to copy the element to another list. + RemoveElements(selectedElements); + if (_movingElementChanged != null) + { + SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs(); + _movingElementChanged(this, eventArgs); + } } } @@ -2082,9 +1904,11 @@ namespace Greenshot.Editor.Drawing /// public void CutSelectedElements() { - if (!HasSelectedElements) return; - ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), selectedElements); - RemoveSelectedElements(); + if (HasSelectedElements) + { + ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), selectedElements); + RemoveSelectedElements(); + } } /// @@ -2092,90 +1916,38 @@ namespace Greenshot.Editor.Drawing /// public void CopySelectedElements() { - if (!HasSelectedElements) return; - ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), selectedElements); - } - - /// - /// This method is called to confirm/cancel. - /// Called when pressing enter or using the "check" in the editor. - /// redirects to the specialized confirm/cancel method - /// - /// bool - public void Confirm(bool confirm) - { - if (DrawingMode == DrawingModes.Crop) + if (HasSelectedElements) { - ConfirmCrop(confirm); - } - else - { - ConfirmSelectedConfirmableElements(confirm); + ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), selectedElements); } } /// - /// This method is called to confirm/cancel "confirmable" elements + /// This method is called to confirm/cancel "confirmable" elements, like the crop-container. /// Called when pressing enter or using the "check" in the editor. - ///
- /// For crop-container there is a dedicated method . ///
- /// bool + /// public void ConfirmSelectedConfirmableElements(bool confirm) { // create new collection so that we can iterate safely (selectedElements might change due with confirm/cancel) List selectedDCs = new List(selectedElements); - foreach (IDrawableContainer dc in selectedDCs.Where(c => c.IsConfirmable)) - { - throw new NotImplementedException($"No confirm/cancel defined for Container type {dc.GetType()}"); - } - - // maybe the undo button has to be enabled - _movingElementChanged?.Invoke(this, new SurfaceElementEventArgs()); - } - - /// - /// This method is called to confirm/cancel the crop-container. - /// Called when pressing enter or using the "check" in the editor. - /// - /// bool - public void ConfirmCrop(bool confirm) - { - if (_cropContainer is not CropContainer e) return; - - if (confirm && selectedElements.Count > 0) + foreach (IDrawableContainer dc in selectedDCs) { - // No undo memento for the cropcontainer itself, only for the effect - RemoveElement(_cropContainer, false); - - _ = e.GetFieldValue(FieldType.CROPMODE) switch + if (dc.Equals(_cropContainer)) { - CropContainer.CropModes.Horizontal => ApplyHorizontalCrop(_cropContainer.Bounds), - CropContainer.CropModes.Vertical => ApplyVerticalCrop(_cropContainer.Bounds), - _ => ApplyCrop(_cropContainer.Bounds) - }; + DrawingMode = DrawingModes.None; + // No undo memento for the cropcontainer itself, only for the effect + RemoveElement(_cropContainer, false); + if (confirm) + { + ApplyCrop(_cropContainer.Bounds); + } - _cropContainer.Dispose(); - _cropContainer = null; + _cropContainer.Dispose(); + _cropContainer = null; + break; + } } - else - { - RemoveCropContainer(); - } - - DrawingMode = DrawingModes.None; - - // maybe the undo button has to be enabled - _movingElementChanged?.Invoke(this, new SurfaceElementEventArgs()); - } - - public void RemoveCropContainer() - { - if (_cropContainer == null) return; - - RemoveElement(_cropContainer, false); - _cropContainer.Dispose(); - _cropContainer = null; } /// @@ -2208,33 +1980,34 @@ namespace Greenshot.Editor.Drawing // Make element(s) only move 10,10 if the surface is the same bool isSameSurface = (dcs.ParentID == _uniqueId); dcs.Parent = this; - var moveOffset = isSameSurface ? new NativePoint(10, 10) : NativePoint.Empty; + var moveOffset = isSameSurface ? new Point(10, 10) : Point.Empty; // Here a fix for bug #1475, first calculate the bounds of the complete IDrawableContainerList - NativeRect drawableContainerListBounds = NativeRect.Empty; + Rectangle drawableContainerListBounds = Rectangle.Empty; foreach (var element in dcs) { - drawableContainerListBounds = drawableContainerListBounds == NativeRect.Empty + drawableContainerListBounds = drawableContainerListBounds == Rectangle.Empty ? element.DrawingBounds - : drawableContainerListBounds.Union(element.DrawingBounds); + : Rectangle.Union(drawableContainerListBounds, element.DrawingBounds); } // And find a location inside the target surface to paste to bool containersCanFit = drawableContainerListBounds.Width < Bounds.Width && drawableContainerListBounds.Height < Bounds.Height; if (!containersCanFit) { - NativePoint containersLocation = drawableContainerListBounds.Location; + Point containersLocation = drawableContainerListBounds.Location; containersLocation.Offset(moveOffset); if (!Bounds.Contains(containersLocation)) { // Easy fix for same surface moveOffset = isSameSurface - ? new NativePoint(-10, -10) - : new NativePoint(-drawableContainerListBounds.Location.X + 10, -drawableContainerListBounds.Location.Y + 10); + ? new Point(-10, -10) + : new Point(-drawableContainerListBounds.Location.X + 10, -drawableContainerListBounds.Location.Y + 10); } } else { - NativeRect moveContainerListBounds = drawableContainerListBounds.Offset(moveOffset); + Rectangle moveContainerListBounds = drawableContainerListBounds; + moveContainerListBounds.Offset(moveOffset); // check if the element is inside if (!Bounds.Contains(moveContainerListBounds)) { @@ -2282,22 +2055,24 @@ namespace Greenshot.Editor.Drawing } else if (ClipboardHelper.ContainsImage(clipboard)) { - NativePoint pasteLocation = GetPasteLocation(0.1f, 0.1f); + Point pasteLocation = GetPasteLocation(0.1f, 0.1f); - foreach (var drawableContainer in ClipboardHelper.GetDrawables(clipboard)) + foreach (Image clipboardImage in ClipboardHelper.GetImages(clipboard)) { - if (drawableContainer == null) continue; - DeselectAllElements(); - drawableContainer.Left = pasteLocation.X; - drawableContainer.Top = pasteLocation.Y; - AddElement(drawableContainer); - SelectElement(drawableContainer); - pasteLocation = pasteLocation.Offset(10, 10); + if (clipboardImage != null) + { + DeselectAllElements(); + IImageContainer container = AddImageContainer(clipboardImage as Bitmap, pasteLocation.X, pasteLocation.Y); + SelectElement(container); + clipboardImage.Dispose(); + pasteLocation.X += 10; + pasteLocation.Y += 10; + } } } else if (ClipboardHelper.ContainsText(clipboard)) { - NativePoint pasteLocation = GetPasteLocation(0.4f, 0.4f); + Point pasteLocation = GetPasteLocation(0.4f, 0.4f); string text = ClipboardHelper.GetText(clipboard); if (text != null) @@ -2317,13 +2092,13 @@ namespace Greenshot.Editor.Drawing /// /// 0.0f for the left edge of visible area, 1.0f for the right edge of visible area. /// 0.0f for the top edge of visible area, 1.0f for the bottom edge of visible area. - private NativePoint GetPasteLocation(float horizontalRatio = 0.5f, float verticalRatio = 0.5f) + private Point GetPasteLocation(float horizontalRatio = 0.5f, float verticalRatio = 0.5f) { var point = PointToClient(MousePosition); var rc = GetVisibleRectangle(); if (!rc.Contains(point)) { - point = new NativePoint( + point = new Point( rc.Left + (int) (rc.Width * horizontalRatio), rc.Top + (int) (rc.Height * verticalRatio) ); @@ -2335,11 +2110,11 @@ namespace Greenshot.Editor.Drawing /// /// Get the rectangle bounding the part of this Surface currently visible in the editor (in surface coordinate space). /// - public NativeRect GetVisibleRectangle() + public Rectangle GetVisibleRectangle() { var bounds = Bounds; var clientArea = Parent.ClientRectangle; - return new NativeRect( + return new Rectangle( Math.Max(0, -bounds.Left), Math.Max(0, -bounds.Top), clientArea.Width, @@ -2349,13 +2124,13 @@ namespace Greenshot.Editor.Drawing /// /// Get the rectangle bounding all selected elements (in surface coordinates space), - /// or empty rectangle if nothing is selected. + /// or empty rectangle if nothing is selcted. /// - public NativeRect GetSelectionRectangle() + public Rectangle GetSelectionRectangle() => ToSurfaceCoordinates(selectedElements.DrawingBounds); /// - /// Duplicate all the selected elements + /// Duplicate all the selecteded elements /// public void DuplicateSelectedElements() { @@ -2433,23 +2208,24 @@ namespace Greenshot.Editor.Drawing /// false to skip event generation public void SelectElement(IDrawableContainer container, bool invalidate = true, bool generateEvents = true) { - if (selectedElements.Contains(container)) return; - - selectedElements.Add(container); - container.Selected = true; - FieldAggregator.BindElement(container); - if (generateEvents && _movingElementChanged != null) + if (!selectedElements.Contains(container)) { - SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs + selectedElements.Add(container); + container.Selected = true; + FieldAggregator.BindElement(container); + if (generateEvents && _movingElementChanged != null) { - Elements = selectedElements - }; - _movingElementChanged(this, eventArgs); - } + SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs + { + Elements = selectedElements + }; + _movingElementChanged(this, eventArgs); + } - if (invalidate) - { - container.Invalidate(); + if (invalidate) + { + container.Invalidate(); + } } } @@ -2494,28 +2270,28 @@ namespace Greenshot.Editor.Drawing /// false if no keys were processed public bool ProcessCmdKey(Keys k) { - if (selectedElements.Count <= 0 && k != Keys.Escape) return false; + if (selectedElements.Count <= 0) return false; bool shiftModifier = (ModifierKeys & Keys.Shift) == Keys.Shift; int px = shiftModifier ? 10 : 1; - NativePoint moveBy = NativePoint.Empty; + Point moveBy = Point.Empty; switch (k) { case Keys.Left: case Keys.Left | Keys.Shift: - moveBy = new NativePoint(-px, 0); + moveBy = new Point(-px, 0); break; case Keys.Up: case Keys.Up | Keys.Shift: - moveBy = new NativePoint(0, -px); + moveBy = new Point(0, -px); break; case Keys.Right: case Keys.Right | Keys.Shift: - moveBy = new NativePoint(px, 0); + moveBy = new Point(px, 0); break; case Keys.Down: case Keys.Down | Keys.Shift: - moveBy = new NativePoint(0, px); + moveBy = new Point(0, px); break; case Keys.PageUp: PullElementsUp(); @@ -2530,10 +2306,10 @@ namespace Greenshot.Editor.Drawing PushElementsToBottom(); break; case Keys.Enter: - Confirm(true); + ConfirmSelectedConfirmableElements(true); break; case Keys.Escape: - Confirm(false); + ConfirmSelectedConfirmableElements(false); break; case Keys.D0 | Keys.Control: case Keys.D0 | Keys.Shift | Keys.Control: @@ -2593,7 +2369,7 @@ namespace Greenshot.Editor.Drawing return false; } - if (moveBy != NativePoint.Empty) + if (!Point.Empty.Equals(moveBy)) { selectedElements.MakeBoundsChangeUndoable(true); selectedElements.MoveBy(moveBy.X, moveBy.Y); @@ -2692,7 +2468,7 @@ namespace Greenshot.Editor.Drawing return _elements.CanPushDown(selectedElements); } - private void Element_FieldChanged(object sender, FieldChangedEventArgs e) + public void Element_FieldChanged(object sender, FieldChangedEventArgs e) { selectedElements.HandleFieldChangedEvent(sender, e); } @@ -2702,7 +2478,7 @@ namespace Greenshot.Editor.Drawing return _elements.Contains(container); } - public NativePoint ToSurfaceCoordinates(NativePoint point) + public Point ToSurfaceCoordinates(Point point) { Point[] points = { @@ -2712,27 +2488,29 @@ namespace Greenshot.Editor.Drawing return points[0]; } - public NativeRect ToSurfaceCoordinates(NativeRect rc) + public Rectangle ToSurfaceCoordinates(Rectangle rc) { if (_zoomMatrix.IsIdentity) { return rc; } - - Point[] points = + else { - rc.Location, rc.Location.Offset(rc.Size.Width, rc.Size.Height) - }; - _zoomMatrix.TransformPoints(points); - return new NativeRect( - points[0].X, - points[0].Y, - points[1].X - points[0].X, - points[1].Y - points[0].Y - ); + Point[] points = + { + rc.Location, rc.Location + rc.Size + }; + _zoomMatrix.TransformPoints(points); + return new Rectangle( + points[0].X, + points[0].Y, + points[1].X - points[0].X, + points[1].Y - points[0].Y + ); + } } - public NativePoint ToImageCoordinates(NativePoint point) + public Point ToImageCoordinates(Point point) { Point[] points = { @@ -2742,24 +2520,26 @@ namespace Greenshot.Editor.Drawing return points[0]; } - public NativeRect ToImageCoordinates(NativeRect rc) + public Rectangle ToImageCoordinates(Rectangle rc) { if (_inverseZoomMatrix.IsIdentity) { return rc; } - - Point[] points = + else { - rc.Location, rc.Location.Offset(rc.Size.Width, rc.Size.Height) - }; - _inverseZoomMatrix.TransformPoints(points); - return new NativeRect( - points[0].X, - points[0].Y, - points[1].X - points[0].X, - points[1].Y - points[0].Y - ); + Point[] points = + { + rc.Location, rc.Location + rc.Size + }; + _inverseZoomMatrix.TransformPoints(points); + return new Rectangle( + points[0].X, + points[0].Y, + points[1].X - points[0].X, + points[1].Y - points[0].Y + ); + } } } } \ No newline at end of file diff --git a/src/Greenshot.Editor/Drawing/SvgContainer.cs b/src/Greenshot.Editor/Drawing/SvgContainer.cs deleted file mode 100644 index 85b8cb43d..000000000 --- a/src/Greenshot.Editor/Drawing/SvgContainer.cs +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Drawing; -using System.IO; -using Dapplo.Windows.Common.Structs; -using Greenshot.Base.Core; -using Greenshot.Base.Interfaces; -using Svg; - -namespace Greenshot.Editor.Drawing -{ - /// - /// This provides a resizable SVG container, redrawing the SVG in the size the container takes. - /// - [Serializable] - public class SvgContainer : VectorGraphicsContainer - { - private MemoryStream _svgContent; - - [NonSerialized] - private SvgDocument _svgDocument; - - public SvgContainer(Stream stream, ISurface parent) : base(parent) - { - _svgContent = new MemoryStream(); - stream.CopyTo(_svgContent); - Init(); - Size = new Size((int)_svgDocument.Width, (int)_svgDocument.Height); - } - - protected override void Init() - { - base.Init(); - // Do nothing when there is no content - if (_svgContent == null) - { - return; - } - _svgContent.Position = 0; - - _svgDocument = SvgDocument.Open(_svgContent); - } - - protected override Image ComputeBitmap() - { - //var image = ImageHelper.CreateEmpty(Width, Height, PixelFormat.Format32bppArgb, Color.Transparent); - - var image = _svgDocument.Draw(Width, Height); - - if (RotationAngle == 0) return image; - - var newImage = image.Rotate(RotationAngle); - image.Dispose(); - return newImage; - } - - public override bool HasDefaultSize => true; - - public override NativeSize DefaultSize => new NativeSize((int)_svgDocument.Width, (int)_svgDocument.Height); - } -} \ No newline at end of file diff --git a/src/Greenshot.Editor/Drawing/TextContainer.cs b/src/Greenshot.Editor/Drawing/TextContainer.cs index a49fd4bee..4d670b4e0 100644 --- a/src/Greenshot.Editor/Drawing/TextContainer.cs +++ b/src/Greenshot.Editor/Drawing/TextContainer.cs @@ -27,12 +27,10 @@ using System.Drawing.Drawing2D; using System.Drawing.Text; using System.Runtime.Serialization; using System.Windows.Forms; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Core; -using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Helpers; using Greenshot.Editor.Memento; namespace Greenshot.Editor.Drawing @@ -75,7 +73,7 @@ namespace Greenshot.Editor.Drawing { if ((text != null || newText == null) && string.Equals(text, newText)) return; - if (makeUndoable && allowUndoable && IsUndoable) + if (makeUndoable && allowUndoable) { makeUndoable = false; _parent.MakeUndoable(new TextChangeMemento(this), false); @@ -85,7 +83,7 @@ namespace Greenshot.Editor.Drawing OnPropertyChanged("Text"); } - public TextContainer(ISurface parent) : base(parent) + public TextContainer(Surface parent) : base(parent) { Init(); } @@ -156,17 +154,17 @@ namespace Greenshot.Editor.Drawing FieldChanged += TextContainer_FieldChanged; } - protected override void SwitchParent(ISurface newParent) + protected override void SwitchParent(Surface newParent) { - if (InternalParent != null) + if (_parent != null) { - InternalParent.SizeChanged -= Parent_SizeChanged; + _parent.SizeChanged -= Parent_SizeChanged; } base.SwitchParent(newParent); - if (InternalParent != null) + if (_parent != null) { - InternalParent.SizeChanged += Parent_SizeChanged; + _parent.SizeChanged += Parent_SizeChanged; } } @@ -176,7 +174,7 @@ namespace Greenshot.Editor.Drawing UpdateTextBoxFont(); } - public override void ApplyBounds(NativeRectFloat newBounds) + public override void ApplyBounds(RectangleF newBounds) { base.ApplyBounds(newBounds); UpdateTextBoxPosition(); @@ -223,10 +221,10 @@ namespace Greenshot.Editor.Drawing { ShowTextBox(); } - else if (InternalParent != null && Selected && Status == EditStatus.IDLE && _textBox.Visible) + else if (_parent != null && Selected && Status == EditStatus.IDLE && _textBox.Visible) { // Fix (workaround) for BUG-1698 - InternalParent.KeysLocked = true; + _parent.KeysLocked = true; } } @@ -291,16 +289,16 @@ namespace Greenshot.Editor.Drawing }; _textBox.DataBindings.Add("Text", this, "Text", false, DataSourceUpdateMode.OnPropertyChanged); - _textBox.LostFocus += TextBox_LostFocus; + _textBox.LostFocus += textBox_LostFocus; _textBox.KeyDown += textBox_KeyDown; } private void ShowTextBox() { - if (InternalParent != null) + if (_parent != null) { - InternalParent.KeysLocked = true; - InternalParent.Controls.Add(_textBox); + _parent.KeysLocked = true; + _parent.Controls.Add(_textBox); } EnsureTextBoxContrast(); @@ -334,18 +332,15 @@ namespace Greenshot.Editor.Drawing private void HideTextBox() { - InternalParent?.Focus(); + _parent?.Focus(); _textBox?.Hide(); - if (InternalParent == null) + if (_parent == null) { return; } - InternalParent.KeysLocked = false; - if (_textBox != null) - { - InternalParent.Controls.Remove(_textBox); - } + _parent.KeysLocked = false; + _parent.Controls.Remove(_textBox); } /// @@ -354,12 +349,12 @@ namespace Greenshot.Editor.Drawing /// public override void Transform(Matrix matrix) { - var rect = new NativeRect(Left, Top, Width, Height).Normalize(); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); int pixelsBefore = rect.Width * rect.Height; // Transform this container base.Transform(matrix); - rect = new NativeRect(Left, Top, Width, Height).Normalize(); + rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); int pixelsAfter = rect.Width * rect.Height; float factor = pixelsAfter / (float) pixelsBefore; @@ -509,8 +504,8 @@ namespace Greenshot.Editor.Drawing correction = -1; } - var absRectangle = new NativeRect(Left, Top, Width, Height).Normalize(); - var displayRectangle = Parent.ToSurfaceCoordinates(absRectangle); + Rectangle absRectangle = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + Rectangle displayRectangle = Parent.ToSurfaceCoordinates(absRectangle); _textBox.Left = displayRectangle.X + lineWidth; _textBox.Top = displayRectangle.Y + lineWidth; if (lineThickness <= 1) @@ -585,7 +580,7 @@ namespace Greenshot.Editor.Drawing } } - private void TextBox_LostFocus(object sender, EventArgs e) + private void textBox_LostFocus(object sender, EventArgs e) { // next change will be made undoable makeUndoable = true; @@ -602,7 +597,7 @@ namespace Greenshot.Editor.Drawing graphics.PixelOffsetMode = PixelOffsetMode.None; graphics.TextRenderingHint = TextRenderingHint.SystemDefault; - var rect = new NativeRect(Left, Top, Width, Height).Normalize(); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); if (Selected && rm == RenderMode.EDIT) { DrawSelectionBorder(graphics, rect); @@ -634,7 +629,7 @@ namespace Greenshot.Editor.Drawing /// /// /// - public static void DrawText(Graphics graphics, NativeRect drawingRectange, int lineThickness, Color fontColor, bool drawShadow, StringFormat stringFormat, string text, + public static void DrawText(Graphics graphics, Rectangle drawingRectange, int lineThickness, Color fontColor, bool drawShadow, StringFormat stringFormat, string text, Font font) { #if DEBUG @@ -656,10 +651,10 @@ namespace Greenshot.Editor.Drawing while (currentStep <= steps) { int offset = currentStep; - NativeRect shadowRect = new NativeRect(drawingRectange.Left + offset, drawingRectange.Top + offset, drawingRectange.Width, drawingRectange.Height).Normalize(); + Rectangle shadowRect = GuiRectangle.GetGuiRectangle(drawingRectange.Left + offset, drawingRectange.Top + offset, drawingRectange.Width, drawingRectange.Height); if (lineThickness > 0) { - shadowRect = shadowRect.Inflate(-textOffset, -textOffset); + shadowRect.Inflate(-textOffset, -textOffset); } using Brush fontBrush = new SolidBrush(Color.FromArgb(alpha, 100, 100, 100)); graphics.DrawString(text, font, fontBrush, shadowRect, stringFormat); @@ -671,7 +666,7 @@ namespace Greenshot.Editor.Drawing if (lineThickness > 0) { - drawingRectange = drawingRectange.Inflate(-textOffset, -textOffset); + drawingRectange.Inflate(-textOffset, -textOffset); } using (Brush fontBrush = new SolidBrush(fontColor)) { @@ -688,7 +683,8 @@ namespace Greenshot.Editor.Drawing public override bool ClickableAt(int x, int y) { - var r = new NativeRect(Left, Top, Width, Height).Normalize().Inflate(5, 5); + Rectangle r = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + r.Inflate(5, 5); return r.Contains(x, y); } } diff --git a/src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs b/src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs deleted file mode 100644 index 43da94c7d..000000000 --- a/src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Runtime.Serialization; -using Greenshot.Base.Interfaces; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Editor.Drawing.Adorners; - -namespace Greenshot.Editor.Drawing -{ - /// - /// This is the base container for vector graphics, these ae graphics which can resize without loss of quality. - /// Examples for this are SVG, WMF or EMF, but also graphics based on fonts (e.g. Emoji) - /// - [Serializable] - public abstract class VectorGraphicsContainer : DrawableContainer - { - private int _rotationAngle; - protected int RotationAngle - { - get => _rotationAngle; - set => _rotationAngle = value; - } - - /// - /// This is the cached version of the bitmap, pre-rendered to save performance - /// Do not serialized, it can be rebuild with other information. - /// - [NonSerialized] - private Image _cachedImage; - - /// - /// Constructor takes care of calling Init - /// - /// ISurface - public VectorGraphicsContainer(ISurface parent) : base(parent) - { - Init(); - } - - /// - /// Make sure Init is called after deserializing - /// - /// StreamingContext - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - Init(); - } - - /// - /// Init is called after creating the instance, and from OnDeserialized - /// This is the place to generate your adorners - /// - protected virtual void Init() - { - // Check if the adorners are already defined! - if (Adorners.Count > 0) - { - return; - } - - Adorners.Add(new ResizeAdorner(this, Positions.TopLeft)); - Adorners.Add(new ResizeAdorner(this, Positions.TopRight)); - Adorners.Add(new ResizeAdorner(this, Positions.BottomLeft)); - Adorners.Add(new ResizeAdorner(this, Positions.BottomRight)); - } - - /// - /// The bulk of the clean-up code is implemented in Dispose(bool) - /// This Dispose is called from the Dispose and the Destructor. - /// When disposing==true all non-managed resources should be freed too! - /// - /// - - protected override void Dispose(bool disposing) - { - if (disposing) - { - ResetCachedBitmap(); - } - - base.Dispose(disposing); - } - - /// - public override void Transform(Matrix matrix) - { - RotationAngle += CalculateAngle(matrix); - RotationAngle %= 360; - - ResetCachedBitmap(); - - base.Transform(matrix); - } - - /// - public override void Draw(Graphics graphics, RenderMode rm) - { - if (_cachedImage != null && _cachedImage.Size != Bounds.Size) - { - ResetCachedBitmap(); - } - - _cachedImage ??= ComputeBitmap(); - if (_cachedImage == null) - { - return; - } - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - - - graphics.DrawImage(_cachedImage, Bounds); - } - - /// - /// Implement this to compute the new bitmap according to the size of the container - /// - /// Image - protected abstract Image ComputeBitmap(); - - /// - /// Dispose of the cached bitmap, forcing the code to regenerate it - /// - protected void ResetCachedBitmap() - { - _cachedImage?.Dispose(); - _cachedImage = null; - } - } -} \ No newline at end of file diff --git a/src/Greenshot.Editor/EditorInitialize.cs b/src/Greenshot.Editor/EditorInitialize.cs deleted file mode 100644 index c1ee27db8..000000000 --- a/src/Greenshot.Editor/EditorInitialize.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using Greenshot.Base.Core; -using Greenshot.Base.Interfaces; -using Greenshot.Editor.FileFormatHandlers; - -namespace Greenshot.Editor -{ - public static class EditorInitialize - { - public static void Initialize() - { - SimpleServiceProvider.Current.AddService( - // All generic things, like gif, png, jpg etc. - new DefaultFileFormatHandler(), - // Greenshot format - new GreenshotFileFormatHandler(), - // For .svg support - new SvgFileFormatHandler(), - // For clipboard support - new DibFileFormatHandler(), - // .ico - new IconFileFormatHandler(), - // EMF & WMF - new MetaFileFormatHandler(), - // JPG XR - new WpfFileFormatHandler() - ); - } - } -} diff --git a/src/Greenshot.Editor/FileFormatHandlers/AbstractFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/AbstractFileFormatHandler.cs deleted file mode 100644 index fff576267..000000000 --- a/src/Greenshot.Editor/FileFormatHandlers/AbstractFileFormatHandler.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System.Collections.Generic; -using System.Drawing; -using System.IO; -using Greenshot.Base.Interfaces; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Base.Interfaces.Plugin; -using Greenshot.Editor.Drawing; - -namespace Greenshot.Editor.FileFormatHandlers -{ - public abstract class AbstractFileFormatHandler : IFileFormatHandler - { - /// - public IDictionary> SupportedExtensions { get; } = new Dictionary>(); - - /// - public virtual int PriorityFor(FileFormatHandlerActions fileFormatHandlerAction, string extension) - { - return 0; - } - - public abstract bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null); - - public abstract bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap); - - /// - /// Default implementation taking the TryLoadFromStream image and placing it in an ImageContainer - /// - /// Stream - /// string - /// ISurface - /// IEnumerable{IDrawableContainer} - public virtual IEnumerable LoadDrawablesFromStream(Stream stream, string extension, ISurface parent = null) - { - if (TryLoadFromStream(stream, extension, out var bitmap)) - { - var imageContainer = new ImageContainer(parent) - { - Image = bitmap - }; - yield return imageContainer; - } - } - } -} diff --git a/src/Greenshot.Editor/FileFormatHandlers/DefaultFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/DefaultFileFormatHandler.cs deleted file mode 100644 index 115b5328f..000000000 --- a/src/Greenshot.Editor/FileFormatHandlers/DefaultFileFormatHandler.cs +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Linq; -using Greenshot.Base.Core; -using Greenshot.Base.Interfaces; -using Greenshot.Base.Interfaces.Plugin; -using log4net; - -namespace Greenshot.Editor.FileFormatHandlers -{ - /// - /// This is the default .NET bitmap file format handler - /// - public class DefaultFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler - { - private static readonly ILog Log = LogManager.GetLogger(typeof(DefaultFileFormatHandler)); - private readonly IReadOnlyCollection _ourExtensions = new[] { ".png", ".bmp", ".gif", ".jpg", ".jpeg", ".tiff", ".tif" }; - public DefaultFileFormatHandler() - { - SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions; - SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions; - SupportedExtensions[FileFormatHandlerActions.SaveToStream] = _ourExtensions; - } - - /// - public override bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) - { - ImageFormat imageFormat = extension switch - { - ".png" => ImageFormat.Png, - ".bmp" => ImageFormat.Bmp, - ".gif" => ImageFormat.Gif, - ".jpg" => ImageFormat.Jpeg, - ".jpeg" => ImageFormat.Jpeg, - ".tiff" => ImageFormat.Tiff, - ".tif" => ImageFormat.Tiff, - _ => null - }; - - if (imageFormat == null) - { - return false; - } - surfaceOutputSettings ??= new SurfaceOutputSettings(); - var imageEncoder = ImageCodecInfo.GetImageEncoders().FirstOrDefault(ie => ie.FilenameExtension.ToLowerInvariant().Contains(extension)); - if (imageEncoder == null) - { - return false; - } - EncoderParameters parameters = new EncoderParameters(1) - { - Param = - { - [0] = new EncoderParameter(Encoder.Quality, surfaceOutputSettings.JPGQuality) - } - }; - // For those images which are with Alpha, but the format doesn't support this, change it to 24bpp - if (imageFormat.Guid == ImageFormat.Jpeg.Guid && Image.IsAlphaPixelFormat(bitmap.PixelFormat)) - { - var nonAlphaImage = ImageHelper.Clone(bitmap, PixelFormat.Format24bppRgb); - try - { - // Set that this file was written by Greenshot - nonAlphaImage.AddTag(); - } - catch (Exception ex) - { - Log.Warn("Couldn't set 'software used' tag on image.", ex); - } - - try - { - nonAlphaImage.Save(destination, imageEncoder, parameters); - } - catch (Exception ex) - { - Log.Error("Couldn't save image: ", ex); - } - finally - { - nonAlphaImage.Dispose(); - } - } - else - { - // Set that this file was written by Greenshot - bitmap.AddTag(); - bitmap.Save(destination, imageEncoder, parameters); - } - - return true; - } - - /// - public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) - { - try - { - using var tmpImage = Image.FromStream(stream, true, true); - bitmap = ImageHelper.Clone(tmpImage, PixelFormat.DontCare); - - return true; - } - catch (Exception ex) - { - Log.Error("Couldn't load image: ", ex); - } - - bitmap = null; - return false; - } - } -} diff --git a/src/Greenshot.Editor/FileFormatHandlers/DibFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/DibFileFormatHandler.cs deleted file mode 100644 index b14a33bd0..000000000 --- a/src/Greenshot.Editor/FileFormatHandlers/DibFileFormatHandler.cs +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Runtime.InteropServices; -using Dapplo.Windows.Common.Structs; -using Dapplo.Windows.Gdi32.Enums; -using Dapplo.Windows.Gdi32.Structs; -using Greenshot.Base.Core; -using Greenshot.Base.Interfaces; -using Greenshot.Base.Interfaces.Plugin; -using log4net; - -namespace Greenshot.Editor.FileFormatHandlers -{ - /// - /// This handles creating a DIB (Device Independent Bitmap) on the clipboard - /// - public class DibFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler - { - private const double DpiToPelsPerMeter = 39.3701; - private static readonly ILog Log = LogManager.GetLogger(typeof(DibFileFormatHandler)); - - private readonly IReadOnlyCollection _ourExtensions = new[] { ".dib", ".format17", ".deviceindependentbitmap" }; - - public DibFileFormatHandler() - { - SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions; - SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions; - SupportedExtensions[FileFormatHandlerActions.SaveToStream] = _ourExtensions; - } - - /// - public override bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) - { - var dibBytes = ConvertToDib(bitmap); - destination.Write(dibBytes, 0, dibBytes.Length); - return true; - } - - /// - public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) - { - byte[] dibBuffer = new byte[stream.Length]; - _ = stream.Read(dibBuffer, 0, dibBuffer.Length); - var infoHeader = BinaryStructHelper.FromByteArray(dibBuffer); - if (!infoHeader.IsDibV5) - { - Log.InfoFormat("Using special DIB - /// Converts the Bitmap to a Device Independent Bitmap format of type BITFIELDS. - /// - /// Bitmap to convert to DIB - /// byte{} with the image converted to DIB - private static byte[] ConvertToDib(Bitmap sourceBitmap) - { - if (sourceBitmap == null) throw new ArgumentNullException(nameof(sourceBitmap)); - - var area = new NativeRect(0, 0, sourceBitmap.Width, sourceBitmap.Height); - - // If the supplied format doesn't match 32bpp, we need to convert it first, and dispose the new bitmap afterwards - bool needsDisposal = false; - if (sourceBitmap.PixelFormat != PixelFormat.Format32bppArgb) - { - needsDisposal = true; - var clonedImage = ImageHelper.CreateEmptyLike(sourceBitmap, Color.Transparent, PixelFormat.Format32bppArgb); - using (var graphics = Graphics.FromImage(clonedImage)) - { - graphics.DrawImage(sourceBitmap, area); - } - sourceBitmap = clonedImage; - } - - // All the pixels take this many bytes: - var bitmapSize = 4 * sourceBitmap.Width * sourceBitmap.Height; - // The bitmap info hear takes this many bytes: - var bitmapInfoHeaderSize = Marshal.SizeOf(typeof(BitmapInfoHeader)); - // The bitmap info size is the header + 3 RGBQUADs - var bitmapInfoSize = bitmapInfoHeaderSize + 3 * Marshal.SizeOf(typeof(RgbQuad)); - - // Create a byte [] to contain the complete DIB (with .NET 5 and upwards, we could write the pixels directly to a stream) - var fullBmpBytes = new byte[bitmapInfoSize + bitmapSize]; - // Get a span for this, this simplifies the code a bit - var fullBmpSpan = fullBmpBytes.AsSpan(); - // Cast the span to be of type BITMAPINFOHEADER so we can assign values - // TODO: in .NET 6 we could do a AsRef, and even write to a stream directly - var bitmapInfoHeader = MemoryMarshal.Cast(fullBmpSpan); - - // Fill up the bitmap info header - bitmapInfoHeader[0].Size = (uint)bitmapInfoHeaderSize; - bitmapInfoHeader[0].Width = sourceBitmap.Width; - bitmapInfoHeader[0].Height = sourceBitmap.Height; - bitmapInfoHeader[0].Planes = 1; - bitmapInfoHeader[0].BitCount = 32; - bitmapInfoHeader[0].Compression = BitmapCompressionMethods.BI_BITFIELDS; - bitmapInfoHeader[0].SizeImage = (uint)bitmapSize; - bitmapInfoHeader[0].XPelsPerMeter = (int)(sourceBitmap.HorizontalResolution * DpiToPelsPerMeter); - bitmapInfoHeader[0].YPelsPerMeter = (int)(sourceBitmap.VerticalResolution * DpiToPelsPerMeter); - - // Specify the color masks applied to the Int32 pixel value to get the R, G and B values. - var rgbQuads = MemoryMarshal.Cast(fullBmpSpan.Slice(Marshal.SizeOf(typeof(BitmapInfoHeader)))); - rgbQuads[0].Red = 255; - rgbQuads[1].Green = 255; - rgbQuads[2].Blue = 255; - - // Now copy the lines, in reverse (bmp is upside down) to the byte array - var sourceBitmapData = sourceBitmap.LockBits(area, ImageLockMode.ReadOnly, sourceBitmap.PixelFormat); - try - { - // Get a span for the real bitmap bytes, which starts after the bitmapinfo (header + 3xRGBQuad) - var bitmapSpan = fullBmpSpan.Slice(bitmapInfoSize); - // Make sure we also have a span to copy from, by taking the pointer from the locked bitmap - Span bitmapSourceSpan; - unsafe - { - bitmapSourceSpan = new Span(sourceBitmapData.Scan0.ToPointer(), sourceBitmapData.Stride * sourceBitmapData.Height); - } - - // Loop over all the bitmap lines - for (int destinationY = 0; destinationY < sourceBitmap.Height; destinationY++) - { - // Calculate the y coordinate for the bottom up. (flipping the image) - var sourceY = (sourceBitmap.Height - 1) - destinationY; - // Make a Span for the source bitmap pixels - var sourceLine = bitmapSourceSpan.Slice(sourceBitmapData.Stride * sourceY, 4 * sourceBitmap.Width); - // Make a Span for the destination dib pixels - var destinationLine = bitmapSpan.Slice(destinationY * 4 * sourceBitmap.Width); - sourceLine.CopyTo(destinationLine); - } - } - finally - { - sourceBitmap.UnlockBits(sourceBitmapData); - } - - // If we created a new bitmap, we need to dispose this - if (needsDisposal) - { - sourceBitmap.Dispose(); - } - return fullBmpBytes; - } - } -} diff --git a/src/Greenshot.Editor/FileFormatHandlers/GreenshotFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/GreenshotFileFormatHandler.cs deleted file mode 100644 index 0b956c001..000000000 --- a/src/Greenshot.Editor/FileFormatHandlers/GreenshotFileFormatHandler.cs +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Reflection; -using System.Text; -using Greenshot.Base.Core; -using Greenshot.Base.Interfaces; -using Greenshot.Base.Interfaces.Plugin; -using log4net; - -namespace Greenshot.Editor.FileFormatHandlers -{ - public class GreenshotFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler - { - private static readonly ILog Log = LogManager.GetLogger(typeof(GreenshotFileFormatHandler)); - private readonly IReadOnlyCollection _ourExtensions = new [] { ".greenshot" }; - public GreenshotFileFormatHandler() - { - SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions; - SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions; - SupportedExtensions[FileFormatHandlerActions.SaveToStream] = _ourExtensions; - } - - public override bool TrySaveToStream(Bitmap bitmap, Stream stream, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) - { - if (surface == null) - { - return false; - } - - try - { - bitmap.Save(stream, ImageFormat.Png); - using MemoryStream tmpStream = new MemoryStream(); - long bytesWritten = surface.SaveElementsToStream(tmpStream); - using BinaryWriter writer = new BinaryWriter(tmpStream); - writer.Write(bytesWritten); - Version v = Assembly.GetExecutingAssembly().GetName().Version; - byte[] marker = Encoding.ASCII.GetBytes($"Greenshot{v.Major:00}.{v.Minor:00}"); - writer.Write(marker); - tmpStream.WriteTo(stream); - return true; - } - catch (Exception ex) - { - Log.Error("Couldn't save surface as .greenshot: ", ex); - } - - return false; - } - - public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) - { - try - { - var surface = LoadSurface(stream); - bitmap = (Bitmap)surface.GetImageForExport(); - return true; - } - catch (Exception ex) - { - Log.Error("Couldn't load .greenshot: ", ex); - } - bitmap = null; - return false; - } - - private ISurface LoadSurface(Stream surfaceFileStream) - { - var returnSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); - Bitmap captureBitmap; - - // Fixed problem that the bitmap stream is disposed... by Cloning the image - // This also ensures the bitmap is correctly created - using (Image tmpImage = Image.FromStream(surfaceFileStream, true, true)) - { - Log.DebugFormat("Loaded capture from .greenshot file with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat); - captureBitmap = ImageHelper.Clone(tmpImage) as Bitmap; - } - - // Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor) - const int markerSize = 14; - surfaceFileStream.Seek(-markerSize, SeekOrigin.End); - using (var streamReader = new StreamReader(surfaceFileStream)) - { - var greenshotMarker = streamReader.ReadToEnd(); - if (!greenshotMarker.StartsWith("Greenshot")) - { - throw new ArgumentException("Stream is not a Greenshot file!"); - } - - Log.InfoFormat("Greenshot file format: {0}", greenshotMarker); - const int fileSizeLocation = 8 + markerSize; - surfaceFileStream.Seek(-fileSizeLocation, SeekOrigin.End); - using BinaryReader reader = new BinaryReader(surfaceFileStream); - long bytesWritten = reader.ReadInt64(); - surfaceFileStream.Seek(-(bytesWritten + fileSizeLocation), SeekOrigin.End); - returnSurface.LoadElementsFromStream(surfaceFileStream); - } - - if (captureBitmap != null) - { - returnSurface.Image = captureBitmap; - Log.InfoFormat("Information about .greenshot file: {0}x{1}-{2} Resolution {3}x{4}", captureBitmap.Width, captureBitmap.Height, captureBitmap.PixelFormat, captureBitmap.HorizontalResolution, captureBitmap.VerticalResolution); - } - - return returnSurface; - } - } -} diff --git a/src/Greenshot.Editor/FileFormatHandlers/IconFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/IconFileFormatHandler.cs deleted file mode 100644 index d2769d5f0..000000000 --- a/src/Greenshot.Editor/FileFormatHandlers/IconFileFormatHandler.cs +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using Greenshot.Base.Core; -using Greenshot.Base.Interfaces; -using Greenshot.Base.Interfaces.Plugin; -using log4net; - -namespace Greenshot.Editor.FileFormatHandlers -{ - /// - /// THis is the .ico format handler - /// - public class IconFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler - { - private static readonly ILog Log = LogManager.GetLogger(typeof(IconFileFormatHandler)); - - private readonly IReadOnlyCollection _ourExtensions = new[] { ".ico" }; - - public IconFileFormatHandler() - { - SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions; - SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions; - SupportedExtensions[FileFormatHandlerActions.SaveToStream] = _ourExtensions; - } - - public override bool TrySaveToStream(Bitmap bitmap, Stream stream, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) - { - IList images = new List - { - bitmap - }; - - var binaryWriter = new BinaryWriter(stream); - // - // ICONDIR structure - // - binaryWriter.Write((short)0); // reserved - binaryWriter.Write((short)1); // image type (icon) - binaryWriter.Write((short)images.Count); // number of images - - IList imageSizes = new List(); - IList encodedImages = new List(); - foreach (var image in images) - { - // Pick the best fit - var sizes = new[] - { - 16, 32, 48 - }; - int size = 256; - foreach (var possibleSize in sizes) - { - if (image.Width <= possibleSize && image.Height <= possibleSize) - { - size = possibleSize; - break; - } - } - - var imageStream = new MemoryStream(); - if (image.Width == size && image.Height == size) - { - using var clonedImage = ImageHelper.Clone(image, PixelFormat.Format32bppArgb); - clonedImage.Save(imageStream, ImageFormat.Png); - imageSizes.Add(new Size(size, size)); - } - else - { - // Resize to the specified size, first make sure the image is 32bpp - using var clonedImage = ImageHelper.Clone(image, PixelFormat.Format32bppArgb); - using var resizedImage = ImageHelper.ResizeImage(clonedImage, true, true, Color.Empty, size, size, null); - resizedImage.Save(imageStream, ImageFormat.Png); - imageSizes.Add(resizedImage.Size); - } - - imageStream.Seek(0, SeekOrigin.Begin); - encodedImages.Add(imageStream); - } - - // - // ICONDIRENTRY structure - // - const int iconDirSize = 6; - const int iconDirEntrySize = 16; - - var offset = iconDirSize + (images.Count * iconDirEntrySize); - for (int i = 0; i < images.Count; i++) - { - var imageSize = imageSizes[i]; - // Write the width / height, 0 means 256 - binaryWriter.Write(imageSize.Width == 256 ? (byte)0 : (byte)imageSize.Width); - binaryWriter.Write(imageSize.Height == 256 ? (byte)0 : (byte)imageSize.Height); - binaryWriter.Write((byte)0); // no pallete - binaryWriter.Write((byte)0); // reserved - binaryWriter.Write((short)0); // no color planes - binaryWriter.Write((short)32); // 32 bpp - binaryWriter.Write((int)encodedImages[i].Length); // image data length - binaryWriter.Write(offset); - offset += (int)encodedImages[i].Length; - } - - binaryWriter.Flush(); - // - // Write image data - // - foreach (var encodedImage in encodedImages) - { - encodedImage.WriteTo(stream); - encodedImage.Dispose(); - } - - // TODO: Implement this - return true; - } - - public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) - { - _ = stream.Seek(0, SeekOrigin.Current); - - // Icon logic, try to get the Vista icon, else the biggest possible - try - { - using Image tmpImage = ExtractVistaIcon(stream); - if (tmpImage != null) - { - bitmap = ImageHelper.Clone(tmpImage, PixelFormat.Format32bppArgb); - return true; - } - } - catch (Exception vistaIconException) - { - Log.Warn("Can't read icon", vistaIconException); - } - - try - { - // No vista icon, try normal icon - stream.Position = stream.Seek(0, SeekOrigin.Begin); - // We create a copy of the bitmap, so everything else can be disposed - using Icon tmpIcon = new Icon(stream, new Size(1024, 1024)); - using Image tmpImage = tmpIcon.ToBitmap(); - bitmap = ImageHelper.Clone(tmpImage, PixelFormat.Format32bppArgb); - return true; - } - catch (Exception iconException) - { - Log.Warn("Can't read icon", iconException); - } - - bitmap = null; - return false; - } - - /// - /// Based on: https://www.codeproject.com/KB/cs/IconExtractor.aspx - /// And a hint from: https://www.codeproject.com/KB/cs/IconLib.aspx - /// - /// Stream with the icon information - /// Bitmap with the Vista Icon (256x256) - private static Bitmap ExtractVistaIcon(Stream iconStream) - { - const int sizeIconDir = 6; - const int sizeIconDirEntry = 16; - Bitmap bmpPngExtracted = null; - try - { - byte[] srcBuf = new byte[iconStream.Length]; - // TODO: Check if there is a need to process the result - _ = iconStream.Read(srcBuf, 0, (int)iconStream.Length); - int iCount = BitConverter.ToInt16(srcBuf, 4); - for (int iIndex = 0; iIndex < iCount; iIndex++) - { - int iWidth = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex]; - int iHeight = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex + 1]; - if (iWidth != 0 || iHeight != 0) continue; - - int iImageSize = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 8); - int iImageOffset = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 12); - using MemoryStream destStream = new MemoryStream(); - destStream.Write(srcBuf, iImageOffset, iImageSize); - destStream.Seek(0, SeekOrigin.Begin); - bmpPngExtracted = new Bitmap(destStream); // This is PNG! :) - break; - } - } - catch - { - return null; - } - - return bmpPngExtracted; - } - } -} diff --git a/src/Greenshot.Editor/FileFormatHandlers/MetaFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/MetaFileFormatHandler.cs deleted file mode 100644 index 04c3825b3..000000000 --- a/src/Greenshot.Editor/FileFormatHandlers/MetaFileFormatHandler.cs +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using Greenshot.Base.Core; -using Greenshot.Base.Interfaces; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Base.Interfaces.Plugin; -using Greenshot.Editor.Drawing; - -namespace Greenshot.Editor.FileFormatHandlers -{ - /// - /// This handles the Windows metafile files - /// - public class MetaFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler - { - private readonly IReadOnlyCollection _ourExtensions = new[] { ".wmf", ".emf" }; - - public MetaFileFormatHandler() - { - SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions; - SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions; - } - - /// - public override bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) - { - return false; - } - - /// - public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) - { - try - { - if (Image.FromStream(stream, true, true) is Metafile metaFile) - { - bitmap = ImageHelper.Clone(metaFile, PixelFormat.Format32bppArgb); - return true; - } - } - catch - { - // Ignore - } - bitmap = null; - return false; - } - - /// - public override IEnumerable LoadDrawablesFromStream(Stream stream, string extension, ISurface surface = null) - { - if (Image.FromStream(stream, true, true) is Metafile metaFile) - { - yield return new MetafileContainer(metaFile, surface); - } - } - } -} diff --git a/src/Greenshot.Editor/FileFormatHandlers/SvgFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/SvgFileFormatHandler.cs deleted file mode 100644 index 76c5e85b9..000000000 --- a/src/Greenshot.Editor/FileFormatHandlers/SvgFileFormatHandler.cs +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.IO; -using Greenshot.Base.Interfaces; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Base.Interfaces.Plugin; -using Greenshot.Editor.Drawing; -using log4net; -using Svg; - -namespace Greenshot.Editor.FileFormatHandlers -{ - /// - /// This handled the loading of SVG images to the editor - /// - public class SvgFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler - { - private static readonly ILog Log = LogManager.GetLogger(typeof(SvgFileFormatHandler)); - private readonly IReadOnlyCollection _ourExtensions = new[] { ".svg" }; - - public SvgFileFormatHandler() - { - SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions; - SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions; - } - - public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) - { - var svgDocument = SvgDocument.Open(stream); - - try - { - bitmap = svgDocument.Draw(); - return true; - } - catch (Exception ex) - { - Log.Error("Can't load SVG", ex); - } - bitmap = null; - return false; - } - - public override bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) - { - // TODO: Implement this - return false; - } - - public override IEnumerable LoadDrawablesFromStream(Stream stream, string extension, ISurface parent = null) - { - SvgContainer svgContainer = null; - try - { - svgContainer = new SvgContainer(stream, parent); - } - catch (Exception ex) - { - Log.Error("Can't load SVG", ex); - } - if (svgContainer != null) - { - yield return svgContainer; - } - } - } -} diff --git a/src/Greenshot.Editor/FileFormatHandlers/WpfFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/WpfFileFormatHandler.cs deleted file mode 100644 index 2d655bb1b..000000000 --- a/src/Greenshot.Editor/FileFormatHandlers/WpfFileFormatHandler.cs +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.IO; -using System.Linq; -using System.Windows.Media.Imaging; -using Greenshot.Base.Interfaces; -using Greenshot.Base.Core; -using Greenshot.Base.Interfaces.Plugin; -using log4net; -using Microsoft.Win32; - -namespace Greenshot.Editor.FileFormatHandlers -{ - /// - /// This is the System.Windows.Media.Imaging (WPF) file format handler, which uses WIC - /// - public class WpfFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler - { - private static readonly ILog Log = LogManager.GetLogger(typeof(WpfFileFormatHandler)); - private const string HeifDecoder = "{E9A4A80A-44FE-4DE4-8971-7150B10A5199}"; - private const string WicDecoderCategory = "{7ED96837-96F0-4812-B211-F13C24117ED3}"; - - private IReadOnlyCollection LoadFromStreamExtensions { get; } = new []{ ".jxr", ".dds", ".hdp", ".wdp", ".wmp"}; - private IReadOnlyCollection SaveToStreamExtensions { get; } = new[] { ".jxr" }; - - public WpfFileFormatHandler() - { - LoadFromStreamExtensions = LoadFromStreamExtensions.ToList().Concat(RetrieveSupportedExtensions()).OrderBy(e => e).Distinct().ToArray(); - - SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = LoadFromStreamExtensions; - SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = LoadFromStreamExtensions; - SupportedExtensions[FileFormatHandlerActions.SaveToStream] = SaveToStreamExtensions; - } - - /// - /// Detect all the formats WIC supports - /// - /// IEnumerable{string} - private IEnumerable RetrieveSupportedExtensions() - { - string baseKeyPath; - if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess) - { - baseKeyPath = "Wow6432Node\\CLSID"; - } - else - { - baseKeyPath = "CLSID"; - } - - using RegistryKey baseKey = Registry.ClassesRoot.OpenSubKey(baseKeyPath, false); - if (baseKey == null) yield break; - - var wicDecoderCategoryPath = Path.Combine(baseKeyPath, WicDecoderCategory, "instance"); - using RegistryKey categoryKey = Registry.ClassesRoot.OpenSubKey(wicDecoderCategoryPath, false); - if (categoryKey == null) - { - yield break; - } - foreach (var codecGuid in categoryKey.GetSubKeyNames()) - { - // Read the properties of the single registered decoder - using var codecKey = baseKey.OpenSubKey(codecGuid); - if (codecKey == null) continue; - - var fileExtensions = Convert.ToString(codecKey.GetValue("FileExtensions", "")).ToLowerInvariant(); - foreach (var fileExtension in fileExtensions.Split(',')) - { - yield return fileExtension; - } - } - var heifDecoderPath = Path.Combine(baseKeyPath, HeifDecoder); - - using RegistryKey heifKey = Registry.ClassesRoot.OpenSubKey(heifDecoderPath, false); - if (heifKey == null) yield break; - - yield return ".heic"; - yield return ".heif"; - } - - /// - public override bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) - { - surfaceOutputSettings ??= new SurfaceOutputSettings(); - try - { - var bitmapSource = bitmap.ToBitmapSource(); - var bitmapFrame = BitmapFrame.Create(bitmapSource); - var jpegXrEncoder = new WmpBitmapEncoder(); - jpegXrEncoder.Frames.Add(bitmapFrame); - // TODO: Support supplying a quality - jpegXrEncoder.ImageQualityLevel = surfaceOutputSettings.JPGQuality / 100f; - jpegXrEncoder.Save(destination); - return true; - } - catch (Exception ex) - { - Log.Error("Couldn't save image as JPEG XR: ", ex); - return false; - } - } - - /// - public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) - { - try - { - var bitmapDecoder = BitmapDecoder.Create(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None); - var bitmapSource = bitmapDecoder.Frames[0]; - bitmap = bitmapSource.ToBitmap(); - return true; - } - catch (Exception ex) - { - Log.Error("Couldn't load image: ", ex); - } - - bitmap = null; - return false; - } - } -} diff --git a/src/Greenshot.Editor/Forms/ColorDialog.cs b/src/Greenshot.Editor/Forms/ColorDialog.cs index 2a146012a..ce8dd4b4c 100644 --- a/src/Greenshot.Editor/Forms/ColorDialog.cs +++ b/src/Greenshot.Editor/Forms/ColorDialog.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Drawing; using System.Globalization; using System.Threading; diff --git a/src/Greenshot.Editor/Forms/ColorPickerToolStripButton.cs b/src/Greenshot.Editor/Forms/ColorPickerToolStripButton.cs index 858be9c3a..7a5123893 100644 --- a/src/Greenshot.Editor/Forms/ColorPickerToolStripButton.cs +++ b/src/Greenshot.Editor/Forms/ColorPickerToolStripButton.cs @@ -20,11 +20,10 @@ */ using System; +using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; namespace Greenshot.Editor.Forms { @@ -33,7 +32,7 @@ namespace Greenshot.Editor.Forms public class ColorPickerToolStripButton : ToolStripButton { private Color _color; - public NativePoint Offset = new NativePoint(0, 0); + public Point Offset = new Point(0, 0); public event ColorPickerEventHandler ColorPicked; private readonly ColorDialog _cd; @@ -57,24 +56,29 @@ namespace Greenshot.Editor.Forms protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); - if (_color == null) return; - // replace transparent color with selected color - Graphics g = e.Graphics; - ColorMap[] colorMap = new ColorMap[1]; - colorMap[0] = new ColorMap + if (_color != null) { - OldColor = Color.Magenta, //this.ImageTransparentColor; - NewColor = _color - }; - ImageAttributes attr = new ImageAttributes(); - attr.SetRemapTable(colorMap); - var rect = new NativeRect(0, 0, Image.Width, Image.Height); - // todo find a way to retrieve transparency offset automatically - // for now, we use the public variable Offset to define this manually - rect = rect.Offset(Offset.X, Offset.Y); - //ssif(color.Equals(Color.Transparent)) ((Bitmap)Image).MakeTransparent(Color.Magenta); - g.DrawImage(Image, rect, 0, 0, rect.Width, rect.Height, GraphicsUnit.Pixel, attr); - //this.Image.In + // replace transparent color with selected color + Graphics g = e.Graphics; + //Graphics g = Graphics.FromImage(Image); + ColorMap[] colorMap = new ColorMap[1]; + colorMap[0] = new ColorMap + { + OldColor = Color.Magenta, //this.ImageTransparentColor; + NewColor = _color + }; + ImageAttributes attr = new ImageAttributes(); + attr.SetRemapTable(colorMap); + Rectangle rect = new Rectangle(0, 0, Image.Width, Image.Height); + // todo find a way to retrieve transparency offset automatically + // for now, we use the public variable Offset to define this manually + rect.Offset(Offset.X, Offset.Y); + //Image. + Debug.WriteLine("paint!" + Text + ": " + _color); + //ssif(color.Equals(Color.Transparent)) ((Bitmap)Image).MakeTransparent(Color.Magenta); + g.DrawImage(Image, rect, 0, 0, rect.Width, rect.Height, GraphicsUnit.Pixel, attr); + //this.Image.In + } } void ToolStripButton1Click(object sender, EventArgs e) diff --git a/src/Greenshot.Editor/Forms/DropShadowSettingsForm.cs b/src/Greenshot.Editor/Forms/DropShadowSettingsForm.cs index c05852109..fcec47c95 100644 --- a/src/Greenshot.Editor/Forms/DropShadowSettingsForm.cs +++ b/src/Greenshot.Editor/Forms/DropShadowSettingsForm.cs @@ -22,7 +22,6 @@ using System; using System.Drawing; using System.Windows.Forms; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Effects; namespace Greenshot.Editor.Forms @@ -52,7 +51,7 @@ namespace Greenshot.Editor.Forms private void ButtonOK_Click(object sender, EventArgs e) { _effect.Darkness = trackBar1.Value / (float) 40; - _effect.ShadowOffset = new NativePoint((int) offsetX.Value, (int) offsetY.Value); + _effect.ShadowOffset = new Point((int) offsetX.Value, (int) offsetY.Value); _effect.ShadowSize = (int) thickness.Value; DialogResult = DialogResult.OK; } diff --git a/src/Greenshot.Editor/Forms/ImageEditorForm.Designer.cs b/src/Greenshot.Editor/Forms/ImageEditorForm.Designer.cs index c0a8f8a63..82fecf559 100644 --- a/src/Greenshot.Editor/Forms/ImageEditorForm.Designer.cs +++ b/src/Greenshot.Editor/Forms/ImageEditorForm.Designer.cs @@ -38,6 +38,7 @@ namespace Greenshot.Editor.Forms { protected override void Dispose(bool disposing) { if (disposing) { + DpiChanged -= AdjustToDpi; if (components != null) { components.Dispose(); } @@ -97,6 +98,8 @@ namespace Greenshot.Editor.Forms { this.toolStripSeparator12 = new System.Windows.Forms.ToolStripSeparator(); this.preferencesToolStripMenuItem = new GreenshotToolStripMenuItem(); this.toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator(); + this.autoCropToolStripMenuItem = new GreenshotToolStripMenuItem(); + this.toolStripSeparator17 = new System.Windows.Forms.ToolStripSeparator(); this.insert_window_toolstripmenuitem = new GreenshotToolStripMenuItem(); this.objectToolStripMenuItem = new GreenshotToolStripMenuItem(); this.addRectangleToolStripMenuItem = new GreenshotToolStripMenuItem(); @@ -141,13 +144,8 @@ namespace Greenshot.Editor.Forms { this.btnHelp = new GreenshotToolStripButton(); this.propertiesToolStrip = new ToolStripEx(); this.obfuscateModeButton = new BindableToolStripDropDownButton(); - this.cropModeButton = new BindableToolStripDropDownButton(); this.pixelizeToolStripMenuItem = new GreenshotToolStripMenuItem(); this.blurToolStripMenuItem = new GreenshotToolStripMenuItem(); - this.defaultCropModeToolStripMenuItem = new GreenshotToolStripMenuItem(); - this.verticalCropModeToolStripMenuItem = new GreenshotToolStripMenuItem(); - this.horizontalCropModeToolStripMenuItem = new GreenshotToolStripMenuItem(); - this.autoCropModeToolStripMenuItem = new GreenshotToolStripMenuItem(); this.highlightModeButton = new BindableToolStripDropDownButton(); this.textHighlightMenuItem = new GreenshotToolStripMenuItem(); this.areaHighlightMenuItem = new GreenshotToolStripMenuItem(); @@ -321,7 +319,7 @@ namespace Greenshot.Editor.Forms { // toolsToolStrip // this.toolsToolStrip.ClickThrough = true; - this.toolsToolStrip.ImageScalingSize = coreConfiguration.IconSize; + this.toolsToolStrip.ImageScalingSize = coreConfiguration.ScaledIconSize; this.toolsToolStrip.Dock = System.Windows.Forms.DockStyle.None; this.toolsToolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; this.toolsToolStrip.Renderer = new CustomToolStripProfessionalRenderer(); @@ -557,7 +555,7 @@ namespace Greenshot.Editor.Forms { // menuStrip1 // this.menuStrip1.ClickThrough = true; - this.menuStrip1.ImageScalingSize = coreConfiguration.IconSize; + this.menuStrip1.ImageScalingSize = coreConfiguration.ScaledIconSize; this.menuStrip1.Dock = System.Windows.Forms.DockStyle.Fill; this.menuStrip1.Stretch = true; this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -595,6 +593,8 @@ namespace Greenshot.Editor.Forms { this.toolStripSeparator12, this.preferencesToolStripMenuItem, this.toolStripSeparator5, + this.autoCropToolStripMenuItem, + this.toolStripSeparator17, this.insert_window_toolstripmenuitem}); this.editToolStripMenuItem.LanguageKey = "editor_edit"; this.editToolStripMenuItem.Name = "editToolStripMenuItem"; @@ -678,6 +678,16 @@ namespace Greenshot.Editor.Forms { // this.toolStripSeparator5.Name = "toolStripSeparator5"; // + // autoCropToolStripMenuItem + // + this.autoCropToolStripMenuItem.LanguageKey = "editor_autocrop"; + this.autoCropToolStripMenuItem.Name = "autoCropToolStripMenuItem"; + this.autoCropToolStripMenuItem.Click += new System.EventHandler(this.AutoCropToolStripMenuItemClick); + // + // toolStripSeparator17 + // + this.toolStripSeparator17.Name = "toolStripSeparator17"; + // // insert_window_toolstripmenuitem // this.insert_window_toolstripmenuitem.LanguageKey = "editor_insertwindow"; @@ -875,7 +885,7 @@ namespace Greenshot.Editor.Forms { // destinationsToolStrip // this.destinationsToolStrip.ClickThrough = true; - this.destinationsToolStrip.ImageScalingSize = coreConfiguration.IconSize; + this.destinationsToolStrip.ImageScalingSize = coreConfiguration.ScaledIconSize; this.destinationsToolStrip.Dock = System.Windows.Forms.DockStyle.Fill; this.destinationsToolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; this.destinationsToolStrip.Name = "toolStrip1"; @@ -1030,10 +1040,10 @@ namespace Greenshot.Editor.Forms { // propertiesToolStrip // this.propertiesToolStrip.ClickThrough = true; - this.propertiesToolStrip.ImageScalingSize = coreConfiguration.IconSize; + this.propertiesToolStrip.ImageScalingSize = coreConfiguration.ScaledIconSize; this.propertiesToolStrip.Dock = System.Windows.Forms.DockStyle.Fill; this.propertiesToolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; - this.propertiesToolStrip.MinimumSize = new System.Drawing.Size(150, coreConfiguration.IconSize.Height + 10); + this.propertiesToolStrip.MinimumSize = new System.Drawing.Size(150, coreConfiguration.ScaledIconSize.Height + 10); this.propertiesToolStrip.Name = "propertiesToolStrip"; this.propertiesToolStrip.Stretch = true; this.propertiesToolStrip.TabIndex = 2; @@ -1072,7 +1082,6 @@ namespace Greenshot.Editor.Forms { this.toolStripSeparator10, this.btnConfirm, this.btnCancel, - this.cropModeButton, this.counterLabel, this.counterUpDown}); // @@ -1089,7 +1098,6 @@ namespace Greenshot.Editor.Forms { this.obfuscateModeButton.SelectedTag = FilterContainer.PreparedFilter.BLUR; this.obfuscateModeButton.Tag = FilterContainer.PreparedFilter.BLUR; // - this.obfuscateModeButton.DropDownItemClicked += FilterPresetDropDownItemClicked; // pixelizeToolStripMenuItem // this.pixelizeToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("pixelizeToolStripMenuItem.Image"))); @@ -1103,55 +1111,6 @@ namespace Greenshot.Editor.Forms { this.blurToolStripMenuItem.LanguageKey = "editor_obfuscate_blur"; this.blurToolStripMenuItem.Name = "blurToolStripMenuItem"; this.blurToolStripMenuItem.Tag = FilterContainer.PreparedFilter.BLUR; - - // - // cropModeButton - // - this.cropModeButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; - this.cropModeButton.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.defaultCropModeToolStripMenuItem, - this.verticalCropModeToolStripMenuItem, - this.horizontalCropModeToolStripMenuItem, - this.autoCropModeToolStripMenuItem}); - this.cropModeButton.Image = ((System.Drawing.Image)(resources.GetObject("btnCrop.Image"))); - this.cropModeButton.ImageTransparentColor = System.Drawing.Color.Magenta; - this.cropModeButton.LanguageKey = "editor_crop_mode"; - this.cropModeButton.Name = "cropModeButton"; - this.cropModeButton.SelectedTag = CropContainer.CropModes.Default; - this.cropModeButton.Tag = CropContainer.CropModes.Default; - this.cropModeButton.DropDownItemClicked += CropStyleDropDownItemClicked; - // - // defaultCropStyleToolStripMenuItem - // - this.defaultCropModeToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("btnCrop.Image"))); - this.defaultCropModeToolStripMenuItem.LanguageKey = "editor_cropmode_default"; - this.defaultCropModeToolStripMenuItem.Name = "defaultCropModeToolStripMenuItem"; - this.defaultCropModeToolStripMenuItem.Tag = CropContainer.CropModes.Default; - - // - // verticalCropStyleToolStripMenuItem - // - this.verticalCropModeToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("CropVertical.Image"))); - this.verticalCropModeToolStripMenuItem.LanguageKey = "editor_cropmode_vertical"; - this.verticalCropModeToolStripMenuItem.Name = "verticalCropModeToolStripMenuItem"; - this.verticalCropModeToolStripMenuItem.Tag = CropContainer.CropModes.Vertical; - - // - // horizontalCropStyleToolStripMenuItem - // - this.horizontalCropModeToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("CropHorizontal.Image"))); - this.horizontalCropModeToolStripMenuItem.LanguageKey = "editor_cropmode_horizontal"; - this.horizontalCropModeToolStripMenuItem.Name = "horizontalCropModeToolStripMenuItem"; - this.horizontalCropModeToolStripMenuItem.Tag = CropContainer.CropModes.Horizontal; - - // - // autoCropModeToolStripMenuItem - // - this.autoCropModeToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("AutoCrop.Image"))); - this.autoCropModeToolStripMenuItem.LanguageKey = "editor_cropmode_auto"; - this.autoCropModeToolStripMenuItem.Name = "autoCropModeToolStripMenuItem"; - this.autoCropModeToolStripMenuItem.Tag = CropContainer.CropModes.AutoCrop; - // // highlightModeButton // @@ -1167,7 +1126,6 @@ namespace Greenshot.Editor.Forms { this.highlightModeButton.Name = "highlightModeButton"; this.highlightModeButton.SelectedTag = FilterContainer.PreparedFilter.TEXT_HIGHTLIGHT; this.highlightModeButton.Tag = FilterContainer.PreparedFilter.TEXT_HIGHTLIGHT; - this.highlightModeButton.DropDownItemClicked += FilterPresetDropDownItemClicked; // // textHighlightMenuItem // @@ -1275,7 +1233,6 @@ namespace Greenshot.Editor.Forms { this.fontFamilyComboBox.Padding = new System.Windows.Forms.Padding(2,0,0,2); this.fontFamilyComboBox.GotFocus += new System.EventHandler(this.ToolBarFocusableElementGotFocus); this.fontFamilyComboBox.LostFocus += new System.EventHandler(this.ToolBarFocusableElementLostFocus); - this.fontFamilyComboBox.PropertyChanged += FontPropertyChanged; // // fontSizeLabel // @@ -1916,11 +1873,6 @@ namespace Greenshot.Editor.Forms { private BindableToolStripButton btnConfirm; private GreenshotToolStripMenuItem selectAllToolStripMenuItem; private BindableToolStripDropDownButton highlightModeButton; - private BindableToolStripDropDownButton cropModeButton; - private GreenshotToolStripMenuItem defaultCropModeToolStripMenuItem; - private GreenshotToolStripMenuItem verticalCropModeToolStripMenuItem; - private GreenshotToolStripMenuItem horizontalCropModeToolStripMenuItem; - private GreenshotToolStripMenuItem autoCropModeToolStripMenuItem; private GreenshotToolStripMenuItem pixelizeToolStripMenuItem; private GreenshotToolStripMenuItem blurToolStripMenuItem; private BindableToolStripDropDownButton obfuscateModeButton; @@ -2030,6 +1982,8 @@ namespace Greenshot.Editor.Forms { private NonJumpingPanel panel1; private ToolStripColorButton btnFillColor; private ToolStripColorButton btnLineColor; + private GreenshotToolStripMenuItem autoCropToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator17; private System.Windows.Forms.ContextMenuStrip zoomMenuStrip; private System.Windows.Forms.ToolStripMenuItem zoomInMenuItem; private System.Windows.Forms.ToolStripMenuItem zoomOutMenuItem; diff --git a/src/Greenshot.Editor/Forms/ImageEditorForm.cs b/src/Greenshot.Editor/Forms/ImageEditorForm.cs index 97263d8c2..3e1be9489 100644 --- a/src/Greenshot.Editor/Forms/ImageEditorForm.cs +++ b/src/Greenshot.Editor/Forms/ImageEditorForm.cs @@ -25,15 +25,8 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IO; -using System.Linq; using System.Threading; using System.Windows.Forms; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; -using Dapplo.Windows.Dpi; -using Dapplo.Windows.Kernel32; -using Dapplo.Windows.User32; -using Dapplo.Windows.User32.Structs; using Greenshot.Base; using Greenshot.Base.Controls; using Greenshot.Base.Core; @@ -43,6 +36,8 @@ using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Base.Interfaces.Forms; +using Greenshot.Base.UnmanagedHelpers; +using Greenshot.Base.UnmanagedHelpers.Structs; using Greenshot.Editor.Configuration; using Greenshot.Editor.Destinations; using Greenshot.Editor.Drawing; @@ -54,20 +49,20 @@ using log4net; namespace Greenshot.Editor.Forms { /// - /// The ImageEditorForm is the editor for Greenshot + /// Description of ImageEditorForm. /// public partial class ImageEditorForm : EditorForm, IImageEditor { private static readonly ILog Log = LogManager.GetLogger(typeof(ImageEditorForm)); private static readonly EditorConfiguration EditorConfiguration = IniConfig.GetIniSection(); - private static readonly List IgnoreDestinations = new() + private static readonly List IgnoreDestinations = new List { nameof(WellKnownDestinations.Picker), EditorDestination.DESIGNATION }; - private static readonly List EditorList = new(); + private static readonly List EditorList = new List(); private Surface _surface; private GreenshotToolStripButton[] _toolbarButtons; @@ -83,7 +78,7 @@ namespace Greenshot.Editor.Forms // whether part of the editor controls are disabled depending on selected item(s) private bool _controlsDisabledDueToConfirmable; - // Used for tracking the mouse scroll wheel changes + // Used for tracking the mouse scrollwheel changes private DateTime _zoomStartTime = DateTime.Now; /// @@ -114,17 +109,19 @@ namespace Greenshot.Editor.Forms /// /// Adjust the icons etc to the supplied DPI settings /// - /// - /// - protected override void DpiChangedHandler(int oldDpi, int newDpi) + /// + /// DpiChangedEventArgs + private void AdjustToDpi(object sender, DpiChangedEventArgs dpiChangedEventArgs) { - var newSize = DpiCalculator.ScaleWithDpi(coreConfiguration.IconSize, newDpi); + var dpi = DpiHelper.GetDpi(Handle); + var newSize = DpiHelper.ScaleWithDpi(coreConfiguration.IconSize, dpi); toolsToolStrip.ImageScalingSize = newSize; menuStrip1.ImageScalingSize = newSize; destinationsToolStrip.ImageScalingSize = newSize; propertiesToolStrip.ImageScalingSize = newSize; propertiesToolStrip.MinimumSize = new Size(150, newSize.Height + 10); - _surface?.AdjustToDpi(newDpi); + + _surface?.AdjustToDpi(dpi); UpdateUi(); } @@ -150,6 +147,7 @@ namespace Greenshot.Editor.Forms ManualLanguageApply = true; InitializeComponent(); // Make sure we change the icon size depending on the scaling + DpiChanged += AdjustToDpi; Load += delegate { var thread = new Thread(AddDestinations) @@ -157,19 +155,21 @@ namespace Greenshot.Editor.Forms Name = "add destinations" }; thread.Start(); + + AdjustToDpi(null, null); }; // Make sure the editor is placed on the same location as the last editor was on close // But only if this still exists, else it will be reset (BUG-1812) WindowPlacement editorWindowPlacement = EditorConfiguration.GetEditorPlacement(); - NativeRect screenBounds = DisplayInfo.ScreenBounds; + Rectangle screenBounds = WindowCapture.GetScreenBounds(); if (!screenBounds.Contains(editorWindowPlacement.NormalPosition)) { EditorConfiguration.ResetEditorPlacement(); } // ReSharper disable once UnusedVariable - WindowDetails thisForm = new(Handle) + WindowDetails thisForm = new WindowDetails(Handle) { WindowPlacement = EditorConfiguration.GetEditorPlacement() }; @@ -181,23 +181,6 @@ namespace Greenshot.Editor.Forms UpdateUi(); - // Workaround: for the MouseWheel event which doesn't get to the panel - MouseWheel += PanelMouseWheel; - - // Use best fit, for those capture modes where we can get huge images - bool useBestFit = _surface.CaptureDetails.CaptureMode switch - { - CaptureMode.File => true, - CaptureMode.Clipboard => true, - CaptureMode.IE => true, - _ => false - }; - - if (useBestFit) - { - ZoomBestFitMenuItemClick(this, EventArgs.Empty); - } - // Workaround: As the cursor is (mostly) selected on the surface a funny artifact is visible, this fixes it. HideToolstripItems(); } @@ -283,6 +266,11 @@ namespace Greenshot.Editor.Forms // a smaller size than the initial panel size (as set by the forms designer) panel1.Height = 10; + fontFamilyComboBox.PropertyChanged += FontPropertyChanged; + + obfuscateModeButton.DropDownItemClicked += FilterPresetDropDownItemClicked; + highlightModeButton.DropDownItemClicked += FilterPresetDropDownItemClicked; + _toolbarButtons = new[] { btnCursor, btnRect, btnEllipse, btnText, btnLine, btnArrow, btnFreehand, btnHighlight, btnObfuscate, btnCrop, btnStepLabel, btnSpeechBubble @@ -291,6 +279,9 @@ namespace Greenshot.Editor.Forms pluginToolStripMenuItem.Visible = pluginToolStripMenuItem.DropDownItems.Count > 0; + // Workaround: for the MouseWheel event which doesn't get to the panel + MouseWheel += PanelMouseWheel; + // Make sure the value is set correctly when starting if (Surface != null) { @@ -312,8 +303,9 @@ namespace Greenshot.Editor.Forms // Loop over all items in the propertiesToolStrip foreach (ToolStripItem item in propertiesToolStrip.Items) { + var cb = item as ToolStripComboBox; // Only ToolStripComboBox that are visible - if (item is not ToolStripComboBox { Visible: true } cb) + if (cb == null || !cb.Visible) { continue; } @@ -321,7 +313,7 @@ namespace Greenshot.Editor.Forms if (cb.ComboBox == null) continue; // Calculate the rectangle - var r = new NativeRect(cb.ComboBox.Location.X - 1, cb.ComboBox.Location.Y - 1, cb.ComboBox.Size.Width + 1, cb.ComboBox.Size.Height + 1); + Rectangle r = new Rectangle(cb.ComboBox.Location.X - 1, cb.ComboBox.Location.Y - 1, cb.ComboBox.Size.Width + 1, cb.ComboBox.Size.Height + 1); // Draw the rectangle e.Graphics.DrawRectangle(cbBorderPen, r); @@ -370,7 +362,7 @@ namespace Greenshot.Editor.Forms { if (toolstripDestination.IsDynamic) { - ToolStripSplitButton destinationButton = new() + ToolStripSplitButton destinationButton = new ToolStripSplitButton { DisplayStyle = ToolStripItemDisplayStyle.Image, Size = new Size(23, 22), @@ -721,10 +713,7 @@ namespace Greenshot.Editor.Forms private void BtnCropClick(object sender, EventArgs e) { - if (_surface.DrawingMode == DrawingModes.Crop) return; - _surface.DrawingMode = DrawingModes.Crop; - InitCropMode((CropContainer.CropModes)_surface.FieldAggregator.GetField(FieldType.CROPMODE).Value); RefreshFieldControls(); } @@ -974,7 +963,7 @@ namespace Greenshot.Editor.Forms GC.Collect(); if (coreConfiguration.MinimizeWorkingSetSize) { - PsApi.EmptyWorkingSet(); + PsAPI.EmptyWorkingSet(); } } @@ -1023,9 +1012,6 @@ namespace Greenshot.Editor.Forms case Keys.C: BtnCropClick(sender, e); break; - case Keys.Z: - BtnResizeClick(sender, e); - break; } } else if (e.Modifiers.Equals(Keys.Control)) @@ -1099,8 +1085,13 @@ namespace Greenshot.Editor.Forms /// /// This is a "work-around" for the MouseWheel event which doesn't get to the panel /// - /// object - /// MouseEventArgs + /// + /// + /// + /// This is a "work-around" for the MouseWheel event which doesn't get to the panel + /// + /// + /// private void PanelMouseWheel(object sender, MouseEventArgs e) { if (System.Windows.Forms.Control.ModifierKeys.Equals(Keys.Control)) @@ -1287,7 +1278,6 @@ namespace Greenshot.Editor.Forms new BidirectionalBinding(previewQualityUpDown, "Value", _surface.FieldAggregator.GetField(FieldType.PREVIEW_QUALITY), "Value", DecimalDoublePercentageConverter.GetInstance(), NotNullValidator.GetInstance()); new BidirectionalBinding(obfuscateModeButton, "SelectedTag", _surface.FieldAggregator.GetField(FieldType.PREPARED_FILTER_OBFUSCATE), "Value"); - new BidirectionalBinding(cropModeButton, "SelectedTag", _surface.FieldAggregator.GetField(FieldType.CROPMODE), "Value"); new BidirectionalBinding(highlightModeButton, "SelectedTag", _surface.FieldAggregator.GetField(FieldType.PREPARED_FILTER_HIGHLIGHT), "Value"); new BidirectionalBinding(counterUpDown, "Value", _surface, "CounterStart", DecimalIntConverter.GetInstance(), NotNullValidator.GetInstance()); } @@ -1300,7 +1290,7 @@ namespace Greenshot.Editor.Forms propertiesToolStrip.SuspendLayout(); if (_surface.HasSelectedElements || _surface.DrawingMode != DrawingModes.None) { - var props = (FieldAggregator)_surface.FieldAggregator; + FieldAggregator props = _surface.FieldAggregator; btnFillColor.Visible = props.HasFieldValue(FieldType.FILL_COLOR); btnLineColor.Visible = props.HasFieldValue(FieldType.LINE_COLOR); lineThicknessLabel.Visible = lineThicknessUpDown.Visible = props.HasFieldValue(FieldType.LINE_THICKNESS); @@ -1317,13 +1307,12 @@ namespace Greenshot.Editor.Forms textHorizontalAlignmentButton.Visible = props.HasFieldValue(FieldType.TEXT_HORIZONTAL_ALIGNMENT); textVerticalAlignmentButton.Visible = props.HasFieldValue(FieldType.TEXT_VERTICAL_ALIGNMENT); shadowButton.Visible = props.HasFieldValue(FieldType.SHADOW); - counterLabel.Visible = counterUpDown.Visible = props.HasFieldValue(FieldType.FLAGS) && ((FieldFlag)props.GetFieldValue(FieldType.FLAGS)).HasFlag(FieldFlag.COUNTER); - - btnConfirm.Visible = btnCancel.Visible = props.HasFieldValue(FieldType.FLAGS) && ((FieldFlag) props.GetFieldValue(FieldType.FLAGS)).HasFlag(FieldFlag.CONFIRMABLE); - btnConfirm.Enabled = _surface.HasSelectedElements; + counterLabel.Visible = counterUpDown.Visible = props.HasFieldValue(FieldType.FLAGS) + && ((FieldFlag) props.GetFieldValue(FieldType.FLAGS) & FieldFlag.COUNTER) == FieldFlag.COUNTER; + btnConfirm.Visible = btnCancel.Visible = props.HasFieldValue(FieldType.FLAGS) + && ((FieldFlag) props.GetFieldValue(FieldType.FLAGS) & FieldFlag.CONFIRMABLE) == FieldFlag.CONFIRMABLE; obfuscateModeButton.Visible = props.HasFieldValue(FieldType.PREPARED_FILTER_OBFUSCATE); - cropModeButton.Visible = props.HasFieldValue(FieldType.CROPMODE); highlightModeButton.Visible = props.HasFieldValue(FieldType.PREPARED_FILTER_HIGHLIGHT); } else @@ -1361,7 +1350,7 @@ namespace Greenshot.Editor.Forms btnStepLabel.Image = icon; addCounterToolStripMenuItem.Image = icon; - FieldAggregator props = (FieldAggregator)_surface.FieldAggregator; + FieldAggregator props = _surface.FieldAggregator; // if a confirmable element is selected, we must disable most of the controls // since we demand confirmation or cancel for confirmable element if (props.HasFieldValue(FieldType.FLAGS) && ((FieldFlag) props.GetFieldValue(FieldType.FLAGS) & FieldFlag.CONFIRMABLE) == FieldFlag.CONFIRMABLE) @@ -1579,39 +1568,6 @@ namespace Greenshot.Editor.Forms Invalidate(true); } - protected void CropStyleDropDownItemClicked(object sender, ToolStripItemClickedEventArgs e) - { - InitCropMode((CropContainer.CropModes)e.ClickedItem.Tag); - - RefreshFieldControls(); - Invalidate(true); - } - - private void InitCropMode(CropContainer.CropModes mode) - { - var cropArea = _surface.Elements.FirstOrDefault(c => c is CropContainer)?.Bounds; - - _surface.DrawingMode = DrawingModes.None; - _surface.RemoveCropContainer(); - - if (mode == CropContainer.CropModes.AutoCrop) - { - if (!_surface.AutoCrop(cropArea)) - { - //not AutoCrop possible automatic switch to default crop mode - _surface.DrawingMode = DrawingModes.Crop; - _surface.FieldAggregator.GetField(FieldType.CROPMODE).Value = CropContainer.CropModes.Default; - this.cropModeButton.SelectedTag = CropContainer.CropModes.Default; - this.statusLabel.Text = Language.GetString(LangKey.editor_autocrop_not_possible); - } - } - else - { - _surface.DrawingMode = DrawingModes.Crop; - } - RefreshEditorControls(); - } - private void SelectAllToolStripMenuItemClick(object sender, EventArgs e) { _surface.SelectAllElements(); @@ -1620,14 +1576,14 @@ namespace Greenshot.Editor.Forms private void BtnConfirmClick(object sender, EventArgs e) { - _surface.Confirm(true); - RefreshEditorControls(); + _surface.ConfirmSelectedConfirmableElements(true); + RefreshFieldControls(); } private void BtnCancelClick(object sender, EventArgs e) { - _surface.Confirm(false); - RefreshEditorControls(); + _surface.ConfirmSelectedConfirmableElements(false); + RefreshFieldControls(); } private void Insert_window_toolstripmenuitemMouseEnter(object sender, EventArgs e) @@ -1673,6 +1629,14 @@ namespace Greenshot.Editor.Forms } } + private void AutoCropToolStripMenuItemClick(object sender, EventArgs e) + { + if (_surface.AutoCrop()) + { + RefreshFieldControls(); + } + } + private void AddBorderToolStripMenuItemClick(object sender, EventArgs e) { _surface.ApplyBitmapEffect(new BorderEffect()); @@ -1697,13 +1661,13 @@ namespace Greenshot.Editor.Forms /// private void ShrinkCanvasToolStripMenuItemClick(object sender, EventArgs e) { - NativeRect cropRectangle; + Rectangle cropRectangle; using (Image tmpImage = GetImageForExport()) { cropRectangle = ImageHelper.FindAutoCropRectangle(tmpImage, coreConfiguration.AutoCropDifference); } - if (_surface.IsCropPossible(ref cropRectangle, CropContainer.CropModes.AutoCrop)) + if (_surface.IsCropPossible(ref cropRectangle)) { _surface.ApplyCrop(cropRectangle); UpdateUndoRedoSurfaceDependencies(); @@ -1952,7 +1916,8 @@ namespace Greenshot.Editor.Forms private void ZoomSetValue(Fraction value) { var surface = Surface as Surface; - if (surface?.Parent is not Panel panel) + var panel = surface?.Parent as Panel; + if (panel == null) { return; } @@ -1967,8 +1932,9 @@ namespace Greenshot.Editor.Forms var size = surface.Size; if (value > Surface.ZoomFactor) // being smart on zoom-in { - var selection = surface.GetSelectionRectangle().Intersect(rc); - if (selection != NativeRect.Empty) + var selection = surface.GetSelectionRectangle(); + selection.Intersect(rc); + if (selection != Rectangle.Empty) { rc = selection; // zoom to visible part of selection } @@ -1978,12 +1944,12 @@ namespace Greenshot.Editor.Forms // - prefer top left corner to zoom-in as less disorienting for screenshots if (size.Width < rc.Width) { - rc = rc.ChangeWidth(0); + rc.Width = 0; } if (size.Height < rc.Height) { - rc = rc.ChangeHeight(0); + rc.Height = 0; } } } diff --git a/src/Greenshot.Editor/Forms/ImageEditorForm.resx b/src/Greenshot.Editor/Forms/ImageEditorForm.resx index 1c5b104fd..57fe2f2c7 100644 --- a/src/Greenshot.Editor/Forms/ImageEditorForm.resx +++ b/src/Greenshot.Editor/Forms/ImageEditorForm.resx @@ -1107,13 +1107,4 @@ 782, 17 - - ..\Resources\AutoCrop.Image.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - ..\Resources\CropHorizontal.Image.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - ..\Resources\CropVertical.Image.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - \ No newline at end of file diff --git a/src/Greenshot.Editor/Forms/MovableShowColorForm.cs b/src/Greenshot.Editor/Forms/MovableShowColorForm.cs index 416ec6407..fb7962a91 100644 --- a/src/Greenshot.Editor/Forms/MovableShowColorForm.cs +++ b/src/Greenshot.Editor/Forms/MovableShowColorForm.cs @@ -22,11 +22,7 @@ using System; using System.Drawing; using System.Windows.Forms; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; -using Dapplo.Windows.Gdi32; -using Dapplo.Windows.Gdi32.SafeHandles; -using Dapplo.Windows.User32; +using Greenshot.Base.UnmanagedHelpers; namespace Greenshot.Editor.Forms { @@ -36,7 +32,7 @@ namespace Greenshot.Editor.Forms /// public partial class MovableShowColorForm : Form { - public Color Color + public Color color { get { return preview.BackColor; } } @@ -49,8 +45,8 @@ namespace Greenshot.Editor.Forms /// /// Move the MovableShowColorForm to the specified location and display the color under the (current mouse) coordinates /// - /// NativePoint with Coordinates - public void MoveTo(NativePoint screenCoordinates) + /// Coordinates + public void MoveTo(Point screenCoordinates) { Color c = GetPixelColor(screenCoordinates); preview.BackColor = c; @@ -60,36 +56,38 @@ namespace Greenshot.Editor.Forms green.Text = string.Empty + c.G; alpha.Text = string.Empty + c.A; - NativeSize cursorSize = Cursor.Current.Size; - NativePoint hotspot = Cursor.Current.HotSpot; + Size cursorSize = Cursor.Current.Size; + Point hotspot = Cursor.Current.HotSpot; - var zoomerLocation = new NativePoint(screenCoordinates.X, screenCoordinates.Y) - .Offset(cursorSize.Width + 5 - hotspot.X, cursorSize.Height + 5 - hotspot.Y); + Point zoomerLocation = new Point(screenCoordinates.X, screenCoordinates.Y); + zoomerLocation.X += cursorSize.Width + 5 - hotspot.X; + zoomerLocation.Y += cursorSize.Height + 5 - hotspot.Y; - foreach (var displayInfo in DisplayInfo.AllDisplayInfos) + foreach (Screen screen in Screen.AllScreens) { - NativeRect screenRectangle = displayInfo.Bounds; - if (!displayInfo.Bounds.Contains(screenCoordinates)) continue; + Rectangle screenRectangle = screen.Bounds; + if (screen.Bounds.Contains(screenCoordinates)) + { + if (zoomerLocation.X < screenRectangle.X) + { + zoomerLocation.X = screenRectangle.X; + } + else if (zoomerLocation.X + Width > screenRectangle.X + screenRectangle.Width) + { + zoomerLocation.X = screenCoordinates.X - Width - 5 - hotspot.X; + } - if (zoomerLocation.X < screenRectangle.X) - { - zoomerLocation = zoomerLocation.ChangeX(screenRectangle.X); - } - else if (zoomerLocation.X + Width > screenRectangle.X + screenRectangle.Width) - { - zoomerLocation = zoomerLocation.ChangeX(screenCoordinates.X - Width - 5 - hotspot.X); - } + if (zoomerLocation.Y < screenRectangle.Y) + { + zoomerLocation.Y = screenRectangle.Y; + } + else if (zoomerLocation.Y + Height > screenRectangle.Y + screenRectangle.Height) + { + zoomerLocation.Y = screenCoordinates.Y - Height - 5 - hotspot.Y; + } - if (zoomerLocation.Y < screenRectangle.Y) - { - zoomerLocation = zoomerLocation.ChangeY(screenRectangle.Y); + break; } - else if (zoomerLocation.Y + Height > screenRectangle.Y + screenRectangle.Height) - { - zoomerLocation = zoomerLocation.ChangeY(screenCoordinates.Y - Height - 5 - hotspot.Y); - } - - break; } Location = zoomerLocation; @@ -99,14 +97,14 @@ namespace Greenshot.Editor.Forms /// /// Get the color from the pixel on the screen at "x,y" /// - /// NativePoint with the coordinates + /// Point with the coordinates /// Color at the specified screenCoordinates - private static Color GetPixelColor(NativePoint screenCoordinates) + private static Color GetPixelColor(Point screenCoordinates) { - using SafeWindowDcHandle safeWindowDcHandle = SafeWindowDcHandle.FromDesktop(); + using SafeWindowDcHandle screenDC = SafeWindowDcHandle.FromDesktop(); try { - uint pixel = Gdi32Api.GetPixel(safeWindowDcHandle, screenCoordinates.X, screenCoordinates.Y); + uint pixel = GDI32.GetPixel(screenDC, screenCoordinates.X, screenCoordinates.Y); Color color = Color.FromArgb(255, (int) (pixel & 0xFF), (int) (pixel & 0xFF00) >> 8, (int) (pixel & 0xFF0000) >> 16); return color; } diff --git a/src/Greenshot.Editor/Forms/TornEdgeSettingsForm.cs b/src/Greenshot.Editor/Forms/TornEdgeSettingsForm.cs index 46638fc6e..db29c227f 100644 --- a/src/Greenshot.Editor/Forms/TornEdgeSettingsForm.cs +++ b/src/Greenshot.Editor/Forms/TornEdgeSettingsForm.cs @@ -20,8 +20,8 @@ */ using System; +using System.Drawing; using System.Windows.Forms; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Effects; namespace Greenshot.Editor.Forms @@ -56,7 +56,7 @@ namespace Greenshot.Editor.Forms private void ButtonOK_Click(object sender, EventArgs e) { _effect.Darkness = shadowDarkness.Value / (float) 40; - _effect.ShadowOffset = new NativePoint((int) offsetX.Value, (int) offsetY.Value); + _effect.ShadowOffset = new Point((int) offsetX.Value, (int) offsetY.Value); _effect.ShadowSize = (int) thickness.Value; _effect.ToothHeight = (int) toothsize.Value; _effect.VerticalToothRange = (int) verticaltoothrange.Value; diff --git a/src/Greenshot.Editor/Greenshot.Editor.csproj b/src/Greenshot.Editor/Greenshot.Editor.csproj index 5dcd99bc1..768bdb99d 100644 --- a/src/Greenshot.Editor/Greenshot.Editor.csproj +++ b/src/Greenshot.Editor/Greenshot.Editor.csproj @@ -1,11 +1,4 @@  - - True - - - none - false - PreserveNewest diff --git a/src/Greenshot.Editor/Helpers/BinaryFormatterHelper.cs b/src/Greenshot.Editor/Helpers/BinaryFormatterHelper.cs deleted file mode 100644 index 11c7dbae6..000000000 --- a/src/Greenshot.Editor/Helpers/BinaryFormatterHelper.cs +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; -using System.ServiceModel.Security; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Editor.Drawing; -using Greenshot.Editor.Drawing.Fields; -using Greenshot.Editor.Drawing.Filters; -using log4net; -using static Greenshot.Editor.Drawing.FilterContainer; - -namespace Greenshot.Editor.Helpers -{ - /// - /// This helps to map the serialization of the old .greenshot file to the newer. - /// It also prevents misuse. - /// - internal class BinaryFormatterHelper : SerializationBinder - { - private static readonly ILog LOG = LogManager.GetLogger(typeof(BinaryFormatterHelper)); - private static readonly IDictionary TypeMapper = new Dictionary - { - {"System.Guid",typeof(Guid) }, - {"System.Drawing.Rectangle",typeof(System.Drawing.Rectangle) }, - {"System.Drawing.Point",typeof(System.Drawing.Point) }, - {"System.Drawing.Color",typeof(System.Drawing.Color) }, - {"System.Drawing.Bitmap",typeof(System.Drawing.Bitmap) }, - {"System.Drawing.Icon",typeof(System.Drawing.Icon) }, - {"System.Drawing.Size",typeof(System.Drawing.Size) }, - {"System.IO.MemoryStream",typeof(System.IO.MemoryStream) }, - {"System.Drawing.StringAlignment",typeof(System.Drawing.StringAlignment) }, - {"System.Collections.Generic.List`1[[Greenshot.Base.Interfaces.Drawing.IFieldHolder", typeof(List)}, - {"System.Collections.Generic.List`1[[Greenshot.Base.Interfaces.Drawing.IField", typeof(List)}, - {"System.Collections.Generic.List`1[[System.Drawing.Point", typeof(List)}, - {"Greenshot.Editor.Drawing.ArrowContainer", typeof(ArrowContainer) }, - {"Greenshot.Editor.Drawing.ArrowContainer+ArrowHeadCombination", typeof(ArrowContainer.ArrowHeadCombination) }, - {"Greenshot.Editor.Drawing.LineContainer", typeof(LineContainer) }, - {"Greenshot.Editor.Drawing.TextContainer", typeof(TextContainer) }, - {"Greenshot.Editor.Drawing.SpeechbubbleContainer", typeof(SpeechbubbleContainer) }, - {"Greenshot.Editor.Drawing.RectangleContainer", typeof(RectangleContainer) }, - {"Greenshot.Editor.Drawing.EllipseContainer", typeof(EllipseContainer) }, - {"Greenshot.Editor.Drawing.FreehandContainer", typeof(FreehandContainer) }, - {"Greenshot.Editor.Drawing.HighlightContainer", typeof(HighlightContainer) }, - {"Greenshot.Editor.Drawing.IconContainer", typeof(IconContainer) }, - {"Greenshot.Editor.Drawing.ObfuscateContainer", typeof(ObfuscateContainer) }, - {"Greenshot.Editor.Drawing.StepLabelContainer", typeof(StepLabelContainer) }, - {"Greenshot.Editor.Drawing.SvgContainer", typeof(SvgContainer) }, - {"Greenshot.Editor.Drawing.VectorGraphicsContainer", typeof(VectorGraphicsContainer) }, - {"Greenshot.Editor.Drawing.MetafileContainer", typeof(MetafileContainer) }, - {"Greenshot.Editor.Drawing.ImageContainer", typeof(ImageContainer) }, - {"Greenshot.Editor.Drawing.FilterContainer", typeof(FilterContainer) }, - {"Greenshot.Editor.Drawing.DrawableContainer", typeof(DrawableContainer) }, - {"Greenshot.Editor.Drawing.DrawableContainerList", typeof(DrawableContainerList) }, - {"Greenshot.Editor.Drawing.CursorContainer", typeof(CursorContainer) }, - {"Greenshot.Editor.Drawing.Filters.HighlightFilter", typeof(HighlightFilter) }, - {"Greenshot.Editor.Drawing.Filters.GrayscaleFilter", typeof(GrayscaleFilter) }, - {"Greenshot.Editor.Drawing.Filters.MagnifierFilter", typeof(MagnifierFilter) }, - {"Greenshot.Editor.Drawing.Filters.BrightnessFilter", typeof(BrightnessFilter) }, - {"Greenshot.Editor.Drawing.Filters.BlurFilter", typeof(BlurFilter) }, - {"Greenshot.Editor.Drawing.Filters.PixelizationFilter", typeof(PixelizationFilter) }, - {"Greenshot.Base.Interfaces.Drawing.IDrawableContainer", typeof(IDrawableContainer) }, - {"Greenshot.Base.Interfaces.Drawing.EditStatus", typeof(EditStatus) }, - {"Greenshot.Base.Interfaces.Drawing.IFieldHolder", typeof(IFieldHolder) }, - {"Greenshot.Base.Interfaces.Drawing.IField", typeof(IField) }, - {"Greenshot.Base.Interfaces.Drawing.FieldFlag", typeof(FieldFlag) }, - {"Greenshot.Editor.Drawing.Fields.Field", typeof(Field) }, - {"Greenshot.Editor.Drawing.Fields.FieldType", typeof(FieldType) }, - {"Greenshot.Editor.Drawing.FilterContainer+PreparedFilter", typeof(PreparedFilter) }, - }; - - /// - /// Do the type mapping - /// - /// Assembly for the type that was serialized - /// Type that was serialized - /// Type which was mapped - /// If something smells fishy - public override Type BindToType(string assemblyName, string typeName) - { - if (string.IsNullOrEmpty(typeName)) - { - return null; - } - var typeNameCommaLocation = typeName.IndexOf(","); - var comparingTypeName = typeName.Substring(0, typeNameCommaLocation > 0 ? typeNameCommaLocation : typeName.Length); - - // Correct wrong types - comparingTypeName = comparingTypeName.Replace("Greenshot.Drawing", "Greenshot.Editor.Drawing"); - comparingTypeName = comparingTypeName.Replace("Greenshot.Plugin.Drawing", "Greenshot.Base.Interfaces.Drawing"); - comparingTypeName = comparingTypeName.Replace("GreenshotPlugin.Interfaces.Drawing", "Greenshot.Base.Interfaces.Drawing"); - comparingTypeName = comparingTypeName.Replace("Greenshot.Drawing.Fields", "Greenshot.Editor.Drawing.Fields"); - comparingTypeName = comparingTypeName.Replace("Greenshot.Drawing.Filters", "Greenshot.Editor.Drawing.Filters"); - - if (TypeMapper.TryGetValue(comparingTypeName, out var returnType)) - { - LOG.Info($"Mapped {assemblyName} - {typeName} to {returnType.FullName}"); - return returnType; - } - LOG.Warn($"Unexpected Greenshot type in .greenshot file detected, maybe vulnerability attack created with ysoserial? Suspicious type: {assemblyName} - {typeName}"); - throw new SecurityAccessDeniedException($"Suspicious type in .greenshot file: {assemblyName} - {typeName}"); - } - } -} diff --git a/src/Greenshot.Editor/Helpers/GuiRectangle.cs b/src/Greenshot.Editor/Helpers/GuiRectangle.cs new file mode 100644 index 000000000..d5855afb5 --- /dev/null +++ b/src/Greenshot.Editor/Helpers/GuiRectangle.cs @@ -0,0 +1,53 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Drawing; + +namespace Greenshot.Editor.Helpers +{ + /// + /// Helper class for creating rectangles with positive dimensions, regardless of input coordinates + /// + public static class GuiRectangle + { + public static Rectangle GetGuiRectangle(int x, int y, int w, int h) + { + var rect = new Rectangle(x, y, w, h); + MakeGuiRectangle(ref rect); + return rect; + } + + public static void MakeGuiRectangle(ref Rectangle rect) + { + if (rect.Width < 0) + { + rect.X += rect.Width; + rect.Width = -rect.Width; + } + + if (rect.Height < 0) + { + rect.Y += rect.Height; + rect.Height = -rect.Height; + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Editor/Helpers/ScaleHelper.cs b/src/Greenshot.Editor/Helpers/ScaleHelper.cs index 8e4a76a13..423fdf075 100644 --- a/src/Greenshot.Editor/Helpers/ScaleHelper.cs +++ b/src/Greenshot.Editor/Helpers/ScaleHelper.cs @@ -22,8 +22,6 @@ using System; using System.Drawing; using System.Windows.Forms; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; using Greenshot.Editor.Drawing; namespace Greenshot.Editor.Helpers @@ -33,62 +31,100 @@ namespace Greenshot.Editor.Helpers ///
public static class ScaleHelper { + [Flags] + public enum ScaleOptions + { + /// + /// Default scale behavior. + /// + Default = 0x00, + + /// + /// Scale a rectangle in two our four directions, mirrored at it's center coordinates + /// + Centered = 0x01, + + /// + /// Scale a rectangle maintaining it's aspect ratio + /// + Rational = 0x02 + } + /// /// calculates the Size an element must be resized to, in order to fit another element, keeping aspect ratio /// /// the size of the element to be resized /// the target size of the element /// in case the aspect ratio of currentSize and targetSize differs: shall the scaled size fit into targetSize (i.e. that one of its dimensions is smaller - false) or vice versa (true) - /// NativeSizeFloat object indicating the width and height the element should be scaled to - public static NativeSizeFloat GetScaledSize(NativeSizeFloat currentSize, NativeSizeFloat targetSize, bool crop) + /// a new SizeF object indicating the width and height the element should be scaled to + public static SizeF GetScaledSize(SizeF currentSize, SizeF targetSize, bool crop) { float wFactor = targetSize.Width / currentSize.Width; float hFactor = targetSize.Height / currentSize.Height; float factor = crop ? Math.Max(wFactor, hFactor) : Math.Min(wFactor, hFactor); - return new NativeSizeFloat(currentSize.Width * factor, currentSize.Height * factor); + return new SizeF(currentSize.Width * factor, currentSize.Height * factor); } /// /// calculates the position of an element depending on the desired alignment within a RectangleF /// - /// NativeRectFloat the bounds of the element to be aligned - /// NativeRectFloat with the rectangle for alignment of the element + /// the bounds of the element to be aligned + /// the rectangle reference for alignment of the element /// the System.Drawing.ContentAlignment value indicating how the element is to be aligned should the width or height differ from targetSize - /// NativeRectFloat object with Location aligned aligned to targetRect - public static NativeRectFloat GetAlignedRectangle(NativeRectFloat currentRect, NativeRectFloat targetRect, ContentAlignment alignment) + /// a new RectangleF object with Location aligned aligned to targetRect + public static RectangleF GetAlignedRectangle(RectangleF currentRect, RectangleF targetRect, ContentAlignment alignment) { - var newRect = new NativeRectFloat(targetRect.Location, currentRect.Size); - return alignment switch + RectangleF newRect = new RectangleF(targetRect.Location, currentRect.Size); + switch (alignment) { - // TODO: Can ContentAlignment be replaced with Positions? - ContentAlignment.TopCenter => newRect.ChangeX((targetRect.Width - currentRect.Width) / 2), - ContentAlignment.TopRight => newRect.ChangeX(targetRect.Width - currentRect.Width), - ContentAlignment.MiddleLeft => newRect.ChangeY((targetRect.Height - currentRect.Height) / 2), - ContentAlignment.MiddleCenter => newRect.ChangeY((targetRect.Height - currentRect.Height) / 2).ChangeX((targetRect.Width - currentRect.Width) / 2), - ContentAlignment.MiddleRight => newRect.ChangeY((targetRect.Height - currentRect.Height) / 2).ChangeX(targetRect.Width - currentRect.Width), - ContentAlignment.BottomLeft => newRect.ChangeY(targetRect.Height - currentRect.Height), - ContentAlignment.BottomCenter => newRect.ChangeY(targetRect.Height - currentRect.Height).ChangeX((targetRect.Width - currentRect.Width) / 2), - ContentAlignment.BottomRight => newRect.ChangeY(targetRect.Height - currentRect.Height).ChangeX(targetRect.Width - currentRect.Width), - _ => newRect - }; + case ContentAlignment.TopCenter: + newRect.X = (targetRect.Width - currentRect.Width) / 2; + break; + case ContentAlignment.TopRight: + newRect.X = targetRect.Width - currentRect.Width; + break; + case ContentAlignment.MiddleLeft: + newRect.Y = (targetRect.Height - currentRect.Height) / 2; + break; + case ContentAlignment.MiddleCenter: + newRect.Y = (targetRect.Height - currentRect.Height) / 2; + newRect.X = (targetRect.Width - currentRect.Width) / 2; + break; + case ContentAlignment.MiddleRight: + newRect.Y = (targetRect.Height - currentRect.Height) / 2; + newRect.X = targetRect.Width - currentRect.Width; + break; + case ContentAlignment.BottomLeft: + newRect.Y = targetRect.Height - currentRect.Height; + break; + case ContentAlignment.BottomCenter: + newRect.Y = targetRect.Height - currentRect.Height; + newRect.X = (targetRect.Width - currentRect.Width) / 2; + break; + case ContentAlignment.BottomRight: + newRect.Y = targetRect.Height - currentRect.Height; + newRect.X = targetRect.Width - currentRect.Width; + break; + } + + return newRect; } /// /// Calculates target size of a given rectangle scaled by dragging one of its handles (corners) /// - /// NativeRectFloat bounds of the current rectangle - /// Positions with the position of the handle/gripper being used for resized, see constants in Gripper.cs, e.g. Gripper.POSITION_TOP_LEFT - /// NativePointFloat coordinates of the used handle/gripper + /// bounds of the current rectangle, scaled values will be written to this reference + /// position of the handle/gripper being used for resized, see constants in Gripper.cs, e.g. Gripper.POSITION_TOP_LEFT + /// coordinates of the used handle/gripper /// ScaleOptions to use when scaling - /// NativeRectFloat scaled originalRectangle - public static NativeRectFloat Scale(NativeRectFloat originalRectangle, Positions resizeHandlePosition, NativePointFloat resizeHandleCoords, ScaleOptions? options) + public static void Scale(ref RectangleF originalRectangle, Positions resizeHandlePosition, PointF resizeHandleCoords, ScaleOptions? options) { options ??= GetScaleOptions(); if ((options & ScaleOptions.Rational) == ScaleOptions.Rational) { - resizeHandleCoords = AdjustCoordsForRationalScale(originalRectangle, resizeHandlePosition, resizeHandleCoords); + AdjustCoordsForRationalScale(originalRectangle, resizeHandlePosition, ref resizeHandleCoords); } if ((options & ScaleOptions.Centered) == ScaleOptions.Centered) @@ -97,40 +133,75 @@ namespace Greenshot.Editor.Helpers float rectCenterX = originalRectangle.Left + originalRectangle.Width / 2; float rectCenterY = originalRectangle.Top + originalRectangle.Height / 2; // scale rectangle using handle coordinates - originalRectangle = Scale(originalRectangle, resizeHandlePosition, resizeHandleCoords); + Scale(ref originalRectangle, resizeHandlePosition, resizeHandleCoords); // mirror handle coordinates via rectangle center coordinates - resizeHandleCoords = resizeHandleCoords.Offset(-2 * (resizeHandleCoords.X - rectCenterX), -2 * (resizeHandleCoords.Y - rectCenterY)); + resizeHandleCoords.X -= 2 * (resizeHandleCoords.X - rectCenterX); + resizeHandleCoords.Y -= 2 * (resizeHandleCoords.Y - rectCenterY); // scale again with opposing handle and mirrored coordinates resizeHandlePosition = (Positions) ((((int) resizeHandlePosition) + 4) % 8); - originalRectangle = Scale(originalRectangle, resizeHandlePosition, resizeHandleCoords); + Scale(ref originalRectangle, resizeHandlePosition, resizeHandleCoords); } else { - originalRectangle = Scale(originalRectangle, resizeHandlePosition, resizeHandleCoords); + Scale(ref originalRectangle, resizeHandlePosition, resizeHandleCoords); } - - return originalRectangle; } /// /// Calculates target size of a given rectangle scaled by dragging one of its handles (corners) /// - /// NativeRectFloat bounds of the current rectangle - /// Positions with the position of the handle/gripper being used for resized, see constants in Gripper.cs, e.g. Gripper.POSITION_TOP_LEFT - /// NativePointFloat with coordinates of the used handle/gripper - /// NativeRectFloat with the scaled originalRectangle - private static NativeRectFloat Scale(NativeRectFloat originalRectangle, Positions resizeHandlePosition, NativePointFloat resizeHandleCoords) + /// bounds of the current rectangle, scaled values will be written to this reference + /// position of the handle/gripper being used for resized, see constants in Gripper.cs, e.g. Gripper.POSITION_TOP_LEFT + /// coordinates of the used handle/gripper + private static void Scale(ref RectangleF originalRectangle, Positions resizeHandlePosition, PointF resizeHandleCoords) { - return resizeHandlePosition switch + switch (resizeHandlePosition) { - Positions.TopLeft => new NativeRectFloat(resizeHandleCoords.X, resizeHandleCoords.Y, originalRectangle.Left + originalRectangle.Width - resizeHandleCoords.X, originalRectangle.Top + originalRectangle.Height - resizeHandleCoords.Y), - Positions.TopCenter => new NativeRectFloat(originalRectangle.X, resizeHandleCoords.Y, originalRectangle.Width, originalRectangle.Top + originalRectangle.Height - resizeHandleCoords.Y), - Positions.TopRight => new NativeRectFloat(originalRectangle.X, resizeHandleCoords.Y, resizeHandleCoords.X - originalRectangle.Left, originalRectangle.Top + originalRectangle.Height - resizeHandleCoords.Y), - Positions.MiddleLeft => new NativeRectFloat(resizeHandleCoords.X, originalRectangle.Y, originalRectangle.Left + originalRectangle.Width - resizeHandleCoords.X, originalRectangle.Height), - Positions.MiddleRight => new NativeRectFloat(originalRectangle.X, originalRectangle.Y, resizeHandleCoords.X - originalRectangle.Left, originalRectangle.Height), Positions.BottomLeft => new NativeRectFloat(resizeHandleCoords.X, originalRectangle.Y, originalRectangle.Left + originalRectangle.Width - resizeHandleCoords.X, resizeHandleCoords.Y - originalRectangle.Top), - Positions.BottomCenter => new NativeRectFloat(originalRectangle.X, originalRectangle.Y, originalRectangle.Width, resizeHandleCoords.Y - originalRectangle.Top), Positions.BottomRight => new NativeRectFloat(originalRectangle.X, originalRectangle.Y, resizeHandleCoords.X - originalRectangle.Left, resizeHandleCoords.Y - originalRectangle.Top), - _ => throw new ArgumentException("Position cannot be handled: " + resizeHandlePosition) - }; + case Positions.TopLeft: + originalRectangle.Width = originalRectangle.Left + originalRectangle.Width - resizeHandleCoords.X; + originalRectangle.Height = originalRectangle.Top + originalRectangle.Height - resizeHandleCoords.Y; + originalRectangle.X = resizeHandleCoords.X; + originalRectangle.Y = resizeHandleCoords.Y; + break; + + case Positions.TopCenter: + originalRectangle.Height = originalRectangle.Top + originalRectangle.Height - resizeHandleCoords.Y; + originalRectangle.Y = resizeHandleCoords.Y; + break; + + case Positions.TopRight: + originalRectangle.Width = resizeHandleCoords.X - originalRectangle.Left; + originalRectangle.Height = originalRectangle.Top + originalRectangle.Height - resizeHandleCoords.Y; + originalRectangle.Y = resizeHandleCoords.Y; + break; + + case Positions.MiddleLeft: + originalRectangle.Width = originalRectangle.Left + originalRectangle.Width - resizeHandleCoords.X; + originalRectangle.X = resizeHandleCoords.X; + break; + + case Positions.MiddleRight: + originalRectangle.Width = resizeHandleCoords.X - originalRectangle.Left; + break; + + case Positions.BottomLeft: + originalRectangle.Width = originalRectangle.Left + originalRectangle.Width - resizeHandleCoords.X; + originalRectangle.Height = resizeHandleCoords.Y - originalRectangle.Top; + originalRectangle.X = resizeHandleCoords.X; + break; + + case Positions.BottomCenter: + originalRectangle.Height = resizeHandleCoords.Y - originalRectangle.Top; + break; + + case Positions.BottomRight: + originalRectangle.Width = resizeHandleCoords.X - originalRectangle.Left; + originalRectangle.Height = resizeHandleCoords.Y - originalRectangle.Top; + break; + + default: + throw new ArgumentException("Position cannot be handled: " + resizeHandlePosition); + } } /// @@ -139,42 +210,43 @@ namespace Greenshot.Editor.Helpers /// To avoid objects growing near infinity unexpectedly in certain combinations, the adjustment will choose the /// option resulting in the smaller rectangle. /// - /// NativeRectFloat with the bounds of the current rectangle - /// Positions with the position of the handle/gripper being used for resized, see Position - /// NativePointFloat with coordinates of the used handle/gripper - /// NativePointFloat with the adjusted coordinates - private static NativePointFloat AdjustCoordsForRationalScale(NativeRectFloat originalRectangle, Positions resizeHandlePosition, NativePointFloat resizeHandleCoords) + /// bounds of the current rectangle + /// position of the handle/gripper being used for resized, see Position + /// coordinates of the used handle/gripper, adjusted coordinates will be written to this reference + private static void AdjustCoordsForRationalScale(RectangleF originalRectangle, Positions resizeHandlePosition, ref PointF resizeHandleCoords) { - NativeSizeFloat selectedRectangle, newSize; + SizeF selectedRectangle, newSize; switch (resizeHandlePosition) { case Positions.TopLeft: - selectedRectangle = new NativeSizeFloat(originalRectangle.Right - resizeHandleCoords.X, originalRectangle.Bottom - resizeHandleCoords.Y); + selectedRectangle = new SizeF(originalRectangle.Right - resizeHandleCoords.X, originalRectangle.Bottom - resizeHandleCoords.Y); newSize = GetNewSizeForRationalScale(originalRectangle.Size, selectedRectangle); - resizeHandleCoords = new NativePointFloat(originalRectangle.Right - newSize.Width, originalRectangle.Bottom - newSize.Height); + resizeHandleCoords.X = originalRectangle.Right - newSize.Width; + resizeHandleCoords.Y = originalRectangle.Bottom - newSize.Height; break; case Positions.TopRight: - selectedRectangle = new NativeSizeFloat(resizeHandleCoords.X - originalRectangle.Left, originalRectangle.Bottom - resizeHandleCoords.Y); + selectedRectangle = new SizeF(resizeHandleCoords.X - originalRectangle.Left, originalRectangle.Bottom - resizeHandleCoords.Y); newSize = GetNewSizeForRationalScale(originalRectangle.Size, selectedRectangle); - resizeHandleCoords = new NativePointFloat(originalRectangle.Left + newSize.Width, originalRectangle.Bottom - newSize.Height); + resizeHandleCoords.X = originalRectangle.Left + newSize.Width; + resizeHandleCoords.Y = originalRectangle.Bottom - newSize.Height; break; case Positions.BottomLeft: - selectedRectangle = new NativeSizeFloat(originalRectangle.Right - resizeHandleCoords.X, resizeHandleCoords.Y - originalRectangle.Top); + selectedRectangle = new SizeF(originalRectangle.Right - resizeHandleCoords.X, resizeHandleCoords.Y - originalRectangle.Top); newSize = GetNewSizeForRationalScale(originalRectangle.Size, selectedRectangle); - resizeHandleCoords = new NativePointFloat(originalRectangle.Right - newSize.Width, originalRectangle.Top + newSize.Height); + resizeHandleCoords.X = originalRectangle.Right - newSize.Width; + resizeHandleCoords.Y = originalRectangle.Top + newSize.Height; break; case Positions.BottomRight: - selectedRectangle = new NativeSizeFloat(resizeHandleCoords.X - originalRectangle.Left, resizeHandleCoords.Y - originalRectangle.Top); + selectedRectangle = new SizeF(resizeHandleCoords.X - originalRectangle.Left, resizeHandleCoords.Y - originalRectangle.Top); newSize = GetNewSizeForRationalScale(originalRectangle.Size, selectedRectangle); - resizeHandleCoords = new NativePointFloat(originalRectangle.Left + newSize.Width, originalRectangle.Top + newSize.Height); + resizeHandleCoords.X = originalRectangle.Left + newSize.Width; + resizeHandleCoords.Y = originalRectangle.Top + newSize.Height; break; } - - return resizeHandleCoords; } /// @@ -182,12 +254,12 @@ namespace Greenshot.Editor.Helpers /// * has the same aspect ratio as the original /// * fits into selected size /// - /// NativeSizeFloat to be considered for keeping aspect ratio - /// NativeSizeFloat selection size (i.e. the size we'd produce if we wouldn't keep aspect ratio) - /// NativeSizeFloat - private static NativeSizeFloat GetNewSizeForRationalScale(NativeSizeFloat originalSize, NativeSizeFloat selectedSize) + /// size to be considered for keeping aspect ratio + /// selection size (i.e. the size we'd produce if we wouldn't keep aspect ratio) + /// + private static SizeF GetNewSizeForRationalScale(SizeF originalSize, SizeF selectedSize) { - NativeSizeFloat newSize = selectedSize; + SizeF newSize = selectedSize; float originalRatio = originalSize.Width / originalSize.Height; float selectedRatio = selectedSize.Width / selectedSize.Height; // will fix orientation if the scaling causes size to be flipped in any direction @@ -196,34 +268,35 @@ namespace Greenshot.Editor.Helpers { // scaled rectangle (ratio) would be wider than original // keep height and tweak width to maintain aspect ratio - newSize = newSize.ChangeWidth(selectedSize.Height * originalRatio * flippedRatioSign); + newSize.Width = selectedSize.Height * originalRatio * flippedRatioSign; } else if (Math.Abs(selectedRatio) < Math.Abs(originalRatio)) { // scaled rectangle (ratio) would be taller than original // keep width and tweak height to maintain aspect ratio - newSize = newSize.ChangeHeight(selectedSize.Width / originalRatio * flippedRatioSign); + newSize.Height = selectedSize.Width / originalRatio * flippedRatioSign; } return newSize; } - /// - /// Scale the boundsBeforeResize with the specified position and new location, using the angle angleRoundBehavior - /// - /// NativeRect - /// int - /// int - /// IDoubleProcessor - /// ScaleOptions - /// NativeRectFloat - public static NativeRectFloat Scale(NativeRect boundsBeforeResize, int cursorX, int cursorY, IDoubleProcessor angleRoundBehavior, ScaleOptions? scaleOptions = null) + public static void Scale(Rectangle boundsBeforeResize, int cursorX, int cursorY, ref RectangleF boundsAfterResize) { - scaleOptions ??= GetScaleOptions(); + Scale(boundsBeforeResize, cursorX, cursorY, ref boundsAfterResize, null); + } - NativeRectFloat result = boundsBeforeResize; - bool rationalScale = (scaleOptions & ScaleOptions.Rational) == ScaleOptions.Rational; - bool centeredScale = (scaleOptions & ScaleOptions.Centered) == ScaleOptions.Centered; + public static void Scale(Rectangle boundsBeforeResize, int cursorX, int cursorY, ref RectangleF boundsAfterResize, IDoubleProcessor angleRoundBehavior) + { + Scale(boundsBeforeResize, Positions.TopLeft, cursorX, cursorY, ref boundsAfterResize, angleRoundBehavior); + } + + public static void Scale(Rectangle boundsBeforeResize, Positions gripperPosition, int cursorX, int cursorY, ref RectangleF boundsAfterResize, + IDoubleProcessor angleRoundBehavior) + { + ScaleOptions opts = GetScaleOptions(); + + bool rationalScale = (opts & ScaleOptions.Rational) == ScaleOptions.Rational; + bool centeredScale = (opts & ScaleOptions.Centered) == ScaleOptions.Centered; if (rationalScale) { @@ -236,19 +309,19 @@ namespace Greenshot.Editor.Helpers int dist = GeometryHelper.Distance2D(boundsBeforeResize.X, boundsBeforeResize.Y, cursorX, cursorY); - result = result - .ChangeWidth((int)Math.Round(dist * Math.Cos(angle / 180 * Math.PI))) - .ChangeHeight((int) Math.Round(dist * Math.Sin(angle / 180 * Math.PI))); + boundsAfterResize.Width = (int) Math.Round(dist * Math.Cos(angle / 180 * Math.PI)); + boundsAfterResize.Height = (int) Math.Round(dist * Math.Sin(angle / 180 * Math.PI)); } if (centeredScale) { - float wdiff = result.Width - result.Width; - float hdiff = result.Height - result.Height; - result = result.Inflate(wdiff, hdiff); + float wdiff = boundsAfterResize.Width - boundsBeforeResize.Width; + float hdiff = boundsAfterResize.Height - boundsBeforeResize.Height; + boundsAfterResize.Width += wdiff; + boundsAfterResize.Height += hdiff; + boundsAfterResize.X -= wdiff; + boundsAfterResize.Y -= hdiff; } - - return result; } /// the current ScaleOptions depending on modifier keys held down @@ -261,5 +334,38 @@ namespace Greenshot.Editor.Helpers if (maintainAspectRatio) opts |= ScaleOptions.Rational; return opts; } + + public interface IDoubleProcessor + { + double Process(double d); + } + + public class ShapeAngleRoundBehavior : IDoubleProcessor + { + public static ShapeAngleRoundBehavior Instance = new(); + + private ShapeAngleRoundBehavior() + { + } + + public double Process(double angle) + { + return Math.Round((angle + 45) / 90) * 90 - 45; + } + } + + public class LineAngleRoundBehavior : IDoubleProcessor + { + public static LineAngleRoundBehavior Instance = new(); + + private LineAngleRoundBehavior() + { + } + + public double Process(double angle) + { + return Math.Round(angle / 15) * 15; + } + } } } \ No newline at end of file diff --git a/src/Greenshot.Editor/Memento/AddElementMemento.cs b/src/Greenshot.Editor/Memento/AddElementMemento.cs index b76858780..dbee523a6 100644 --- a/src/Greenshot.Editor/Memento/AddElementMemento.cs +++ b/src/Greenshot.Editor/Memento/AddElementMemento.cs @@ -20,8 +20,9 @@ */ using System; -using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing; + namespace Greenshot.Editor.Memento { /// @@ -30,9 +31,9 @@ namespace Greenshot.Editor.Memento public class AddElementMemento : IMemento { private IDrawableContainer _drawableContainer; - private ISurface _surface; + private Surface _surface; - public AddElementMemento(ISurface surface, IDrawableContainer drawableContainer) + public AddElementMemento(Surface surface, IDrawableContainer drawableContainer) { _surface = surface; _drawableContainer = drawableContainer; @@ -44,7 +45,7 @@ namespace Greenshot.Editor.Memento GC.SuppressFinalize(this); } - private void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) { //if (disposing) { } _drawableContainer = null; diff --git a/src/Greenshot.Editor/Memento/AddElementsMemento.cs b/src/Greenshot.Editor/Memento/AddElementsMemento.cs index 0b11fac15..80c9d3d99 100644 --- a/src/Greenshot.Editor/Memento/AddElementsMemento.cs +++ b/src/Greenshot.Editor/Memento/AddElementsMemento.cs @@ -19,8 +19,8 @@ * along with this program. If not, see . */ -using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing; namespace Greenshot.Editor.Memento { @@ -30,9 +30,9 @@ namespace Greenshot.Editor.Memento public class AddElementsMemento : IMemento { private IDrawableContainerList _containerList; - private ISurface _surface; + private Surface _surface; - public AddElementsMemento(ISurface surface, IDrawableContainerList containerList) + public AddElementsMemento(Surface surface, IDrawableContainerList containerList) { _surface = surface; _containerList = containerList; @@ -43,7 +43,7 @@ namespace Greenshot.Editor.Memento Dispose(true); } - private void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) { if (disposing) { diff --git a/src/Greenshot.Editor/Memento/ChangeFieldHolderMemento.cs b/src/Greenshot.Editor/Memento/ChangeFieldHolderMemento.cs index 70dba9252..1ffa8c16e 100644 --- a/src/Greenshot.Editor/Memento/ChangeFieldHolderMemento.cs +++ b/src/Greenshot.Editor/Memento/ChangeFieldHolderMemento.cs @@ -44,7 +44,7 @@ namespace Greenshot.Editor.Memento Dispose(true); } - private void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) { if (disposing) { diff --git a/src/Greenshot.Editor/Memento/DeleteElementMemento.cs b/src/Greenshot.Editor/Memento/DeleteElementMemento.cs index 1d744239d..ade27b23a 100644 --- a/src/Greenshot.Editor/Memento/DeleteElementMemento.cs +++ b/src/Greenshot.Editor/Memento/DeleteElementMemento.cs @@ -20,8 +20,8 @@ */ using System; -using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing; namespace Greenshot.Editor.Memento { @@ -31,9 +31,9 @@ namespace Greenshot.Editor.Memento public class DeleteElementMemento : IMemento { private IDrawableContainer _drawableContainer; - private readonly ISurface _surface; + private readonly Surface _surface; - public DeleteElementMemento(ISurface surface, IDrawableContainer drawableContainer) + public DeleteElementMemento(Surface surface, IDrawableContainer drawableContainer) { _surface = surface; _drawableContainer = drawableContainer; @@ -45,7 +45,7 @@ namespace Greenshot.Editor.Memento GC.SuppressFinalize(this); } - private void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) { if (!disposing) return; diff --git a/src/Greenshot.Editor/Memento/DeleteElementsMemento.cs b/src/Greenshot.Editor/Memento/DeleteElementsMemento.cs index 509bbb016..d4d4a3be1 100644 --- a/src/Greenshot.Editor/Memento/DeleteElementsMemento.cs +++ b/src/Greenshot.Editor/Memento/DeleteElementsMemento.cs @@ -19,8 +19,8 @@ * along with this program. If not, see . */ -using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing; namespace Greenshot.Editor.Memento { @@ -30,9 +30,9 @@ namespace Greenshot.Editor.Memento public class DeleteElementsMemento : IMemento { private IDrawableContainerList _containerList; - private ISurface _surface; + private Surface _surface; - public DeleteElementsMemento(ISurface surface, IDrawableContainerList containerList) + public DeleteElementsMemento(Surface surface, IDrawableContainerList containerList) { _surface = surface; _containerList = containerList; @@ -43,7 +43,7 @@ namespace Greenshot.Editor.Memento Dispose(true); } - private void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) { if (disposing) { diff --git a/src/Greenshot.Editor/Memento/DrawableContainerBoundsChangeMemento.cs b/src/Greenshot.Editor/Memento/DrawableContainerBoundsChangeMemento.cs index b5b97e4cf..428b0ff3d 100644 --- a/src/Greenshot.Editor/Memento/DrawableContainerBoundsChangeMemento.cs +++ b/src/Greenshot.Editor/Memento/DrawableContainerBoundsChangeMemento.cs @@ -34,30 +34,30 @@ namespace Greenshot.Editor.Memento { private readonly List _points = new(); private readonly List _sizes = new(); - private IDrawableContainerList _listOfDrawableContainer; + private IDrawableContainerList _listOfdrawableContainer; private void StoreBounds() { - foreach (IDrawableContainer drawableContainer in _listOfDrawableContainer) + foreach (IDrawableContainer drawableContainer in _listOfdrawableContainer) { _points.Add(drawableContainer.Location); _sizes.Add(drawableContainer.Size); } } - public DrawableContainerBoundsChangeMemento(IDrawableContainerList listOfDrawableContainer) + public DrawableContainerBoundsChangeMemento(IDrawableContainerList listOfdrawableContainer) { - _listOfDrawableContainer = listOfDrawableContainer; + _listOfdrawableContainer = listOfdrawableContainer; StoreBounds(); } public DrawableContainerBoundsChangeMemento(IDrawableContainer drawableContainer) { - _listOfDrawableContainer = new DrawableContainerList + _listOfdrawableContainer = new DrawableContainerList { drawableContainer }; - _listOfDrawableContainer.Parent = drawableContainer.Parent; + _listOfdrawableContainer.Parent = drawableContainer.Parent; StoreBounds(); } @@ -66,21 +66,21 @@ namespace Greenshot.Editor.Memento Dispose(true); } - private void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) { if (disposing) { - _listOfDrawableContainer?.Dispose(); + _listOfdrawableContainer?.Dispose(); } - _listOfDrawableContainer = null; + _listOfdrawableContainer = null; } public bool Merge(IMemento otherMemento) { if (otherMemento is not DrawableContainerBoundsChangeMemento other) return false; - if (ObjectExtensions.CompareLists(_listOfDrawableContainer, other._listOfDrawableContainer)) + if (ObjectExtensions.CompareLists(_listOfdrawableContainer, other._listOfdrawableContainer)) { // Lists are equal, as we have the state already we can ignore the new memento return true; @@ -91,10 +91,10 @@ namespace Greenshot.Editor.Memento public IMemento Restore() { - var oldState = new DrawableContainerBoundsChangeMemento(_listOfDrawableContainer); - for (int index = 0; index < _listOfDrawableContainer.Count; index++) + var oldState = new DrawableContainerBoundsChangeMemento(_listOfdrawableContainer); + for (int index = 0; index < _listOfdrawableContainer.Count; index++) { - IDrawableContainer drawableContainer = _listOfDrawableContainer[index]; + IDrawableContainer drawableContainer = _listOfdrawableContainer[index]; // Before drawableContainer.Invalidate(); drawableContainer.Left = _points[index].X; diff --git a/src/Greenshot.Editor/Memento/SurfaceBackgroundChangeMemento.cs b/src/Greenshot.Editor/Memento/SurfaceBackgroundChangeMemento.cs index 8c49614b4..71d2e9f36 100644 --- a/src/Greenshot.Editor/Memento/SurfaceBackgroundChangeMemento.cs +++ b/src/Greenshot.Editor/Memento/SurfaceBackgroundChangeMemento.cs @@ -21,8 +21,8 @@ using System.Drawing; using System.Drawing.Drawing2D; -using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing; namespace Greenshot.Editor.Memento { @@ -32,10 +32,10 @@ namespace Greenshot.Editor.Memento public class SurfaceBackgroundChangeMemento : IMemento { private Image _image; - private ISurface _surface; + private Surface _surface; private Matrix _matrix; - public SurfaceBackgroundChangeMemento(ISurface surface, Matrix matrix) + public SurfaceBackgroundChangeMemento(Surface surface, Matrix matrix) { _surface = surface; _image = surface.Image; @@ -52,7 +52,7 @@ namespace Greenshot.Editor.Memento Dispose(true); } - private void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) { if (!disposing) return; diff --git a/src/Greenshot.Editor/Memento/TextChangeMemento.cs b/src/Greenshot.Editor/Memento/TextChangeMemento.cs index 46f33592b..411dde53c 100644 --- a/src/Greenshot.Editor/Memento/TextChangeMemento.cs +++ b/src/Greenshot.Editor/Memento/TextChangeMemento.cs @@ -43,7 +43,7 @@ namespace Greenshot.Editor.Memento Dispose(true); } - private void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) { if (disposing) { diff --git a/src/Greenshot.Editor/Resources/AutoCrop.Image.png b/src/Greenshot.Editor/Resources/AutoCrop.Image.png deleted file mode 100644 index 1f9d86c4d..000000000 Binary files a/src/Greenshot.Editor/Resources/AutoCrop.Image.png and /dev/null differ diff --git a/src/Greenshot.Editor/Resources/CropHorizontal.Image.png b/src/Greenshot.Editor/Resources/CropHorizontal.Image.png deleted file mode 100644 index f8f321c15..000000000 Binary files a/src/Greenshot.Editor/Resources/CropHorizontal.Image.png and /dev/null differ diff --git a/src/Greenshot.Editor/Resources/CropVertical.Image.png b/src/Greenshot.Editor/Resources/CropVertical.Image.png deleted file mode 100644 index fe1dea06e..000000000 Binary files a/src/Greenshot.Editor/Resources/CropVertical.Image.png and /dev/null differ diff --git a/src/Greenshot.Plugin.Box/BoxConfiguration.cs b/src/Greenshot.Plugin.Box/BoxConfiguration.cs index c745fe985..34ae4afc9 100644 --- a/src/Greenshot.Plugin.Box/BoxConfiguration.cs +++ b/src/Greenshot.Plugin.Box/BoxConfiguration.cs @@ -76,20 +76,5 @@ namespace Greenshot.Plugin.Box return false; } - - /// - /// Upgrade certain values - /// - public override void AfterLoad() - { - var coreConfiguration = IniConfig.GetIniSection(); - bool isUpgradeFrom12 = coreConfiguration.LastSaveWithVersion?.StartsWith("1.2") ?? false; - // Clear token when we upgrade from 1.2 to 1.3 as it is no longer valid, discussed in #421 - if (!isUpgradeFrom12) return; - - // We have an upgrade, remove all previous credentials. - RefreshToken = null; - AccessToken = null; - } } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Box/Forms/SettingsForm.Designer.cs b/src/Greenshot.Plugin.Box/Forms/SettingsForm.Designer.cs index 940eaccb6..de492dde4 100644 --- a/src/Greenshot.Plugin.Box/Forms/SettingsForm.Designer.cs +++ b/src/Greenshot.Plugin.Box/Forms/SettingsForm.Designer.cs @@ -87,7 +87,7 @@ namespace Greenshot.Plugin.Box.Forms { this.combobox_uploadimageformat.FormattingEnabled = true; this.combobox_uploadimageformat.Location = new System.Drawing.Point(208, 12); this.combobox_uploadimageformat.Name = "combobox_uploadimageformat"; - this.combobox_uploadimageformat.PropertyName = nameof(BoxConfiguration.UploadFormat); + this.combobox_uploadimageformat.PropertyName = "UploadFormat"; this.combobox_uploadimageformat.SectionName = "Box"; this.combobox_uploadimageformat.Size = new System.Drawing.Size(215, 21); this.combobox_uploadimageformat.TabIndex = 5; @@ -115,7 +115,7 @@ namespace Greenshot.Plugin.Box.Forms { this.checkboxAfterUploadLinkToClipBoard.LanguageKey = "box.label_AfterUploadLinkToClipBoard"; this.checkboxAfterUploadLinkToClipBoard.Location = new System.Drawing.Point(208, 45); this.checkboxAfterUploadLinkToClipBoard.Name = "checkboxAfterUploadLinkToClipBoard"; - this.checkboxAfterUploadLinkToClipBoard.PropertyName = nameof(BoxConfiguration.AfterUploadLinkToClipBoard); + this.checkboxAfterUploadLinkToClipBoard.PropertyName = "AfterUploadLinkToClipBoard"; this.checkboxAfterUploadLinkToClipBoard.SectionName = "Box"; this.checkboxAfterUploadLinkToClipBoard.Size = new System.Drawing.Size(104, 17); this.checkboxAfterUploadLinkToClipBoard.TabIndex = 10; diff --git a/src/Greenshot.Plugin.Box/Greenshot.Plugin.Box.Credentials.template b/src/Greenshot.Plugin.Box/Greenshot.Plugin.Box.Credentials.template index da2dbc7ae..df6eaa0bd 100644 --- a/src/Greenshot.Plugin.Box/Greenshot.Plugin.Box.Credentials.template +++ b/src/Greenshot.Plugin.Box/Greenshot.Plugin.Box.Credentials.template @@ -1,4 +1,4 @@ -/* +/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom * diff --git a/src/Greenshot.Plugin.Box/Greenshot.Plugin.Box.csproj b/src/Greenshot.Plugin.Box/Greenshot.Plugin.Box.csproj index 2b3410c8f..fced869ad 100644 --- a/src/Greenshot.Plugin.Box/Greenshot.Plugin.Box.csproj +++ b/src/Greenshot.Plugin.Box/Greenshot.Plugin.Box.csproj @@ -1,8 +1,4 @@  - - none - false - PreserveNewest diff --git a/src/Greenshot.Plugin.Box/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Box/Properties/AssemblyInfo.cs index 0379e4a65..2ffb6f6fd 100644 --- a/src/Greenshot.Plugin.Box/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Box/Properties/AssemblyInfo.cs @@ -21,13 +21,11 @@ using System.Reflection; using System.Runtime.InteropServices; -using Greenshot.Base.Interfaces.Plugin; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyDescription("A plugin to upload images to Box")] -[assembly: AssemblyPluginIdentifier("Box Plugin")] // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. diff --git a/src/Greenshot.Plugin.Confluence/ConfluenceConfiguration.cs b/src/Greenshot.Plugin.Confluence/ConfluenceConfiguration.cs index 8176ebe6e..f2212126d 100644 --- a/src/Greenshot.Plugin.Confluence/ConfluenceConfiguration.cs +++ b/src/Greenshot.Plugin.Confluence/ConfluenceConfiguration.cs @@ -20,6 +20,7 @@ */ using System; +using Greenshot.Base.Core; using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; diff --git a/src/Greenshot.Plugin.Confluence/ConfluenceDestination.cs b/src/Greenshot.Plugin.Confluence/ConfluenceDestination.cs index 669087901..ddcb66530 100644 --- a/src/Greenshot.Plugin.Confluence/ConfluenceDestination.cs +++ b/src/Greenshot.Plugin.Confluence/ConfluenceDestination.cs @@ -54,8 +54,8 @@ namespace Greenshot.Plugin.Confluence Uri confluenceIconUri = new Uri("/Greenshot.Plugin.Confluence;component/Images/Confluence.ico", UriKind.Relative); using (Stream iconStream = Application.GetResourceStream(confluenceIconUri)?.Stream) { - // TODO: Replace with FileFormatHandler - ConfluenceIcon = ImageIO.FromStream(iconStream); + // TODO: Check what to do with the IImage + ConfluenceIcon = ImageHelper.FromStream(iconStream); } IsInitialized = true; diff --git a/src/Greenshot.Plugin.Confluence/Greenshot.Plugin.Confluence.csproj b/src/Greenshot.Plugin.Confluence/Greenshot.Plugin.Confluence.csproj index 0734c44e9..d885ea3f5 100644 --- a/src/Greenshot.Plugin.Confluence/Greenshot.Plugin.Confluence.csproj +++ b/src/Greenshot.Plugin.Confluence/Greenshot.Plugin.Confluence.csproj @@ -1,8 +1,4 @@  - - none - false - PreserveNewest diff --git a/src/Greenshot.Plugin.Dropbox/DropboxDestination.cs b/src/Greenshot.Plugin.Dropbox/DropboxDestination.cs index d29c3cbd0..59f6c45b1 100644 --- a/src/Greenshot.Plugin.Dropbox/DropboxDestination.cs +++ b/src/Greenshot.Plugin.Dropbox/DropboxDestination.cs @@ -29,7 +29,7 @@ namespace Greenshot.Plugin.Dropbox { internal class DropboxDestination : AbstractDestination { - private static readonly DropboxConfiguration DropboxConfig = IniConfig.GetIniSection(); + private static readonly DropboxPluginConfiguration DropboxConfig = IniConfig.GetIniSection(); private readonly DropboxPlugin _plugin; diff --git a/src/Greenshot.Plugin.Dropbox/DropboxPlugin.cs b/src/Greenshot.Plugin.Dropbox/DropboxPlugin.cs index 69026066c..9a82b0f40 100644 --- a/src/Greenshot.Plugin.Dropbox/DropboxPlugin.cs +++ b/src/Greenshot.Plugin.Dropbox/DropboxPlugin.cs @@ -37,7 +37,7 @@ namespace Greenshot.Plugin.Dropbox public class DropboxPlugin : IGreenshotPlugin { private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(DropboxPlugin)); - private static DropboxConfiguration _config; + private static DropboxPluginConfiguration _config; private ComponentResourceManager _resources; private ToolStripMenuItem _itemPlugInConfig; @@ -71,7 +71,7 @@ namespace Greenshot.Plugin.Dropbox public bool Initialize() { // Register configuration (don't need the configuration itself) - _config = IniConfig.GetIniSection(); + _config = IniConfig.GetIniSection(); _resources = new ComponentResourceManager(typeof(DropboxPlugin)); SimpleServiceProvider.Current.AddService(new DropboxDestination(this)); _itemPlugInConfig = new ToolStripMenuItem diff --git a/src/Greenshot.Plugin.Dropbox/DropboxConfiguration.cs b/src/Greenshot.Plugin.Dropbox/DropboxPluginConfiguration.cs similarity index 79% rename from src/Greenshot.Plugin.Dropbox/DropboxConfiguration.cs rename to src/Greenshot.Plugin.Dropbox/DropboxPluginConfiguration.cs index d79db8686..996b964f0 100644 --- a/src/Greenshot.Plugin.Dropbox/DropboxConfiguration.cs +++ b/src/Greenshot.Plugin.Dropbox/DropboxPluginConfiguration.cs @@ -1,89 +1,74 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Windows.Forms; -using Greenshot.Base.Core; -using Greenshot.Base.Core.Enums; -using Greenshot.Base.IniFile; -using Greenshot.Plugin.Dropbox.Forms; - -namespace Greenshot.Plugin.Dropbox -{ - /// - /// The configuration for Dropbox - /// - [IniSection("Dropbox", Description = "Greenshot Dropbox Plugin configuration")] - public class DropboxConfiguration : IniSection - { - [IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")] - public OutputFormat UploadFormat { get; set; } - - [IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")] - public int UploadJpegQuality { get; set; } - - [IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send Dropbox link to clipboard.", DefaultValue = "true")] - public bool AfterUploadLinkToClipBoard { get; set; } - - [IniProperty("RefreshToken", Description = "Dropbox refresh Token", Encrypted = true, ExcludeIfNull = true)] - public string RefreshToken { get; set; } - - /// - /// AccessToken, not stored - /// - public string AccessToken { get; set; } - - /// - /// AccessTokenExpires, not stored - /// - public DateTimeOffset AccessTokenExpires { get; set; } - - /// - /// A form for token - /// - /// bool true if OK was pressed, false if cancel - public bool ShowConfigDialog() - { - DialogResult result = new SettingsForm().ShowDialog(); - if (result == DialogResult.OK) - { - return true; - } - - return false; - } - - /// - /// Upgrade certain values - /// - public override void AfterLoad() - { - var coreConfiguration = IniConfig.GetIniSection(); - bool isUpgradeFrom12 = coreConfiguration.LastSaveWithVersion?.StartsWith("1.2") ?? false; - // Clear token when we upgrade from 1.2 to 1.3 as it is no longer valid, discussed in #421 - if (!isUpgradeFrom12) return; - - // We have an upgrade, remove all previous credentials. - RefreshToken = null; - AccessToken = null; - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Windows.Forms; +using Greenshot.Base.Core; +using Greenshot.Base.Core.Enums; +using Greenshot.Base.IniFile; +using Greenshot.Plugin.Dropbox.Forms; + +namespace Greenshot.Plugin.Dropbox +{ + /// + /// Description of ImgurConfiguration. + /// + [IniSection("Dropbox", Description = "Greenshot Dropbox Plugin configuration")] + public class DropboxPluginConfiguration : IniSection + { + [IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")] + public OutputFormat UploadFormat { get; set; } + + [IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")] + public int UploadJpegQuality { get; set; } + + [IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send Dropbox link to clipboard.", DefaultValue = "true")] + public bool AfterUploadLinkToClipBoard { get; set; } + + [IniProperty("RefreshToken", Description = "Dropbox refresh Token", Encrypted = true, ExcludeIfNull = true)] + public string RefreshToken { get; set; } + + /// + /// AccessToken, not stored + /// + public string AccessToken { get; set; } + + /// + /// AccessTokenExpires, not stored + /// + public DateTimeOffset AccessTokenExpires { get; set; } + + /// + /// A form for token + /// + /// bool true if OK was pressed, false if cancel + public bool ShowConfigDialog() + { + DialogResult result = new SettingsForm().ShowDialog(); + if (result == DialogResult.OK) + { + return true; + } + + return false; + } + } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Dropbox/DropboxUtils.cs b/src/Greenshot.Plugin.Dropbox/DropboxUtils.cs index c7f6dcfe9..4b5eb4d60 100644 --- a/src/Greenshot.Plugin.Dropbox/DropboxUtils.cs +++ b/src/Greenshot.Plugin.Dropbox/DropboxUtils.cs @@ -37,7 +37,7 @@ namespace Greenshot.Plugin.Dropbox public class DropboxUtils { private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(DropboxUtils)); - private static readonly DropboxConfiguration DropboxConfig = IniConfig.GetIniSection(); + private static readonly DropboxPluginConfiguration DropboxConfig = IniConfig.GetIniSection(); private DropboxUtils() { diff --git a/src/Greenshot.Plugin.Dropbox/Forms/SettingsForm.Designer.cs b/src/Greenshot.Plugin.Dropbox/Forms/SettingsForm.Designer.cs index e65cba1ec..174342a90 100644 --- a/src/Greenshot.Plugin.Dropbox/Forms/SettingsForm.Designer.cs +++ b/src/Greenshot.Plugin.Dropbox/Forms/SettingsForm.Designer.cs @@ -86,7 +86,7 @@ namespace Greenshot.Plugin.Dropbox.Forms { this.combobox_uploadimageformat.FormattingEnabled = true; this.combobox_uploadimageformat.Location = new System.Drawing.Point(116, 9); this.combobox_uploadimageformat.Name = "combobox_uploadimageformat"; - this.combobox_uploadimageformat.PropertyName = nameof(DropboxConfiguration.UploadFormat); + this.combobox_uploadimageformat.PropertyName = "UploadFormat"; this.combobox_uploadimageformat.SectionName = "Dropbox"; this.combobox_uploadimageformat.Size = new System.Drawing.Size(309, 21); this.combobox_uploadimageformat.TabIndex = 1; @@ -112,7 +112,7 @@ namespace Greenshot.Plugin.Dropbox.Forms { this.checkboxAfterUploadLinkToClipBoard.LanguageKey = "dropbox.label_AfterUploadLinkToClipBoard"; this.checkboxAfterUploadLinkToClipBoard.Location = new System.Drawing.Point(116, 37); this.checkboxAfterUploadLinkToClipBoard.Name = "checkboxAfterUploadLinkToClipBoard"; - this.checkboxAfterUploadLinkToClipBoard.PropertyName = nameof(DropboxConfiguration.AfterUploadLinkToClipBoard); + this.checkboxAfterUploadLinkToClipBoard.PropertyName = "AfterUploadLinkToClipBoard"; this.checkboxAfterUploadLinkToClipBoard.SectionName = "Dropbox"; this.checkboxAfterUploadLinkToClipBoard.Size = new System.Drawing.Size(104, 17); this.checkboxAfterUploadLinkToClipBoard.TabIndex = 2; diff --git a/src/Greenshot.Plugin.Dropbox/Greenshot.Plugin.Dropbox.csproj b/src/Greenshot.Plugin.Dropbox/Greenshot.Plugin.Dropbox.csproj index 7437b64aa..09eb988a5 100644 --- a/src/Greenshot.Plugin.Dropbox/Greenshot.Plugin.Dropbox.csproj +++ b/src/Greenshot.Plugin.Dropbox/Greenshot.Plugin.Dropbox.csproj @@ -1,8 +1,4 @@  - - none - false - PreserveNewest diff --git a/src/Greenshot.Plugin.Dropbox/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Dropbox/Properties/AssemblyInfo.cs index b95b9af6d..d01359e25 100644 --- a/src/Greenshot.Plugin.Dropbox/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Dropbox/Properties/AssemblyInfo.cs @@ -21,13 +21,11 @@ using System.Reflection; using System.Runtime.InteropServices; -using Greenshot.Base.Interfaces.Plugin; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyDescription("A plugin to upload images to Dropbox")] -[assembly: AssemblyPluginIdentifier("Dropbox Plugin")] // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. diff --git a/src/Greenshot.Plugin.ExternalCommand/ExternalCommandDestination.cs b/src/Greenshot.Plugin.ExternalCommand/ExternalCommandDestination.cs index 4083827e3..273535952 100644 --- a/src/Greenshot.Plugin.ExternalCommand/ExternalCommandDestination.cs +++ b/src/Greenshot.Plugin.ExternalCommand/ExternalCommandDestination.cs @@ -77,7 +77,7 @@ namespace Greenshot.Plugin.ExternalCommand } bool runInBackground = config.RunInbackground[_presetCommand]; - string fullPath = captureDetails.Filename ?? ImageIO.SaveNamedTmpFile(surface, captureDetails, outputSettings); + string fullPath = captureDetails.Filename ?? ImageOutput.SaveNamedTmpFile(surface, captureDetails, outputSettings); string output; string error; diff --git a/src/Greenshot.Plugin.ExternalCommand/Greenshot.Plugin.ExternalCommand.csproj b/src/Greenshot.Plugin.ExternalCommand/Greenshot.Plugin.ExternalCommand.csproj index d0151b7c0..50e39b62a 100644 --- a/src/Greenshot.Plugin.ExternalCommand/Greenshot.Plugin.ExternalCommand.csproj +++ b/src/Greenshot.Plugin.ExternalCommand/Greenshot.Plugin.ExternalCommand.csproj @@ -1,8 +1,4 @@  - - none - false - PreserveNewest diff --git a/src/Greenshot.Plugin.ExternalCommand/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.ExternalCommand/Properties/AssemblyInfo.cs deleted file mode 100644 index acbe80370..000000000 --- a/src/Greenshot.Plugin.ExternalCommand/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System.Reflection; -using System.Runtime.InteropServices; -using Greenshot.Base.Interfaces.Plugin; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyDescription("A plugin to send screenshots to other applications")] -[assembly: AssemblyPluginIdentifier("External command Plugin")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] \ No newline at end of file diff --git a/src/Greenshot.Plugin.Flickr/FlickrConfiguration.cs b/src/Greenshot.Plugin.Flickr/FlickrConfiguration.cs index df5a428fa..8eedbf768 100644 --- a/src/Greenshot.Plugin.Flickr/FlickrConfiguration.cs +++ b/src/Greenshot.Plugin.Flickr/FlickrConfiguration.cs @@ -87,21 +87,5 @@ namespace Greenshot.Plugin.Flickr return false; } - - /// - /// Upgrade certain values - /// - public override void AfterLoad() - { - var coreConfiguration = IniConfig.GetIniSection(); - bool isUpgradeFrom12 = coreConfiguration.LastSaveWithVersion?.StartsWith("1.2") ?? false; - // Clear token when we upgrade from 1.2 to 1.3 as it is no longer valid, discussed in #421 - if (!isUpgradeFrom12) return; - - // We have an upgrade, remove all previous credentials. - FlickrToken = null; - FlickrTokenSecret = null; - } - } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Flickr/FlickrPlugin.cs b/src/Greenshot.Plugin.Flickr/FlickrPlugin.cs index dcad49473..1939fba22 100644 --- a/src/Greenshot.Plugin.Flickr/FlickrPlugin.cs +++ b/src/Greenshot.Plugin.Flickr/FlickrPlugin.cs @@ -30,6 +30,7 @@ using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Plugin; using log4net; +using log4net.Config; namespace Greenshot.Plugin.Flickr { diff --git a/src/Greenshot.Plugin.Flickr/Forms/SettingsForm.Designer.cs b/src/Greenshot.Plugin.Flickr/Forms/SettingsForm.Designer.cs index 5eed52b57..46549f943 100644 --- a/src/Greenshot.Plugin.Flickr/Forms/SettingsForm.Designer.cs +++ b/src/Greenshot.Plugin.Flickr/Forms/SettingsForm.Designer.cs @@ -93,7 +93,7 @@ namespace Greenshot.Plugin.Flickr.Forms { this.combobox_uploadimageformat.FormattingEnabled = true; this.combobox_uploadimageformat.Location = new System.Drawing.Point(174, 6); this.combobox_uploadimageformat.Name = "combobox_uploadimageformat"; - this.combobox_uploadimageformat.PropertyName = nameof(FlickrConfiguration.UploadFormat); + this.combobox_uploadimageformat.PropertyName = "UploadFormat"; this.combobox_uploadimageformat.SectionName = "Flickr"; this.combobox_uploadimageformat.Size = new System.Drawing.Size(251, 21); this.combobox_uploadimageformat.TabIndex = 1; @@ -111,7 +111,7 @@ namespace Greenshot.Plugin.Flickr.Forms { this.checkBoxPublic.LanguageKey = "flickr.public"; this.checkBoxPublic.Location = new System.Drawing.Point(174, 88); this.checkBoxPublic.Name = "checkBoxPublic"; - this.checkBoxPublic.PropertyName = nameof(FlickrConfiguration.IsPublic); + this.checkBoxPublic.PropertyName = "flickrIsPublic"; this.checkBoxPublic.SectionName = "Flickr"; this.checkBoxPublic.Size = new System.Drawing.Size(55, 17); this.checkBoxPublic.TabIndex = 4; @@ -122,7 +122,7 @@ namespace Greenshot.Plugin.Flickr.Forms { this.checkBoxFamily.LanguageKey = "flickr.family"; this.checkBoxFamily.Location = new System.Drawing.Point(265, 88); this.checkBoxFamily.Name = "checkBoxFamily"; - this.checkBoxFamily.PropertyName = nameof(FlickrConfiguration.IsFamily); + this.checkBoxFamily.PropertyName = "flickrIsFamily"; this.checkBoxFamily.SectionName = "Flickr"; this.checkBoxFamily.Size = new System.Drawing.Size(55, 17); this.checkBoxFamily.TabIndex = 5; @@ -133,7 +133,7 @@ namespace Greenshot.Plugin.Flickr.Forms { this.checkBoxFriend.LanguageKey = "flickr.friend"; this.checkBoxFriend.Location = new System.Drawing.Point(350, 88); this.checkBoxFriend.Name = "checkBoxFriend"; - this.checkBoxFriend.PropertyName = nameof(FlickrConfiguration.IsFriend); + this.checkBoxFriend.PropertyName = "flickrIsFriend"; this.checkBoxFriend.SectionName = "Flickr"; this.checkBoxFriend.Size = new System.Drawing.Size(55, 17); this.checkBoxFriend.TabIndex = 6; @@ -155,7 +155,7 @@ namespace Greenshot.Plugin.Flickr.Forms { this.combobox_safetyLevel.FormattingEnabled = true; this.combobox_safetyLevel.Location = new System.Drawing.Point(174, 33); this.combobox_safetyLevel.Name = "combobox_safetyLevel"; - this.combobox_safetyLevel.PropertyName = nameof(FlickrConfiguration.SafetyLevel); + this.combobox_safetyLevel.PropertyName = "SafetyLevel"; this.combobox_safetyLevel.SectionName = "Flickr"; this.combobox_safetyLevel.Size = new System.Drawing.Size(251, 21); this.combobox_safetyLevel.TabIndex = 2; @@ -173,7 +173,7 @@ namespace Greenshot.Plugin.Flickr.Forms { this.checkboxAfterUploadLinkToClipBoard.LanguageKey = "flickr.label_AfterUploadLinkToClipBoard"; this.checkboxAfterUploadLinkToClipBoard.Location = new System.Drawing.Point(173, 116); this.checkboxAfterUploadLinkToClipBoard.Name = "checkboxAfterUploadLinkToClipBoard"; - this.checkboxAfterUploadLinkToClipBoard.PropertyName = nameof(FlickrConfiguration.AfterUploadLinkToClipBoard); + this.checkboxAfterUploadLinkToClipBoard.PropertyName = "AfterUploadLinkToClipBoard"; this.checkboxAfterUploadLinkToClipBoard.SectionName = "Flickr"; this.checkboxAfterUploadLinkToClipBoard.Size = new System.Drawing.Size(104, 17); this.checkboxAfterUploadLinkToClipBoard.TabIndex = 7; @@ -184,7 +184,7 @@ namespace Greenshot.Plugin.Flickr.Forms { this.checkBox_hiddenfromsearch.LanguageKey = "flickr.label_HiddenFromSearch"; this.checkBox_hiddenfromsearch.Location = new System.Drawing.Point(174, 60); this.checkBox_hiddenfromsearch.Name = "checkBox_hiddenfromsearch"; - this.checkBox_hiddenfromsearch.PropertyName = nameof(FlickrConfiguration.HiddenFromSearch); + this.checkBox_hiddenfromsearch.PropertyName = "HiddenFromSearch"; this.checkBox_hiddenfromsearch.SectionName = "Flickr"; this.checkBox_hiddenfromsearch.Size = new System.Drawing.Size(118, 17); this.checkBox_hiddenfromsearch.TabIndex = 3; diff --git a/src/Greenshot.Plugin.Flickr/Greenshot.Plugin.Flickr.csproj b/src/Greenshot.Plugin.Flickr/Greenshot.Plugin.Flickr.csproj index 8bdb8bcb4..5bc629bf1 100644 --- a/src/Greenshot.Plugin.Flickr/Greenshot.Plugin.Flickr.csproj +++ b/src/Greenshot.Plugin.Flickr/Greenshot.Plugin.Flickr.csproj @@ -1,8 +1,4 @@  - - none - false - PreserveNewest diff --git a/src/Greenshot.Plugin.Flickr/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Flickr/Properties/AssemblyInfo.cs index 9701ef654..30eafc83e 100644 --- a/src/Greenshot.Plugin.Flickr/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Flickr/Properties/AssemblyInfo.cs @@ -21,13 +21,11 @@ using System.Reflection; using System.Runtime.InteropServices; -using Greenshot.Base.Interfaces.Plugin; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyDescription("A plugin to upload images to Flickr")] -[assembly: AssemblyPluginIdentifier("Flickr Plugin")] // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. diff --git a/src/Greenshot.Plugin.GooglePhotos/Forms/SettingsForm.Designer.cs b/src/Greenshot.Plugin.GooglePhotos/Forms/SettingsForm.Designer.cs index 23e68012f..7b0325981 100644 --- a/src/Greenshot.Plugin.GooglePhotos/Forms/SettingsForm.Designer.cs +++ b/src/Greenshot.Plugin.GooglePhotos/Forms/SettingsForm.Designer.cs @@ -86,7 +86,7 @@ namespace Greenshot.Plugin.GooglePhotos.Forms { this.combobox_uploadimageformat.FormattingEnabled = true; this.combobox_uploadimageformat.Location = new System.Drawing.Point(197, 12); this.combobox_uploadimageformat.Name = "combobox_uploadimageformat"; - this.combobox_uploadimageformat.PropertyName = nameof(GooglePhotosConfiguration.UploadFormat); + this.combobox_uploadimageformat.PropertyName = "UploadFormat"; this.combobox_uploadimageformat.SectionName = "GooglePhotos"; this.combobox_uploadimageformat.Size = new System.Drawing.Size(225, 21); this.combobox_uploadimageformat.TabIndex = 1; @@ -114,7 +114,7 @@ namespace Greenshot.Plugin.GooglePhotos.Forms { this.checkboxAfterUploadLinkToClipBoard.LanguageKey = "googlephotos.label_AfterUploadLinkToClipBoard"; this.checkboxAfterUploadLinkToClipBoard.Location = new System.Drawing.Point(197, 50); this.checkboxAfterUploadLinkToClipBoard.Name = "checkboxAfterUploadLinkToClipBoard"; - this.checkboxAfterUploadLinkToClipBoard.PropertyName = nameof(GooglePhotosConfiguration.AfterUploadLinkToClipBoard); + this.checkboxAfterUploadLinkToClipBoard.PropertyName = "AfterUploadLinkToClipBoard"; this.checkboxAfterUploadLinkToClipBoard.SectionName = "GooglePhotos"; this.checkboxAfterUploadLinkToClipBoard.Size = new System.Drawing.Size(104, 17); this.checkboxAfterUploadLinkToClipBoard.TabIndex = 2; diff --git a/src/Greenshot.Plugin.GooglePhotos/GooglePhotosConfiguration.cs b/src/Greenshot.Plugin.GooglePhotos/GooglePhotosConfiguration.cs index 5765544b9..91892f136 100644 --- a/src/Greenshot.Plugin.GooglePhotos/GooglePhotosConfiguration.cs +++ b/src/Greenshot.Plugin.GooglePhotos/GooglePhotosConfiguration.cs @@ -20,6 +20,7 @@ using System; using System.Windows.Forms; +using Greenshot.Base.Core; using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; using Greenshot.Plugin.GooglePhotos.Forms; diff --git a/src/Greenshot.Plugin.GooglePhotos/Greenshot.Plugin.GooglePhotos.csproj b/src/Greenshot.Plugin.GooglePhotos/Greenshot.Plugin.GooglePhotos.csproj index 154410dd2..f2bfb8be5 100644 --- a/src/Greenshot.Plugin.GooglePhotos/Greenshot.Plugin.GooglePhotos.csproj +++ b/src/Greenshot.Plugin.GooglePhotos/Greenshot.Plugin.GooglePhotos.csproj @@ -1,8 +1,4 @@  - - none - false - PreserveNewest diff --git a/src/Greenshot.Plugin.GooglePhotos/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.GooglePhotos/Properties/AssemblyInfo.cs index 3ff014500..a49045818 100644 --- a/src/Greenshot.Plugin.GooglePhotos/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.GooglePhotos/Properties/AssemblyInfo.cs @@ -21,17 +21,12 @@ using System.Reflection; using System.Runtime.InteropServices; -using Greenshot.Base.Interfaces.Plugin; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyDescription("A plugin to upload images to GooglePhotos")] -// Still using the old name 'Picasa-Web Plugin' as identifier for backwards compatibility -// TODO: replace plugin identifier with "GooglePhotos Plugin" in the future -[assembly: AssemblyPluginIdentifier("Picasa-Web Plugin")] - // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. [assembly: ComVisible(false)] \ No newline at end of file diff --git a/src/Greenshot.Plugin.Imgur/Forms/SettingsForm.Designer.cs b/src/Greenshot.Plugin.Imgur/Forms/SettingsForm.Designer.cs index 1b9b52c42..f40ee6460 100644 --- a/src/Greenshot.Plugin.Imgur/Forms/SettingsForm.Designer.cs +++ b/src/Greenshot.Plugin.Imgur/Forms/SettingsForm.Designer.cs @@ -86,7 +86,7 @@ namespace Greenshot.Plugin.Imgur.Forms { this.combobox_uploadimageformat.FormattingEnabled = true; this.combobox_uploadimageformat.Location = new System.Drawing.Point(168, 7); this.combobox_uploadimageformat.Name = "combobox_uploadimageformat"; - this.combobox_uploadimageformat.PropertyName = nameof(ImgurConfiguration.UploadFormat); + this.combobox_uploadimageformat.PropertyName = "UploadFormat"; this.combobox_uploadimageformat.SectionName = "Imgur"; this.combobox_uploadimageformat.Size = new System.Drawing.Size(210, 21); this.combobox_uploadimageformat.TabIndex = 1; @@ -115,7 +115,7 @@ namespace Greenshot.Plugin.Imgur.Forms { this.checkbox_anonymous_access.LanguageKey = "imgur.anonymous_access"; this.checkbox_anonymous_access.Location = new System.Drawing.Point(15, 38); this.checkbox_anonymous_access.Name = "checkbox_anonymous_access"; - this.checkbox_anonymous_access.PropertyName = nameof(ImgurConfiguration.AnonymousAccess); + this.checkbox_anonymous_access.PropertyName = "AnonymousAccess"; this.checkbox_anonymous_access.SectionName = "Imgur"; this.checkbox_anonymous_access.Size = new System.Drawing.Size(139, 17); this.checkbox_anonymous_access.TabIndex = 2; @@ -126,7 +126,7 @@ namespace Greenshot.Plugin.Imgur.Forms { this.checkbox_usepagelink.LanguageKey = "imgur.use_page_link"; this.checkbox_usepagelink.Location = new System.Drawing.Point(15, 57); this.checkbox_usepagelink.Name = "checkbox_usepagelink"; - this.checkbox_usepagelink.PropertyName = nameof(ImgurConfiguration.UsePageLink); + this.checkbox_usepagelink.PropertyName = "UsePageLink"; this.checkbox_usepagelink.SectionName = "Imgur"; this.checkbox_usepagelink.Size = new System.Drawing.Size(251, 17); this.checkbox_usepagelink.TabIndex = 3; diff --git a/src/Greenshot.Plugin.Imgur/Greenshot.Plugin.Imgur.csproj b/src/Greenshot.Plugin.Imgur/Greenshot.Plugin.Imgur.csproj index 5cb8e0baa..e0696e270 100644 --- a/src/Greenshot.Plugin.Imgur/Greenshot.Plugin.Imgur.csproj +++ b/src/Greenshot.Plugin.Imgur/Greenshot.Plugin.Imgur.csproj @@ -1,8 +1,4 @@  - - none - false - PreserveNewest diff --git a/src/Greenshot.Plugin.Imgur/ImgurConfiguration.cs b/src/Greenshot.Plugin.Imgur/ImgurConfiguration.cs index 3dc145d0b..7635ee3de 100644 --- a/src/Greenshot.Plugin.Imgur/ImgurConfiguration.cs +++ b/src/Greenshot.Plugin.Imgur/ImgurConfiguration.cs @@ -85,22 +85,6 @@ namespace Greenshot.Plugin.Imgur public Dictionary runtimeImgurHistory = new Dictionary(); public int Credits { get; set; } - /// - /// Upgrade certain values - /// - public override void AfterLoad() - { - var coreConfiguration = IniConfig.GetIniSection(); - bool isUpgradeFrom12 = coreConfiguration.LastSaveWithVersion?.StartsWith("1.2") ?? false; - // Clear token when we upgrade from 1.2 to 1.3 as it is no longer valid, discussed in #421 - if (!isUpgradeFrom12) return; - - // We have an upgrade, remove all previous credentials. - AccessToken = null; - RefreshToken = null; - AccessTokenExpires = default; - } - /// /// Supply values we can't put as defaults /// @@ -109,7 +93,7 @@ namespace Greenshot.Plugin.Imgur public override object GetDefault(string property) => property switch { - nameof(ImgurUploadHistory) => new Dictionary(), + "ImgurUploadHistory" => new Dictionary(), _ => null }; diff --git a/src/Greenshot.Plugin.Imgur/ImgurUtils.cs b/src/Greenshot.Plugin.Imgur/ImgurUtils.cs index 02af34e56..a2156d6d0 100644 --- a/src/Greenshot.Plugin.Imgur/ImgurUtils.cs +++ b/src/Greenshot.Plugin.Imgur/ImgurUtils.cs @@ -179,7 +179,7 @@ namespace Greenshot.Plugin.Imgur { using (var requestStream = webRequest.GetRequestStream()) { - ImageIO.SaveToStream(surfaceToUpload, requestStream, outputSettings); + ImageOutput.SaveToStream(surfaceToUpload, requestStream, outputSettings); } using WebResponse response = webRequest.GetResponse(); @@ -265,8 +265,7 @@ namespace Greenshot.Plugin.Imgur Stream responseStream = response.GetResponseStream(); if (responseStream != null) { - // TODO: Replace with some other code, like the file format handler - imgurInfo.Image = ImageIO.FromStream(responseStream); + imgurInfo.Image = ImageHelper.FromStream(responseStream); } } diff --git a/src/Greenshot.Plugin.Imgur/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Imgur/Properties/AssemblyInfo.cs index 4f9d4afb5..befa881fd 100644 --- a/src/Greenshot.Plugin.Imgur/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Imgur/Properties/AssemblyInfo.cs @@ -21,13 +21,11 @@ using System.Reflection; using System.Runtime.InteropServices; -using Greenshot.Base.Interfaces.Plugin; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyDescription("A plugin to upload images to Imgur")] -[assembly: AssemblyPluginIdentifier("Imgur Plugin")] // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. diff --git a/src/Greenshot.Plugin.Jira/Forms/JiraForm.cs b/src/Greenshot.Plugin.Jira/Forms/JiraForm.cs index 29b75e9bb..5830eb1dc 100644 --- a/src/Greenshot.Plugin.Jira/Forms/JiraForm.cs +++ b/src/Greenshot.Plugin.Jira/Forms/JiraForm.cs @@ -26,7 +26,6 @@ using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; using Dapplo.Jira.Entities; -using Dapplo.Windows.Dpi; using Greenshot.Base.Controls; using Greenshot.Base.Core; using Greenshot.Base.IniFile; @@ -174,7 +173,7 @@ namespace Greenshot.Plugin.Jira.Forms jiraListView.Columns.Add(translation); } - var scaledIconSize = DpiCalculator.ScaleWithDpi(CoreConfig.IconSize, NativeDpiMethods.GetDpi(Handle)); + var scaledIconSize = DpiHelper.ScaleWithDpi(CoreConfig.IconSize, DpiHelper.GetDpi(Handle)); var imageList = new ImageList { ImageSize = scaledIconSize diff --git a/src/Greenshot.Plugin.Jira/Forms/SettingsForm.Designer.cs b/src/Greenshot.Plugin.Jira/Forms/SettingsForm.Designer.cs index 6903f74fd..945abe034 100644 --- a/src/Greenshot.Plugin.Jira/Forms/SettingsForm.Designer.cs +++ b/src/Greenshot.Plugin.Jira/Forms/SettingsForm.Designer.cs @@ -94,7 +94,7 @@ namespace Greenshot.Plugin.Jira.Forms { this.textBoxUrl.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.textBoxUrl.Location = new System.Drawing.Point(164, 21); this.textBoxUrl.Name = "textBoxUrl"; - this.textBoxUrl.PropertyName = nameof(JiraConfiguration.Url); + this.textBoxUrl.PropertyName = "Url"; this.textBoxUrl.SectionName = "Jira"; this.textBoxUrl.Size = new System.Drawing.Size(214, 20); this.textBoxUrl.TabIndex = 6; @@ -105,7 +105,7 @@ namespace Greenshot.Plugin.Jira.Forms { this.combobox_uploadimageformat.FormattingEnabled = true; this.combobox_uploadimageformat.Location = new System.Drawing.Point(164, 47); this.combobox_uploadimageformat.Name = "combobox_uploadimageformat"; - this.combobox_uploadimageformat.PropertyName = nameof(JiraConfiguration.UploadFormat); + this.combobox_uploadimageformat.PropertyName = "UploadFormat"; this.combobox_uploadimageformat.SectionName = "Jira"; this.combobox_uploadimageformat.Size = new System.Drawing.Size(214, 21); this.combobox_uploadimageformat.TabIndex = 8; diff --git a/src/Greenshot.Plugin.Jira/Greenshot.Plugin.Jira.csproj b/src/Greenshot.Plugin.Jira/Greenshot.Plugin.Jira.csproj index d38eb063a..e75002ed7 100644 --- a/src/Greenshot.Plugin.Jira/Greenshot.Plugin.Jira.csproj +++ b/src/Greenshot.Plugin.Jira/Greenshot.Plugin.Jira.csproj @@ -1,8 +1,4 @@  - - none - false - PreserveNewest @@ -10,7 +6,7 @@ - - + + \ No newline at end of file diff --git a/src/Greenshot.Plugin.Jira/JiraConfiguration.cs b/src/Greenshot.Plugin.Jira/JiraConfiguration.cs index 7163924c5..069cc3263 100644 --- a/src/Greenshot.Plugin.Jira/JiraConfiguration.cs +++ b/src/Greenshot.Plugin.Jira/JiraConfiguration.cs @@ -19,6 +19,7 @@ * along with this program. If not, see . */ +using Greenshot.Base.Core; using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; diff --git a/src/Greenshot.Plugin.Jira/JiraConnector.cs b/src/Greenshot.Plugin.Jira/JiraConnector.cs index fbfd42543..ad48a09fa 100644 --- a/src/Greenshot.Plugin.Jira/JiraConnector.cs +++ b/src/Greenshot.Plugin.Jira/JiraConnector.cs @@ -63,8 +63,8 @@ namespace Greenshot.Plugin.Jira var jiraConnector = SimpleServiceProvider.Current.GetInstance(); jiraConnector._jiraClient?.Behaviour.SetConfig(new SvgConfiguration { - Width = CoreConfig.IconSize.Width, - Height = CoreConfig.IconSize.Height + Width = CoreConfig.ScaledIconSize.Width, + Height = CoreConfig.ScaledIconSize.Height }); } }; @@ -112,8 +112,8 @@ namespace Greenshot.Plugin.Jira _jiraClient = JiraClient.Create(new Uri(JiraConfig.Url)); _jiraClient.Behaviour.SetConfig(new SvgConfiguration { - Width = CoreConfig.IconSize.Width, - Height = CoreConfig.IconSize.Height + Width = CoreConfig.ScaledIconSize.Width, + Height = CoreConfig.ScaledIconSize.Height }); _jiraClient.SetBasicAuthentication(user, password); diff --git a/src/Greenshot.Plugin.Jira/JiraMonitor.cs b/src/Greenshot.Plugin.Jira/JiraMonitor.cs index 81a62b759..17cc20d16 100644 --- a/src/Greenshot.Plugin.Jira/JiraMonitor.cs +++ b/src/Greenshot.Plugin.Jira/JiraMonitor.cs @@ -27,9 +27,7 @@ using System.Threading; using System.Threading.Tasks; using Dapplo.Jira; using Dapplo.Log; -using Dapplo.Windows.Desktop; -using Dapplo.Windows.Structs; -using Dapplo.Windows.User32; +using Greenshot.Base.Hooking; namespace Greenshot.Plugin.Jira { @@ -42,7 +40,7 @@ namespace Greenshot.Plugin.Jira { private static readonly LogSource Log = new LogSource(); private readonly Regex _jiraKeyPattern = new Regex(@"[A-Z][A-Z0-9]+\-[0-9]+"); - private readonly IDisposable _monitor; + private readonly WindowsTitleMonitor _monitor; private readonly IList _jiraInstances = new List(); private readonly IDictionary _projectJiraClientMap = new Dictionary(); @@ -58,8 +56,9 @@ namespace Greenshot.Plugin.Jira public JiraMonitor(int maxEntries = 40) { - _monitor = WinEventHook.WindowTitleChangeObservable().Subscribe(wei => MonitorTitleChangeEvent(wei)); _maxEntries = maxEntries; + _monitor = new WindowsTitleMonitor(); + _monitor.TitleChangeEvent += MonitorTitleChangeEvent; } /// @@ -83,6 +82,7 @@ namespace Greenshot.Plugin.Jira } // free managed resources + _monitor.TitleChangeEvent -= MonitorTitleChangeEvent; _monitor.Dispose(); // free native resources if there are any. } @@ -162,10 +162,10 @@ namespace Greenshot.Plugin.Jira /// /// Handle title changes, check for JIRA /// - /// WinEventInfo - private void MonitorTitleChangeEvent(WinEventInfo winEventInfo) + /// + private void MonitorTitleChangeEvent(TitleChangeEventArgs eventArgs) { - string windowTitle = User32Api.GetText(winEventInfo.Handle); + string windowTitle = eventArgs.Title; if (string.IsNullOrEmpty(windowTitle)) { return; diff --git a/src/Greenshot.Plugin.Jira/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Jira/Properties/AssemblyInfo.cs deleted file mode 100644 index 44f8800ce..000000000 --- a/src/Greenshot.Plugin.Jira/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System.Reflection; -using System.Runtime.InteropServices; -using Greenshot.Base.Interfaces.Plugin; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyDescription("A plugin to upload images to Jira")] -[assembly: AssemblyPluginIdentifier("Jira Plugin")] - -// This sets the default COM visibility of types in the assembly to invisible. -// If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] \ No newline at end of file diff --git a/src/Greenshot.Plugin.Office/Com/Ole32Api.cs b/src/Greenshot.Plugin.Office/Com/Ole32Api.cs index 1a57aa6c9..07d807ee2 100644 --- a/src/Greenshot.Plugin.Office/Com/Ole32Api.cs +++ b/src/Greenshot.Plugin.Office/Com/Ole32Api.cs @@ -3,8 +3,8 @@ using System; using System.Runtime.InteropServices; -using Dapplo.Windows.Common.Enums; -using Dapplo.Windows.Common.Extensions; +using Greenshot.Base.Core; +using Greenshot.Base.Core.Enums; namespace Greenshot.Plugin.Office.Com { diff --git a/src/Greenshot.Plugin.Office/Com/OleAut32Api.cs b/src/Greenshot.Plugin.Office/Com/OleAut32Api.cs index ea0f1b0c9..c32b11a19 100644 --- a/src/Greenshot.Plugin.Office/Com/OleAut32Api.cs +++ b/src/Greenshot.Plugin.Office/Com/OleAut32Api.cs @@ -3,8 +3,8 @@ using System; using System.Runtime.InteropServices; -using Dapplo.Windows.Common.Enums; -using Dapplo.Windows.Common.Extensions; +using Greenshot.Base.Core; +using Greenshot.Base.Core.Enums; namespace Greenshot.Plugin.Office.Com { diff --git a/src/Greenshot.Plugin.Office/Destinations/ExcelDestination.cs b/src/Greenshot.Plugin.Office/Destinations/ExcelDestination.cs index 1a671cf7a..cf09272e2 100644 --- a/src/Greenshot.Plugin.Office/Destinations/ExcelDestination.cs +++ b/src/Greenshot.Plugin.Office/Destinations/ExcelDestination.cs @@ -42,8 +42,7 @@ namespace Greenshot.Plugin.Office.Destinations static ExcelDestination() { - ExePath = OfficeUtils.GetOfficeExePath("EXCEL.EXE") ?? PluginUtils.GetExePath("EXCEL.EXE"); - + ExePath = PluginUtils.GetExePath("EXCEL.EXE"); if (ExePath != null && File.Exists(ExePath)) { WindowDetails.AddProcessToExcludeFromFreeze("excel"); @@ -90,7 +89,7 @@ namespace Greenshot.Plugin.Office.Destinations string imageFile = captureDetails.Filename; if (imageFile == null || surface.Modified || !Regex.IsMatch(imageFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) { - imageFile = ImageIO.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); + imageFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); createdFile = true; } @@ -108,7 +107,7 @@ namespace Greenshot.Plugin.Office.Destinations // Cleanup imageFile if we created it here, so less tmp-files are generated and left if (createdFile) { - ImageIO.DeleteNamedTmpFile(imageFile); + ImageOutput.DeleteNamedTmpFile(imageFile); } return exportInformation; diff --git a/src/Greenshot.Plugin.Office/Destinations/OneNoteDestination.cs b/src/Greenshot.Plugin.Office/Destinations/OneNoteDestination.cs index 5f8da7a67..387810494 100644 --- a/src/Greenshot.Plugin.Office/Destinations/OneNoteDestination.cs +++ b/src/Greenshot.Plugin.Office/Destinations/OneNoteDestination.cs @@ -41,7 +41,7 @@ namespace Greenshot.Plugin.Office.Destinations static OneNoteDestination() { - exePath = OfficeUtils.GetOfficeExePath("ONENOTE.EXE") ?? PluginUtils.GetExePath("ONENOTE.EXE"); + exePath = PluginUtils.GetExePath("ONENOTE.EXE"); if (exePath != null && File.Exists(exePath)) { WindowDetails.AddProcessToExcludeFromFreeze("onenote"); diff --git a/src/Greenshot.Plugin.Office/Destinations/OutlookDestination.cs b/src/Greenshot.Plugin.Office/Destinations/OutlookDestination.cs index 3c77ea4f1..da694be62 100644 --- a/src/Greenshot.Plugin.Office/Destinations/OutlookDestination.cs +++ b/src/Greenshot.Plugin.Office/Destinations/OutlookDestination.cs @@ -58,7 +58,8 @@ namespace Greenshot.Plugin.Office.Destinations { IsActiveFlag = true; } - ExePath = OfficeUtils.GetOfficeExePath("OUTLOOK.EXE") ?? PluginUtils.GetExePath("OUTLOOK.EXE"); + + ExePath = PluginUtils.GetExePath("OUTLOOK.EXE"); if (ExePath != null && File.Exists(ExePath)) { WindowDetails.AddProcessToExcludeFromFreeze("outlook"); @@ -159,7 +160,7 @@ namespace Greenshot.Plugin.Office.Destinations string tmpFile = captureDetails.Filename; if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) { - tmpFile = ImageIO.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); + tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); } else { diff --git a/src/Greenshot.Plugin.Office/Destinations/PowerpointDestination.cs b/src/Greenshot.Plugin.Office/Destinations/PowerpointDestination.cs index 010093027..a151d3388 100644 --- a/src/Greenshot.Plugin.Office/Destinations/PowerpointDestination.cs +++ b/src/Greenshot.Plugin.Office/Destinations/PowerpointDestination.cs @@ -45,7 +45,7 @@ namespace Greenshot.Plugin.Office.Destinations static PowerpointDestination() { - ExePath = OfficeUtils.GetOfficeExePath("POWERPNT.EXE") ?? PluginUtils.GetExePath("POWERPNT.EXE"); + ExePath = PluginUtils.GetExePath("POWERPNT.EXE"); if (ExePath != null && File.Exists(ExePath)) { WindowDetails.AddProcessToExcludeFromFreeze("powerpnt"); @@ -114,7 +114,7 @@ namespace Greenshot.Plugin.Office.Destinations Size imageSize = Size.Empty; if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) { - tmpFile = ImageIO.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); + tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); imageSize = surface.Image.Size; } diff --git a/src/Greenshot.Plugin.Office/Destinations/WordDestination.cs b/src/Greenshot.Plugin.Office/Destinations/WordDestination.cs index d6220a302..fd91295be 100644 --- a/src/Greenshot.Plugin.Office/Destinations/WordDestination.cs +++ b/src/Greenshot.Plugin.Office/Destinations/WordDestination.cs @@ -46,7 +46,7 @@ namespace Greenshot.Plugin.Office.Destinations static WordDestination() { - ExePath = OfficeUtils.GetOfficeExePath("WINWORD.EXE") ?? PluginUtils.GetExePath("WINWORD.EXE"); + ExePath = PluginUtils.GetExePath("WINWORD.EXE"); if (ExePath != null && !File.Exists(ExePath)) { ExePath = null; @@ -88,7 +88,7 @@ namespace Greenshot.Plugin.Office.Destinations string tmpFile = captureDetails.Filename; if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) { - tmpFile = ImageIO.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); + tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); } if (_documentCaption != null) @@ -118,7 +118,7 @@ namespace Greenshot.Plugin.Office.Destinations if (!manuallyInitiated) { var documents = _wordExporter.GetWordDocuments().ToList(); - if (documents is { Count: > 0 }) + if (documents != null && documents.Count > 0) { var destinations = new List { diff --git a/src/Greenshot.Plugin.Office/Greenshot.Plugin.Office.csproj b/src/Greenshot.Plugin.Office/Greenshot.Plugin.Office.csproj index 982b86833..fdb555adc 100644 --- a/src/Greenshot.Plugin.Office/Greenshot.Plugin.Office.csproj +++ b/src/Greenshot.Plugin.Office/Greenshot.Plugin.Office.csproj @@ -1,8 +1,4 @@  - - none - false - PreserveNewest @@ -82,23 +78,23 @@ - + true runtime - + true runtime - + true runtime - + true runtime - + true runtime diff --git a/src/Greenshot.Plugin.Office/OfficeExport/ExcelExporter.cs b/src/Greenshot.Plugin.Office/OfficeExport/ExcelExporter.cs index cfffff5f6..48b7d994b 100644 --- a/src/Greenshot.Plugin.Office/OfficeExport/ExcelExporter.cs +++ b/src/Greenshot.Plugin.Office/OfficeExport/ExcelExporter.cs @@ -20,7 +20,7 @@ using System; using System.Collections.Generic; using System.Drawing; -using Dapplo.Windows.User32; +using Greenshot.Base.UnmanagedHelpers; using Greenshot.Plugin.Office.Com; using Greenshot.Plugin.Office.OfficeInterop; using Microsoft.Office.Core; @@ -177,7 +177,7 @@ namespace Greenshot.Plugin.Office.OfficeExport shape.ComObject.ScaleWidth(1, MsoTriState.msoTrue, MsoScaleFrom.msoScaleFromTopLeft); workbook.ComObject.Activate(); using var application = DisposableCom.Create(workbook.ComObject.Application); - User32Api.SetForegroundWindow((IntPtr) application.ComObject.Hwnd); + User32.SetForegroundWindow((IntPtr) application.ComObject.Hwnd); } /// diff --git a/src/Greenshot.Plugin.Office/OfficeExport/OneNoteExporter.cs b/src/Greenshot.Plugin.Office/OfficeExport/OneNoteExporter.cs index 645623780..305888ee2 100644 --- a/src/Greenshot.Plugin.Office/OfficeExport/OneNoteExporter.cs +++ b/src/Greenshot.Plugin.Office/OfficeExport/OneNoteExporter.cs @@ -97,7 +97,7 @@ namespace Greenshot.Plugin.Office.OfficeExport using var pngStream = new MemoryStream(); var pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false); - ImageIO.SaveToStream(surfaceToUpload, pngStream, pngOutputSettings); + ImageOutput.SaveToStream(surfaceToUpload, pngStream, pngOutputSettings); var base64String = Convert.ToBase64String(pngStream.GetBuffer()); var imageXmlStr = string.Format(XmlImageContent, base64String, surfaceToUpload.Image.Width, surfaceToUpload.Image.Height); var pageChangesXml = string.Format(XmlOutline, imageXmlStr, page.Id, OnenoteNamespace2010, page.Name); diff --git a/src/Greenshot.Plugin.Office/OfficeUtils.cs b/src/Greenshot.Plugin.Office/OfficeUtils.cs deleted file mode 100644 index 305611c5d..000000000 --- a/src/Greenshot.Plugin.Office/OfficeUtils.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Linq; -using Microsoft.Win32; - -namespace Greenshot.Plugin.Office -{ - - /// - /// A small utility class for helping with office - /// - internal static class OfficeUtils - { - private static readonly string[] OfficeRootKeys = { @"SOFTWARE\Microsoft\Office", @"SOFTWARE\WOW6432Node\Microsoft\Office" }; - - /// - /// Get the path to the office exe - /// - /// Name of the office executable - public static string GetOfficeExePath(string exeName) - { - string strKeyName = exeName switch - { - "WINWORD.EXE" => "Word", - "EXCEL.EXE" => "Excel", - "POWERPNT.EXE" => "PowerPoint", - "OUTLOOK.EXE" => "Outlook", - "ONENOTE.EXE" => "OneNote", - _ => "" - }; - - foreach (string strRootKey in OfficeRootKeys) - { - using RegistryKey rootKey = Registry.LocalMachine.OpenSubKey(strRootKey); - if (rootKey is null) continue; - - foreach (string officeVersion in rootKey.GetSubKeyNames().Where(r => r.Contains(".")).Reverse()) - { - using RegistryKey installRootKey = Registry.LocalMachine.OpenSubKey($@"{strRootKey}\{officeVersion}\{strKeyName}\InstallRoot"); - if (installRootKey == null) continue; - return $@"{installRootKey.GetValue("Path")}\{exeName}"; - } - } - return null; - } - } -} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Office/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Office/Properties/AssemblyInfo.cs index 6b14a40f5..9dcc75892 100644 --- a/src/Greenshot.Plugin.Office/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Office/Properties/AssemblyInfo.cs @@ -21,13 +21,11 @@ using System.Reflection; using System.Runtime.InteropServices; -using Greenshot.Base.Interfaces.Plugin; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyDescription("A plugin to export images to Office applications")] -[assembly: AssemblyPluginIdentifier("Office Plugin")] // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. diff --git a/src/Greenshot.Plugin.Photobucket/Forms/SettingsForm.Designer.cs b/src/Greenshot.Plugin.Photobucket/Forms/SettingsForm.Designer.cs index 1fff9f5c3..9376f693e 100644 --- a/src/Greenshot.Plugin.Photobucket/Forms/SettingsForm.Designer.cs +++ b/src/Greenshot.Plugin.Photobucket/Forms/SettingsForm.Designer.cs @@ -84,7 +84,7 @@ namespace Greenshot.Plugin.Photobucket.Forms { this.combobox_uploadimageformat.FormattingEnabled = true; this.combobox_uploadimageformat.Location = new System.Drawing.Point(102, 11); this.combobox_uploadimageformat.Name = "combobox_uploadimageformat"; - this.combobox_uploadimageformat.PropertyName = nameof(PhotobucketConfiguration.UploadFormat); + this.combobox_uploadimageformat.PropertyName = "UploadFormat"; this.combobox_uploadimageformat.SectionName = "Photobucket"; this.combobox_uploadimageformat.Size = new System.Drawing.Size(276, 21); this.combobox_uploadimageformat.TabIndex = 1; @@ -102,7 +102,7 @@ namespace Greenshot.Plugin.Photobucket.Forms { this.checkbox_usepagelink.LanguageKey = "photobucket.use_page_link"; this.checkbox_usepagelink.Location = new System.Drawing.Point(15, 43); this.checkbox_usepagelink.Name = "checkbox_usepagelink"; - this.checkbox_usepagelink.PropertyName = nameof(PhotobucketConfiguration.UsePageLink); + this.checkbox_usepagelink.PropertyName = "UsePageLink"; this.checkbox_usepagelink.SectionName = "Photobucket"; this.checkbox_usepagelink.Size = new System.Drawing.Size(251, 17); this.checkbox_usepagelink.TabIndex = 2; diff --git a/src/Greenshot.Plugin.Photobucket/Greenshot.Plugin.Photobucket.csproj b/src/Greenshot.Plugin.Photobucket/Greenshot.Plugin.Photobucket.csproj index d0151b7c0..50e39b62a 100644 --- a/src/Greenshot.Plugin.Photobucket/Greenshot.Plugin.Photobucket.csproj +++ b/src/Greenshot.Plugin.Photobucket/Greenshot.Plugin.Photobucket.csproj @@ -1,8 +1,4 @@  - - none - false - PreserveNewest diff --git a/src/Greenshot.Plugin.Photobucket/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Photobucket/Properties/AssemblyInfo.cs index 231ca594d..783762320 100644 --- a/src/Greenshot.Plugin.Photobucket/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Photobucket/Properties/AssemblyInfo.cs @@ -21,14 +21,12 @@ using System.Reflection; using System.Runtime.InteropServices; -using Greenshot.Base.Interfaces.Plugin; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyDescription("A plugin to upload images to Photobucket")] -[assembly: AssemblyPluginIdentifier("Photobucket Plugin")] // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. diff --git a/src/Greenshot.Plugin.Win10/Destinations/Win10ShareDestination.cs b/src/Greenshot.Plugin.Win10/Destinations/Win10ShareDestination.cs index fa31d3a11..0f2557da2 100644 --- a/src/Greenshot.Plugin.Win10/Destinations/Win10ShareDestination.cs +++ b/src/Greenshot.Plugin.Win10/Destinations/Win10ShareDestination.cs @@ -29,11 +29,9 @@ using System.Windows.Interop; using System.Windows.Media; using Windows.Storage; using Windows.Storage.Streams; -using Dapplo.Windows.Desktop; -using Dapplo.Windows.Enums; -using Dapplo.Windows.User32; using Greenshot.Base.Core; using Greenshot.Base.Core.Enums; +using Greenshot.Base.Hooking; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Plugin; using Greenshot.Plugin.Win10.Internal; @@ -84,22 +82,24 @@ namespace Greenshot.Plugin.Win10.Destinations triggerWindow.Show(); + var focusMonitor = new WindowsOpenCloseMonitor(); var windowHandle = new WindowInteropHelper(triggerWindow).Handle; // This is a bad trick, but don't know how else to do it. // Wait for the focus to return, and depending on the state close the window! - var createDestroyMonitor = WinEventHook.WindowTitleChangeObservable().Subscribe(wei => + focusMonitor.WindowOpenCloseChangeEvent += e => { - if (wei.WinEvent == WinEvents.EVENT_OBJECT_CREATE) + if (e.IsOpen) { - var windowTitle = User32Api.GetText(wei.Handle); - if ("Windows Shell Experience Host" == windowTitle) + if ("Windows Shell Experience Host" == e.Title) { - shareInfo.SharingHwnd = wei.Handle; + shareInfo.SharingHwnd = e.HWnd; } + return; } - if (wei.Handle == shareInfo.SharingHwnd) + + if (e.HWnd == shareInfo.SharingHwnd) { if (shareInfo.ApplicationName != null) { @@ -108,12 +108,12 @@ namespace Greenshot.Plugin.Win10.Destinations shareInfo.ShareTask.TrySetResult(false); } - }); + }; Share(shareInfo, windowHandle, surface, captureDetails).GetAwaiter().GetResult(); Log.Debug("Sharing finished, closing window."); triggerWindow.Close(); - createDestroyMonitor.Dispose(); + focusMonitor.Dispose(); if (string.IsNullOrWhiteSpace(shareInfo.ApplicationName)) { exportInformation.ExportMade = false; @@ -151,7 +151,7 @@ namespace Greenshot.Plugin.Win10.Destinations outputSettings.PreventGreenshotFormat(); // Create capture for export - ImageIO.SaveToStream(surface, imageStream, outputSettings); + ImageOutput.SaveToStream(surface, imageStream, outputSettings); imageStream.Position = 0; Log.Debug("Created RandomAccessStreamReference for the image"); var imageRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(imageStream); @@ -161,7 +161,7 @@ namespace Greenshot.Plugin.Win10.Destinations using (var tmpImageForThumbnail = surface.GetImageForExport()) using (var thumbnail = ImageHelper.CreateThumbnail(tmpImageForThumbnail, 240, 160)) { - ImageIO.SaveToStream(thumbnail, null, thumbnailStream, outputSettings); + ImageOutput.SaveToStream(thumbnail, null, thumbnailStream, outputSettings); thumbnailStream.Position = 0; thumbnailRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(thumbnailStream); Log.Debug("Created RandomAccessStreamReference for the thumbnail"); @@ -172,7 +172,7 @@ namespace Greenshot.Plugin.Win10.Destinations using (var logo = GreenshotResources.GetGreenshotIcon().ToBitmap()) using (var logoThumbnail = ImageHelper.CreateThumbnail(logo, 30, 30)) { - ImageIO.SaveToStream(logoThumbnail, null, logoStream, outputSettings); + ImageOutput.SaveToStream(logoThumbnail, null, logoStream, outputSettings); logoStream.Position = 0; logoRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(logoStream); Log.Info("Created RandomAccessStreamReference for the logo"); diff --git a/src/Greenshot.Plugin.Win10/Greenshot.Plugin.Win10.csproj b/src/Greenshot.Plugin.Win10/Greenshot.Plugin.Win10.csproj index 14126adb3..c1c1729e0 100644 --- a/src/Greenshot.Plugin.Win10/Greenshot.Plugin.Win10.csproj +++ b/src/Greenshot.Plugin.Win10/Greenshot.Plugin.Win10.csproj @@ -1,8 +1,4 @@  - - none - false - PreserveNewest diff --git a/src/Greenshot.Plugin.Win10/Native/DataTransferManagerHelper.cs b/src/Greenshot.Plugin.Win10/Native/DataTransferManagerHelper.cs index 1ef1059c4..a03b07266 100644 --- a/src/Greenshot.Plugin.Win10/Native/DataTransferManagerHelper.cs +++ b/src/Greenshot.Plugin.Win10/Native/DataTransferManagerHelper.cs @@ -22,7 +22,7 @@ using System; using System.Runtime.InteropServices.WindowsRuntime; using Windows.ApplicationModel.DataTransfer; -using Dapplo.Windows.Common.Extensions; +using Greenshot.Base.Core; namespace Greenshot.Plugin.Win10.Native { diff --git a/src/Greenshot.Plugin.Win10/Native/IDataTransferManagerInterOp.cs b/src/Greenshot.Plugin.Win10/Native/IDataTransferManagerInterOp.cs index 408f5f451..bba908bb8 100644 --- a/src/Greenshot.Plugin.Win10/Native/IDataTransferManagerInterOp.cs +++ b/src/Greenshot.Plugin.Win10/Native/IDataTransferManagerInterOp.cs @@ -20,7 +20,7 @@ using System; using System.Runtime.InteropServices; using Windows.ApplicationModel.DataTransfer; -using Dapplo.Windows.Common.Enums; +using Greenshot.Base.Core.Enums; namespace Greenshot.Plugin.Win10.Native { diff --git a/src/Greenshot.Plugin.Win10/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Win10/Properties/AssemblyInfo.cs index 3f4b5bef2..d1b6cbd1b 100644 --- a/src/Greenshot.Plugin.Win10/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Win10/Properties/AssemblyInfo.cs @@ -1,33 +1,10 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System.Reflection; +using System.Reflection; using System.Runtime.InteropServices; -using Greenshot.Base.Interfaces.Plugin; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyDescription("A plug-in for Windows 10 only functionality")] -[assembly: AssemblyPluginIdentifier("Win10 Plugin")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from diff --git a/src/Greenshot.Plugin.Win10/ToastNotificationService.cs b/src/Greenshot.Plugin.Win10/ToastNotificationService.cs index 52005356c..b9120fee7 100644 --- a/src/Greenshot.Plugin.Win10/ToastNotificationService.cs +++ b/src/Greenshot.Plugin.Win10/ToastNotificationService.cs @@ -95,17 +95,7 @@ namespace Greenshot.Plugin.Win10 } // Prepare the toast notifier. Be sure to specify the AppUserModelId on your application's shortcut! - Microsoft.Toolkit.Uwp.Notifications.ToastNotifierCompat toastNotifier = null; - try - { - toastNotifier = ToastNotificationManagerCompat.CreateToastNotifier(); - } - catch (Exception ex) - { - Log.Warn("Could not create a toast notifier.", ex); - - return; - } + var toastNotifier = ToastNotificationManagerCompat.CreateToastNotifier(); // Here is an interesting article on reading the settings: https://www.rudyhuyn.com/blog/2018/02/10/toastnotifier-and-settings-careful-with-non-uwp-applications/ try diff --git a/src/Greenshot.Plugin.Win10/Win10OcrProvider.cs b/src/Greenshot.Plugin.Win10/Win10OcrProvider.cs index e33909581..77aa0d663 100644 --- a/src/Greenshot.Plugin.Win10/Win10OcrProvider.cs +++ b/src/Greenshot.Plugin.Win10/Win10OcrProvider.cs @@ -26,7 +26,6 @@ using System.Threading.Tasks; using Windows.Graphics.Imaging; using Windows.Media.Ocr; using Windows.Storage.Streams; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Core; using Greenshot.Base.Core.Enums; using Greenshot.Base.Effects; @@ -98,7 +97,7 @@ namespace Greenshot.Plugin.Win10 IEffect effect = new ResizeCanvasEffect(addedWidth, addedWidth, addedHeight, addedHeight); outputSettings.Effects.Add(effect); } - ImageIO.SaveToStream(surface, imageStream, outputSettings); + ImageOutput.SaveToStream(surface, imageStream, outputSettings); imageStream.Position = 0; var randomAccessStream = imageStream.AsRandomAccessStream(); @@ -118,7 +117,7 @@ namespace Greenshot.Plugin.Win10 OcrInformation result; using (var imageStream = new MemoryStream()) { - ImageIO.SaveToStream(image, null, imageStream, new SurfaceOutputSettings()); + ImageOutput.SaveToStream(image, null, imageStream, new SurfaceOutputSettings()); imageStream.Position = 0; var randomAccessStream = imageStream.AsRandomAccessStream(); @@ -170,7 +169,7 @@ namespace Greenshot.Plugin.Win10 for (var index = 0; index < ocrLine.Words.Count; index++) { var ocrWord = ocrLine.Words[index]; - var location = new NativeRect((int) ocrWord.BoundingRect.X, (int) ocrWord.BoundingRect.Y, + var location = new Rectangle((int) ocrWord.BoundingRect.X, (int) ocrWord.BoundingRect.Y, (int) ocrWord.BoundingRect.Width, (int) ocrWord.BoundingRect.Height); var word = line.Words[index]; diff --git a/src/Greenshot.sln b/src/Greenshot.sln index b71e79bd3..91ddf4314 100644 --- a/src/Greenshot.sln +++ b/src/Greenshot.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.7.34009.444 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29728.190 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Greenshot", "Greenshot\Greenshot.csproj", "{CD642BF4-D815-4D67-A0B5-C69F0B8231AF}" ProjectSection(ProjectDependencies) = postProject @@ -48,7 +48,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\azure-pipelines.yml = ..\azure-pipelines.yml Directory.Build.props = Directory.Build.props Directory.Build.targets = Directory.Build.targets - ..\.github\workflows\release.yml = ..\.github\workflows\release.yml EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Greenshot.Editor", "Greenshot.Editor\Greenshot.Editor.csproj", "{148D3C8B-D6EC-4A7D-80E9-243A81F19DD2}" diff --git a/src/Greenshot/App.config b/src/Greenshot/App.config index ab8a7c40c..3b81b058d 100644 --- a/src/Greenshot/App.config +++ b/src/Greenshot/App.config @@ -1,6 +1,7 @@ + @@ -11,7 +12,8 @@ - + + \ No newline at end of file diff --git a/src/Greenshot/Controls/ContextMenuToolStripProfessionalRenderer.cs b/src/Greenshot/Controls/ContextMenuToolStripProfessionalRenderer.cs index 0a9a511c7..2c0446d09 100644 --- a/src/Greenshot/Controls/ContextMenuToolStripProfessionalRenderer.cs +++ b/src/Greenshot/Controls/ContextMenuToolStripProfessionalRenderer.cs @@ -21,11 +21,8 @@ using System.Drawing; using System.Windows.Forms; -using Dapplo.Windows.Common.Structs; -using Dapplo.Windows.Dpi; using Greenshot.Base.Core; using Greenshot.Base.IniFile; -using Greenshot.Base.Interfaces; namespace Greenshot.Controls { @@ -34,25 +31,19 @@ namespace Greenshot.Controls /// public class ContextMenuToolStripProfessionalRenderer : ToolStripProfessionalRenderer { - private readonly IProvideDeviceDpi _provideDeviceDpi; private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); private static Image _scaledCheckbox; - public ContextMenuToolStripProfessionalRenderer(IProvideDeviceDpi provideDeviceDpi) - { - _provideDeviceDpi = provideDeviceDpi; - } protected override void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e) { - var newSize = DpiCalculator.ScaleWithDpi(CoreConfig.IconSize, _provideDeviceDpi.DeviceDpi); - if (_scaledCheckbox == null || _scaledCheckbox.Size != newSize) + if (_scaledCheckbox == null || _scaledCheckbox.Size != CoreConfig.ScaledIconSize) { _scaledCheckbox?.Dispose(); - _scaledCheckbox = ImageHelper.ResizeImage(e.Image, true, newSize.Width, newSize.Height, null); + _scaledCheckbox = ImageHelper.ResizeImage(e.Image, true, CoreConfig.ScaledIconSize.Width, CoreConfig.ScaledIconSize.Height, null); } - NativeRect old = e.ImageRectangle; - ToolStripItemImageRenderEventArgs clone = new ToolStripItemImageRenderEventArgs(e.Graphics, e.Item, _scaledCheckbox, new NativeRect(old.X, 0, old.Width, old.Height)); + Rectangle old = e.ImageRectangle; + ToolStripItemImageRenderEventArgs clone = new ToolStripItemImageRenderEventArgs(e.Graphics, e.Item, _scaledCheckbox, new Rectangle(old.X, 0, old.Width, old.Height)); base.OnRenderItemCheck(clone); } } diff --git a/src/Greenshot/Destinations/FileDestination.cs b/src/Greenshot/Destinations/FileDestination.cs index 64f8f21a9..9389c3d26 100644 --- a/src/Greenshot/Destinations/FileDestination.cs +++ b/src/Greenshot/Destinations/FileDestination.cs @@ -68,7 +68,7 @@ namespace Greenshot.Destinations overwrite = true; Log.InfoFormat("Using previous filename"); fullPath = captureDetails.Filename; - outputSettings.Format = ImageIO.FormatForFilename(fullPath); + outputSettings.Format = ImageOutput.FormatForFilename(fullPath); } else { @@ -87,7 +87,7 @@ namespace Greenshot.Destinations // This is done for e.g. bugs #2974608, #2963943, #2816163, #2795317, #2789218, #3004642 try { - ImageIO.Save(surface, fullPath, overwrite, outputSettings, CoreConfig.OutputFileCopyPathToClipboard); + ImageOutput.Save(surface, fullPath, overwrite, outputSettings, CoreConfig.OutputFileCopyPathToClipboard); outputMade = true; } catch (ArgumentException ex1) @@ -95,7 +95,7 @@ namespace Greenshot.Destinations // Our generated filename exists, display 'save-as' Log.InfoFormat("Not overwriting: {0}", ex1.Message); // when we don't allow to overwrite present a new SaveWithDialog - fullPath = ImageIO.SaveWithDialog(surface, captureDetails); + fullPath = ImageOutput.SaveWithDialog(surface, captureDetails); outputMade = fullPath != null; } catch (Exception ex2) @@ -104,7 +104,7 @@ namespace Greenshot.Destinations // Show the problem MessageBox.Show(Language.GetString(LangKey.error_save), Language.GetString(LangKey.error)); // when save failed we present a SaveWithDialog - fullPath = ImageIO.SaveWithDialog(surface, captureDetails); + fullPath = ImageOutput.SaveWithDialog(surface, captureDetails); outputMade = fullPath != null; } diff --git a/src/Greenshot/Destinations/FileWithDialogDestination.cs b/src/Greenshot/Destinations/FileWithDialogDestination.cs index 72924689f..3a8520fb0 100644 --- a/src/Greenshot/Destinations/FileWithDialogDestination.cs +++ b/src/Greenshot/Destinations/FileWithDialogDestination.cs @@ -57,7 +57,7 @@ namespace Greenshot.Destinations { ExportInformation exportInformation = new ExportInformation(Designation, Description); // Bug #2918756 don't overwrite path if SaveWithDialog returns null! - var savedTo = ImageIO.SaveWithDialog(surface, captureDetails); + var savedTo = ImageOutput.SaveWithDialog(surface, captureDetails); if (savedTo != null) { exportInformation.ExportMade = true; diff --git a/src/Greenshot/Forms/AboutForm.cs b/src/Greenshot/Forms/AboutForm.cs index a9cfc2cfc..edb00a6ac 100644 --- a/src/Greenshot/Forms/AboutForm.cs +++ b/src/Greenshot/Forms/AboutForm.cs @@ -28,7 +28,6 @@ using System.Drawing.Imaging; using System.IO; using System.Security.Permissions; using System.Windows.Forms; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Core; using Greenshot.Base.IniFile; using Greenshot.Configuration; @@ -70,43 +69,43 @@ namespace Greenshot.Forms /// /// The location of every dot in the "G" /// - private readonly List _gSpots = new List + private readonly List _gSpots = new List { // Top row - new(P2, P1), // 0 - new(P3, P1), // 1 - new(P4, P1), // 2 - new(P5, P1), // 3 - new(P6, P1), // 4 + new Point(P2, P1), // 0 + new Point(P3, P1), // 1 + new Point(P4, P1), // 2 + new Point(P5, P1), // 3 + new Point(P6, P1), // 4 // Second row - new(P1, P2), // 5 - new(P2, P2), // 6 + new Point(P1, P2), // 5 + new Point(P2, P2), // 6 // Third row - new(P1, P3), // 7 - new(P2, P3), // 8 + new Point(P1, P3), // 7 + new Point(P2, P3), // 8 // Fourth row - new(P1, P4), // 9 - new(P2, P4), // 10 - new(P5, P4), // 11 - new(P6, P4), // 12 - new(P7, P4), // 13 + new Point(P1, P4), // 9 + new Point(P2, P4), // 10 + new Point(P5, P4), // 11 + new Point(P6, P4), // 12 + new Point(P7, P4), // 13 // Fifth row - new(P1, P5), // 14 - new(P2, P5), // 15 - new(P6, P5), // 16 - new(P7, P5), // 17 + new Point(P1, P5), // 14 + new Point(P2, P5), // 15 + new Point(P6, P5), // 16 + new Point(P7, P5), // 17 // Sixth row - new(P1, P6), // 18 - new(P2, P6), // 19 - new(P3, P6), // 20 - new(P4, P6), // 21 - new(P5, P6), // 22 - new(P6, P6) // 23 + new Point(P1, P6), // 18 + new Point(P2, P6), // 19 + new Point(P3, P6), // 20 + new Point(P4, P6), // 21 + new Point(P5, P6), // 22 + new Point(P6, P6) // 23 }; // 0 1 2 3 4 @@ -166,7 +165,7 @@ namespace Greenshot.Forms for (int index = 0; index < _gSpots.Count; index++) { // Read the pixels in the order of the flow - NativePoint gSpot = _gSpots[_flowOrder[index]]; + Point gSpot = _gSpots[_flowOrder[index]]; // Create the animation, first we do nothing (on the final destination) RectangleAnimator pixelAnimation; @@ -177,15 +176,15 @@ namespace Greenshot.Forms if (IsTerminalServerSession) { // No animation - pixelAnimation = new RectangleAnimator(new NativeRect(gSpot.X, gSpot.Y, W - 2, W - 2), new NativeRect(gSpot.X, gSpot.Y, W - 2, W - 2), 1, EasingType.Cubic, EasingMode.EaseIn); + pixelAnimation = new RectangleAnimator(new Rectangle(gSpot.X, gSpot.Y, W - 2, W - 2), new Rectangle(gSpot.X, gSpot.Y, W - 2, W - 2), 1, EasingType.Cubic, EasingMode.EaseIn); } else { // Create the animation, first we do nothing (on the final destination) - NativeRect standingStill = new NativeRect(gSpot.X + offset, gSpot.Y + offset, 0, 0); + Rectangle standingStill = new Rectangle(gSpot.X + offset, gSpot.Y + offset, 0, 0); pixelAnimation = new RectangleAnimator(standingStill, standingStill, pixelWaitFrames, EasingType.Quintic, EasingMode.EaseIn); // And than we size to the wanted size. - pixelAnimation.QueueDestinationLeg(new NativeRect(gSpot.X, gSpot.Y, W - 2, W - 2), frames); + pixelAnimation.QueueDestinationLeg(new Rectangle(gSpot.X, gSpot.Y, W - 2, W - 2), frames); } // Increase the wait frames @@ -219,7 +218,7 @@ namespace Greenshot.Forms /// private void LinkLabelClicked(object sender, LinkLabelLinkClickedEventArgs e) { - if (sender is not LinkLabel linkLabel) return; + if (!(sender is LinkLabel linkLabel)) return; var link = linkLabel.Tag?.ToString() ?? linkLabel.Text; try { diff --git a/src/Greenshot/Forms/CaptureForm.cs b/src/Greenshot/Forms/CaptureForm.cs index 107aa9096..f8b2c004b 100644 --- a/src/Greenshot/Forms/CaptureForm.cs +++ b/src/Greenshot/Forms/CaptureForm.cs @@ -30,15 +30,14 @@ using System.Security.Permissions; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; -using Dapplo.Windows.User32; -using Dapplo.Windows.User32.Enums; using Greenshot.Base.Controls; using Greenshot.Base.Core; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Ocr; +using Greenshot.Base.UnmanagedHelpers; +using Greenshot.Base.UnmanagedHelpers.Enums; +using Greenshot.Editor.Helpers; namespace Greenshot.Forms { @@ -73,15 +72,15 @@ namespace Greenshot.Forms private int _mX; private int _mY; - private NativePoint _mouseMovePos = NativePoint.Empty; - private NativePoint _cursorPos; + private Point _mouseMovePos = Point.Empty; + private Point _cursorPos; private CaptureMode _captureMode; private readonly List _windows; private WindowDetails _selectedCaptureWindow; private bool _mouseDown; - private NativeRect _captureRect = NativeRect.Empty; + private Rectangle _captureRect = Rectangle.Empty; private readonly ICapture _capture; - private NativePoint _previousMousePos = NativePoint.Empty; + private Point _previousMousePos = Point.Empty; private FixMode _fixMode = FixMode.None; private RectangleAnimator _windowAnimator; private RectangleAnimator _zoomAnimator; @@ -92,7 +91,7 @@ namespace Greenshot.Forms /// /// Property to access the selected capture rectangle /// - public NativeRect CaptureRectangle => _captureRect; + public Rectangle CaptureRectangle => _captureRect; /// /// Property to access the used capture mode @@ -182,7 +181,7 @@ namespace Greenshot.Forms // Initialize the animations, the window capture zooms out from the cursor to the window under the cursor if (_captureMode == CaptureMode.Window) { - _windowAnimator = new RectangleAnimator(new NativeRect(_cursorPos, Size.Empty), _captureRect, FramesForMillis(700), EasingType.Quintic, EasingMode.EaseOut); + _windowAnimator = new RectangleAnimator(new Rectangle(_cursorPos, Size.Empty), _captureRect, FramesForMillis(700), EasingType.Quintic, EasingMode.EaseOut); } // Set the zoomer animation @@ -197,7 +196,7 @@ namespace Greenshot.Forms private void CaptureForm_Resize(object sender, EventArgs e) { Log.DebugFormat("Resize was called, new size: {0}", this.Bounds); - if (_capture.ScreenBounds.Equals(Bounds)) + if (Bounds.Equals(_capture.ScreenBounds)) { // We have the correct size return; @@ -226,12 +225,12 @@ namespace Greenshot.Forms if (isOn) { // Initialize the zoom with a invalid position - _zoomAnimator = new RectangleAnimator(NativeRect.Empty, new NativeRect(int.MaxValue, int.MaxValue, 0, 0), FramesForMillis(1000), EasingType.Quintic, EasingMode.EaseOut); + _zoomAnimator = new RectangleAnimator(Rectangle.Empty, new Rectangle(int.MaxValue, int.MaxValue, 0, 0), FramesForMillis(1000), EasingType.Quintic, EasingMode.EaseOut); VerifyZoomAnimation(_cursorPos, false); } else { - _zoomAnimator?.ChangeDestination(new NativeRect(NativePoint.Empty, NativeSize.Empty), FramesForMillis(1000)); + _zoomAnimator?.ChangeDestination(new Rectangle(Point.Empty, Size.Empty), FramesForMillis(1000)); } } @@ -320,8 +319,8 @@ namespace Greenshot.Forms // "Fade out" Zoom InitializeZoomer(false); // "Fade in" window - _windowAnimator = new RectangleAnimator(new NativeRect(_cursorPos, NativeSize.Empty), _captureRect, FramesForMillis(700), EasingType.Quintic, EasingMode.EaseOut); - _captureRect = NativeRect.Empty; + _windowAnimator = new RectangleAnimator(new Rectangle(_cursorPos, Size.Empty), _captureRect, FramesForMillis(700), EasingType.Quintic, EasingMode.EaseOut); + _captureRect = Rectangle.Empty; Invalidate(); break; case CaptureMode.Text: @@ -333,10 +332,10 @@ namespace Greenshot.Forms // Set the region capture mode _captureMode = CaptureMode.Region; // "Fade out" window - _windowAnimator.ChangeDestination(new NativeRect(_cursorPos, NativeSize.Empty), FramesForMillis(700)); + _windowAnimator.ChangeDestination(new Rectangle(_cursorPos, Size.Empty), FramesForMillis(700)); // Fade in zoom InitializeZoomer(Conf.ZoomerEnabled); - _captureRect = NativeRect.Empty; + _captureRect = Rectangle.Empty; Invalidate(); break; } @@ -404,7 +403,7 @@ namespace Greenshot.Forms private void HandleMouseDown() { - NativePoint tmpCursorLocation = WindowCapture.GetCursorLocationRelativeToScreenBounds(); + Point tmpCursorLocation = WindowCapture.GetCursorLocationRelativeToScreenBounds(); _mX = tmpCursorLocation.X; _mY = tmpCursorLocation.Y; _mouseDown = true; @@ -425,11 +424,10 @@ namespace Greenshot.Forms else if (_captureRect.Height > 0 && _captureRect.Width > 0) { // correct the GUI width to real width if Region mode - if (_captureMode is CaptureMode.Region or CaptureMode.Text) + if (_captureMode == CaptureMode.Region || _captureMode == CaptureMode.Text) { - // Correct the rectangle size, by making it 1 pixel bigger - // We cannot use inflate, this would make the rect bigger to all sizes. - _captureRect = new NativeRect(_captureRect.Left, _captureRect.Top, _captureRect.Width+1, _captureRect.Height+1); + _captureRect.Width += 1; + _captureRect.Height += 1; } // Go and process the capture @@ -438,7 +436,7 @@ namespace Greenshot.Forms else if (_captureMode == CaptureMode.Text && IsWordUnderCursor(_mouseMovePos)) { // Handle a click on a single word - _captureRect = new NativeRect(_mouseMovePos, new NativeSize(1, 1)); + _captureRect = new Rectangle(_mouseMovePos, new Size(1, 1)); // Go and process the capture DialogResult = DialogResult.OK; } @@ -449,11 +447,11 @@ namespace Greenshot.Forms } /// - /// Check if there is a word under the specified location + /// /// - /// NativePoint - /// bool true if there is a word - private bool IsWordUnderCursor(NativePoint location) + /// + /// + private bool IsWordUnderCursor(Point cursorLocation) { if (_captureMode != CaptureMode.Text || _capture.CaptureDetails.OcrInformation == null) return false; @@ -464,10 +462,10 @@ namespace Greenshot.Forms var lineBounds = line.CalculatedBounds; if (lineBounds.IsEmpty) continue; // Highlight the text which is selected - if (!lineBounds.Contains(location)) continue; + if (!lineBounds.Contains(cursorLocation)) continue; foreach (var word in line.Words) { - if (word.Bounds.Contains(location)) + if (word.Bounds.Contains(cursorLocation)) { return true; } @@ -495,7 +493,7 @@ namespace Greenshot.Forms /// /// /// - private NativePoint FixMouseCoordinates(NativePoint currentMouse) + private Point FixMouseCoordinates(Point currentMouse) { if (_fixMode == FixMode.Initiated) { @@ -510,11 +508,11 @@ namespace Greenshot.Forms } else if (_fixMode == FixMode.Vertical) { - currentMouse = new NativePoint(currentMouse.X, _previousMousePos.Y); + currentMouse = new Point(currentMouse.X, _previousMousePos.Y); } else if (_fixMode == FixMode.Horizontal) { - currentMouse = new NativePoint(_previousMousePos.X, currentMouse.Y); + currentMouse = new Point(_previousMousePos.X, currentMouse.Y); } _previousMousePos = currentMouse; @@ -529,7 +527,7 @@ namespace Greenshot.Forms private void OnMouseMove(object sender, MouseEventArgs e) { // Make sure the mouse coordinates are fixed, when pressing shift - var mouseMovePos = FixMouseCoordinates(User32Api.GetCursorLocation()); + var mouseMovePos = FixMouseCoordinates(User32.GetCursorLocation()); _mouseMovePos = WindowCapture.GetLocationRelativeToScreenBounds(mouseMovePos); } @@ -553,7 +551,7 @@ namespace Greenshot.Forms /// protected override void Animate() { - NativePoint lastPos = _cursorPos; + Point lastPos = _cursorPos; _cursorPos = _mouseMovePos; if (_selectedCaptureWindow != null && lastPos.Equals(_cursorPos) && !IsAnimating(_zoomAnimator) && !IsAnimating(_windowAnimator)) @@ -577,11 +575,11 @@ namespace Greenshot.Forms if ((_captureMode == CaptureMode.Region || _captureMode == CaptureMode.Text) && _mouseDown) { - _captureRect = new NativeRect(_cursorPos.X, _cursorPos.Y, _mX - _cursorPos.X, _mY - _cursorPos.Y).Normalize(); + _captureRect = GuiRectangle.GetGuiRectangle(_cursorPos.X, _cursorPos.Y, _mX - _cursorPos.X, _mY - _cursorPos.Y); } // Iterate over the found windows and check if the current location is inside a window - NativePoint cursorPosition = Cursor.Position; + Point cursorPosition = Cursor.Position; _selectedCaptureWindow = null; lock (_windows) { @@ -607,11 +605,11 @@ namespace Greenshot.Forms // Here we want to capture the window which is under the mouse _captureRect = _selectedCaptureWindow.WindowRectangle; // As the ClientRectangle is not in Bitmap coordinates, we need to correct. - _captureRect = _captureRect.Offset(-_capture.ScreenBounds.Location.X, -_capture.ScreenBounds.Location.Y); + _captureRect.Offset(-_capture.ScreenBounds.Location.X, -_capture.ScreenBounds.Location.Y); } } - NativeRect invalidateRectangle; + Rectangle invalidateRectangle; if (_mouseDown && (_captureMode != CaptureMode.Window)) { int x1 = Math.Min(_mX, lastPos.X); @@ -642,32 +640,32 @@ namespace Greenshot.Forms y1 -= measureHeight.Height + 10; } - invalidateRectangle = new NativeRect(x1, y1, x2 - x1, y2 - y1); + invalidateRectangle = new Rectangle(x1, y1, x2 - x1, y2 - y1); Invalidate(invalidateRectangle); } else if (_captureMode != CaptureMode.Window) { if (!IsTerminalServerSession) { - var allScreenBounds = DisplayInfo.ScreenBounds; - allScreenBounds = allScreenBounds.MoveTo(WindowCapture.GetLocationRelativeToScreenBounds(allScreenBounds.Location)); + Rectangle allScreenBounds = WindowCapture.GetScreenBounds(); + allScreenBounds.Location = WindowCapture.GetLocationRelativeToScreenBounds(allScreenBounds.Location); if (verticalMove) { // Before - invalidateRectangle = new NativeRect(allScreenBounds.Left, lastPos.Y - 2, Width + 2, 45).Normalize(); + invalidateRectangle = GuiRectangle.GetGuiRectangle(allScreenBounds.Left, lastPos.Y - 2, Width + 2, 45); Invalidate(invalidateRectangle); // After - invalidateRectangle = new NativeRect(allScreenBounds.Left, _cursorPos.Y - 2, Width + 2, 45).Normalize(); + invalidateRectangle = GuiRectangle.GetGuiRectangle(allScreenBounds.Left, _cursorPos.Y - 2, Width + 2, 45); Invalidate(invalidateRectangle); } if (horizontalMove) { // Before - invalidateRectangle = new NativeRect(lastPos.X - 2, allScreenBounds.Top, 75, Height + 2).Normalize(); + invalidateRectangle = GuiRectangle.GetGuiRectangle(lastPos.X - 2, allScreenBounds.Top, 75, Height + 2); Invalidate(invalidateRectangle); // After - invalidateRectangle = new NativeRect(_cursorPos.X - 2, allScreenBounds.Top, 75, Height + 2).Normalize(); + invalidateRectangle = GuiRectangle.GetGuiRectangle(_cursorPos.X - 2, allScreenBounds.Top, 75, Height + 2); Invalidate(invalidateRectangle); } } @@ -687,9 +685,11 @@ namespace Greenshot.Forms // Check if the animation needs to be drawn if (IsAnimating(_windowAnimator)) { - invalidateRectangle = _windowAnimator.Current.Inflate(safetySize, safetySize); + invalidateRectangle = _windowAnimator.Current; + invalidateRectangle.Inflate(safetySize, safetySize); Invalidate(invalidateRectangle); - invalidateRectangle = _windowAnimator.Next().Inflate(safetySize, safetySize); + invalidateRectangle = _windowAnimator.Next(); + invalidateRectangle.Inflate(safetySize, safetySize); Invalidate(invalidateRectangle); // Check if this was the last of the windows animations in the normal region capture. if (_captureMode != CaptureMode.Window && !IsAnimating(_windowAnimator)) @@ -701,7 +701,8 @@ namespace Greenshot.Forms if (_zoomAnimator != null && (IsAnimating(_zoomAnimator) || _captureMode != CaptureMode.Window)) { // Make sure we invalidate the old zoom area - invalidateRectangle = _zoomAnimator.Current.Offset(lastPos); + invalidateRectangle = _zoomAnimator.Current; + invalidateRectangle.Offset(lastPos); Invalidate(invalidateRectangle); // Only verify if we are really showing the zoom, not the outgoing animation if (Conf.ZoomerEnabled && _captureMode != CaptureMode.Window) @@ -712,7 +713,7 @@ namespace Greenshot.Forms // The following logic is not needed, next always returns the current if there are no frames left // but it makes more sense if we want to change something in the logic invalidateRectangle = IsAnimating(_zoomAnimator) ? _zoomAnimator.Next() : _zoomAnimator.Current; - invalidateRectangle = invalidateRectangle.Offset(_cursorPos); + invalidateRectangle.Offset(_cursorPos); Invalidate(invalidateRectangle); } @@ -721,7 +722,7 @@ namespace Greenshot.Forms { var ocrInfo = _capture.CaptureDetails.OcrInformation; - invalidateRectangle = NativeRect.Empty; + invalidateRectangle = Rectangle.Empty; foreach (var line in ocrInfo.Lines) { var lineBounds = line.CalculatedBounds; @@ -732,20 +733,22 @@ namespace Greenshot.Forms if (_mouseDown) { // Highlight the text which is selected - if (!lineBounds.IntersectsWith(_captureRect)) continue; - foreach (var word in line.Words) + if (lineBounds.IntersectsWith(_captureRect)) { - if (!word.Bounds.IntersectsWith(_captureRect)) + foreach (var word in line.Words) { - continue; - } - if (invalidateRectangle.IsEmpty) - { - invalidateRectangle = word.Bounds; - } - else - { - invalidateRectangle = invalidateRectangle.Union(word.Bounds); + if (!word.Bounds.IntersectsWith(_captureRect)) + { + continue; + } + if (invalidateRectangle.IsEmpty) + { + invalidateRectangle = word.Bounds; + } + else + { + invalidateRectangle = Rectangle.Union(invalidateRectangle, word.Bounds); + } } } } @@ -760,7 +763,7 @@ namespace Greenshot.Forms } else { - invalidateRectangle = invalidateRectangle.Union(word.Bounds); + invalidateRectangle = Rectangle.Union(invalidateRectangle, word.Bounds); } break; @@ -791,51 +794,52 @@ namespace Greenshot.Forms /// /// preferred destination location for the zoom area /// false to try to find a location which is neither out of screen bounds nor intersects with the selected rectangle - private void VerifyZoomAnimation(NativePoint pos, bool allowZoomOverCaptureRect) + private void VerifyZoomAnimation(Point pos, bool allowZoomOverCaptureRect) { - NativeRect screenBounds = DisplayInfo.GetBounds(MousePosition); + Rectangle screenBounds = Screen.GetBounds(MousePosition); // convert to be relative to top left corner of all screen bounds - screenBounds = screenBounds.MoveTo(WindowCapture.GetLocationRelativeToScreenBounds(screenBounds.Location)); + screenBounds.Location = WindowCapture.GetLocationRelativeToScreenBounds(screenBounds.Location); int relativeZoomSize = Math.Min(screenBounds.Width, screenBounds.Height) / 5; // Make sure the final size is a plural of 4, this makes it look better relativeZoomSize -= relativeZoomSize % 4; - var zoomSize = new NativeSize(relativeZoomSize, relativeZoomSize); - var zoomOffset = new NativePoint(20, 20); + Size zoomSize = new Size(relativeZoomSize, relativeZoomSize); + Point zoomOffset = new Point(20, 20); - NativeRect targetRectangle = _zoomAnimator.Final.Offset(pos); + Rectangle targetRectangle = _zoomAnimator.Final; + targetRectangle.Offset(pos); if (screenBounds.Contains(targetRectangle) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(targetRectangle))) { return; } - var destinationLocation = NativePoint.Empty; - var tl = new NativeRect(pos.X - (zoomOffset.X + zoomSize.Width), pos.Y - (zoomOffset.Y + zoomSize.Height), zoomSize.Width, zoomSize.Height); - var tr = new NativeRect(pos.X + zoomOffset.X, pos.Y - (zoomOffset.Y + zoomSize.Height), zoomSize.Width, zoomSize.Height); - var bl = new NativeRect(pos.X - (zoomOffset.X + zoomSize.Width), pos.Y + zoomOffset.Y, zoomSize.Width, zoomSize.Height); - var br = new NativeRect(pos.X + zoomOffset.X, pos.Y + zoomOffset.Y, zoomSize.Width, zoomSize.Height); + Point destinationLocation = Point.Empty; + Rectangle tl = new Rectangle(pos.X - (zoomOffset.X + zoomSize.Width), pos.Y - (zoomOffset.Y + zoomSize.Height), zoomSize.Width, zoomSize.Height); + Rectangle tr = new Rectangle(pos.X + zoomOffset.X, pos.Y - (zoomOffset.Y + zoomSize.Height), zoomSize.Width, zoomSize.Height); + Rectangle bl = new Rectangle(pos.X - (zoomOffset.X + zoomSize.Width), pos.Y + zoomOffset.Y, zoomSize.Width, zoomSize.Height); + Rectangle br = new Rectangle(pos.X + zoomOffset.X, pos.Y + zoomOffset.Y, zoomSize.Width, zoomSize.Height); if (screenBounds.Contains(br) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(br))) { - destinationLocation = new NativePoint(zoomOffset.X, zoomOffset.Y); + destinationLocation = new Point(zoomOffset.X, zoomOffset.Y); } else if (screenBounds.Contains(bl) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(bl))) { - destinationLocation = new NativePoint(-zoomOffset.X - zoomSize.Width, zoomOffset.Y); + destinationLocation = new Point(-zoomOffset.X - zoomSize.Width, zoomOffset.Y); } else if (screenBounds.Contains(tr) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(tr))) { - destinationLocation = new NativePoint(zoomOffset.X, -zoomOffset.Y - zoomSize.Width); + destinationLocation = new Point(zoomOffset.X, -zoomOffset.Y - zoomSize.Width); } else if (screenBounds.Contains(tl) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(tl))) { - destinationLocation = new NativePoint(-zoomOffset.X - zoomSize.Width, -zoomOffset.Y - zoomSize.Width); + destinationLocation = new Point(-zoomOffset.X - zoomSize.Width, -zoomOffset.Y - zoomSize.Width); } - if (destinationLocation == NativePoint.Empty && !allowZoomOverCaptureRect) + if (destinationLocation == Point.Empty && !allowZoomOverCaptureRect) { VerifyZoomAnimation(pos, true); } else { - _zoomAnimator.ChangeDestination(new NativeRect(destinationLocation, zoomSize)); + _zoomAnimator.ChangeDestination(new Rectangle(destinationLocation, zoomSize)); } } @@ -845,7 +849,7 @@ namespace Greenshot.Forms /// /// /// - private void DrawZoom(Graphics graphics, NativeRect sourceRectangle, NativeRect destinationRectangle) + private void DrawZoom(Graphics graphics, Rectangle sourceRectangle, Rectangle destinationRectangle) { if (_capture.Image == null) { @@ -958,7 +962,7 @@ namespace Greenshot.Forms private void OnPaint(object sender, PaintEventArgs e) { Graphics graphics = e.Graphics; - NativeRect clipRectangle = e.ClipRectangle; + Rectangle clipRectangle = e.ClipRectangle; //graphics.BitBlt((Bitmap)buffer, Point.Empty); graphics.DrawImageUnscaled(_capture.Image, Point.Empty); @@ -1006,14 +1010,14 @@ namespace Greenshot.Forms } // Only draw Cursor if it's (partly) visible - if (_capture.Cursor != null && _capture.CursorVisible && clipRectangle.IntersectsWith(new NativeRect(_capture.CursorLocation, _capture.Cursor.Size))) + if (_capture.Cursor != null && _capture.CursorVisible && clipRectangle.IntersectsWith(new Rectangle(_capture.CursorLocation, _capture.Cursor.Size))) { graphics.DrawIcon(_capture.Cursor, _capture.CursorLocation.X, _capture.CursorLocation.Y); } if (_mouseDown || _captureMode == CaptureMode.Window || IsAnimating(_windowAnimator)) { - _captureRect = _captureRect.Intersect(new NativeRect(Point.Empty, _capture.ScreenBounds.Size)); // crop what is outside the screen + _captureRect.Intersect(new Rectangle(Point.Empty, _capture.ScreenBounds.Size)); // crop what is outside the screen var fixedRect = IsAnimating(_windowAnimator) ? _windowAnimator.Current : _captureRect; @@ -1142,7 +1146,7 @@ namespace Greenshot.Forms using (Pen pen = new Pen(Color.LightSeaGreen)) { pen.DashStyle = DashStyle.Dot; - NativeRect screenBounds = _capture.ScreenBounds; + Rectangle screenBounds = _capture.ScreenBounds; graphics.DrawLine(pen, _cursorPos.X, screenBounds.Y, _cursorPos.X, screenBounds.Height); graphics.DrawLine(pen, screenBounds.X, _cursorPos.Y, screenBounds.Width, _cursorPos.Y); } @@ -1171,10 +1175,10 @@ namespace Greenshot.Forms const int zoomSourceWidth = 25; const int zoomSourceHeight = 25; - var sourceRectangle = new NativeRect(_cursorPos.X - zoomSourceWidth / 2, _cursorPos.Y - zoomSourceHeight / 2, zoomSourceWidth, zoomSourceHeight); + Rectangle sourceRectangle = new Rectangle(_cursorPos.X - zoomSourceWidth / 2, _cursorPos.Y - zoomSourceHeight / 2, zoomSourceWidth, zoomSourceHeight); - var destinationRectangle = _zoomAnimator.Current; - destinationRectangle = destinationRectangle.Offset(_cursorPos); + Rectangle destinationRectangle = _zoomAnimator.Current; + destinationRectangle.Offset(_cursorPos); DrawZoom(graphics, sourceRectangle, destinationRectangle); } } diff --git a/src/Greenshot/Forms/MainForm.Designer.cs b/src/Greenshot/Forms/MainForm.Designer.cs index b558bf4f9..015e0060b 100644 --- a/src/Greenshot/Forms/MainForm.Designer.cs +++ b/src/Greenshot/Forms/MainForm.Designer.cs @@ -106,7 +106,7 @@ namespace Greenshot.Forms { this.contextMenu.Name = "contextMenu"; this.contextMenu.Closing += new System.Windows.Forms.ToolStripDropDownClosingEventHandler(this.ContextMenuClosing); this.contextMenu.Opening += new System.ComponentModel.CancelEventHandler(this.ContextMenuOpening); - this.contextMenu.Renderer = new Greenshot.Controls.ContextMenuToolStripProfessionalRenderer(this); + this.contextMenu.Renderer = new Greenshot.Controls.ContextMenuToolStripProfessionalRenderer(); // // contextmenu_capturearea // @@ -204,7 +204,7 @@ namespace Greenshot.Forms { // contextmenu_quicksettings // this.contextmenu_quicksettings.Name = "contextmenu_quicksettings"; - this.contextmenu_quicksettings.Size = new System.Drawing.Size(170, coreConfiguration.IconSize.Height + 8); + this.contextmenu_quicksettings.Size = new System.Drawing.Size(170, coreConfiguration.ScaledIconSize.Height + 8); // // contextmenu_settings // diff --git a/src/Greenshot/Forms/MainForm.cs b/src/Greenshot/Forms/MainForm.cs index 081084d0b..f1c4f8bbc 100644 --- a/src/Greenshot/Forms/MainForm.cs +++ b/src/Greenshot/Forms/MainForm.cs @@ -32,23 +32,17 @@ using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using System.Windows.Forms.Integration; -using Dapplo.Windows.Common.Structs; -using Dapplo.Windows.DesktopWindowsManager; -using Dapplo.Windows.Dpi; -using Dapplo.Windows.Kernel32; -using Dapplo.Windows.User32; using Greenshot.Base; using Greenshot.Base.Controls; using Greenshot.Base.Core; using Greenshot.Base.Core.Enums; -using Greenshot.Base.Core.FileFormatHandlers; using Greenshot.Base.Help; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Plugin; +using Greenshot.Base.UnmanagedHelpers; using Greenshot.Configuration; using Greenshot.Destinations; -using Greenshot.Editor; using Greenshot.Editor.Destinations; using Greenshot.Editor.Drawing; using Greenshot.Editor.Forms; @@ -62,7 +56,7 @@ namespace Greenshot.Forms /// /// This is the MainForm, the shell of Greenshot /// - public partial class MainForm : BaseForm, IGreenshotMainForm, ICaptureHelper, IProvideDeviceDpi + public partial class MainForm : BaseForm, IGreenshotMainForm, ICaptureHelper { private static ILog LOG; private static ResourceMutex _applicationMutex; @@ -120,11 +114,11 @@ namespace Greenshot.Forms if (argument.ToLower().Equals("/help") || argument.ToLower().Equals("/h") || argument.ToLower().Equals("/?")) { // Try to attach to the console - bool attachedToConsole = Kernel32Api.AttachConsole(); + bool attachedToConsole = Kernel32.AttachConsole(Kernel32.ATTACHCONSOLE_ATTACHPARENTPROCESS); // If attach didn't work, open a console if (!attachedToConsole) { - Kernel32Api.AllocConsole(); + Kernel32.AllocConsole(); } var helpOutput = new StringBuilder(); @@ -168,7 +162,7 @@ namespace Greenshot.Forms if (argument.ToLower().Equals("/exit")) { - // un-register application on uninstall (allow uninstall) + // unregister application on uninstall (allow uninstall) try { LOG.Info("Sending all instances the exit command."); @@ -254,7 +248,7 @@ namespace Greenshot.Forms { try { - instanceInfo.Append(index++ + ": ").AppendLine(Kernel32Api.GetProcessPath(greenshotProcess.Id)); + instanceInfo.Append(index++ + ": ").AppendLine(Kernel32.GetProcessPath(greenshotProcess.Id)); if (currentProcessId == greenshotProcess.Id) { matchedThisProcess = true; @@ -271,7 +265,7 @@ namespace Greenshot.Forms if (!matchedThisProcess) { using Process currentProcess = Process.GetCurrentProcess(); - instanceInfo.Append(index + ": ").AppendLine(Kernel32Api.GetProcessPath(currentProcess.Id)); + instanceInfo.Append(index + ": ").AppendLine(Kernel32.GetProcessPath(currentProcess.Id)); } // A dirty fix to make sure the message box is visible as a Greenshot window on the taskbar @@ -382,7 +376,8 @@ namespace Greenshot.Forms { var uiContext = TaskScheduler.FromCurrentSynchronizationContext(); SimpleServiceProvider.Current.AddService(uiContext); - + DpiChanged += (e, o) => ApplyDpiScaling(); + // The most important form is this SimpleServiceProvider.Current.AddService
(this); // Also as itself @@ -392,8 +387,6 @@ namespace Greenshot.Forms _instance = this; - EditorInitialize.Initialize(); - // Factory for surface objects ISurface SurfaceFactory() => new Surface(); @@ -506,7 +499,7 @@ namespace Greenshot.Forms // Make Greenshot use less memory after startup if (_conf.MinimizeWorkingSetSize) { - PsApi.EmptyWorkingSet(); + PsAPI.EmptyWorkingSet(); } } @@ -531,7 +524,7 @@ namespace Greenshot.Forms int len = 250; var stringBuilder = new StringBuilder(len); using var proc = Process.GetCurrentProcess(); - var err = Kernel32Api.GetPackageFullName(proc.Handle, ref len, stringBuilder); + var err = Kernel32.GetPackageFullName(proc.Handle, ref len, stringBuilder); if (err != 0) { useEditor = true; @@ -747,7 +740,7 @@ namespace Greenshot.Forms return; } - DpiChangedHandler(96, DeviceDpi); + ApplyDpiScaling(); string ieExePath = PluginUtils.GetExePath("iexplore.exe"); if (!string.IsNullOrEmpty(ieExePath)) { @@ -758,10 +751,10 @@ namespace Greenshot.Forms /// /// Modify the DPI settings depending in the current value /// - protected override void DpiChangedHandler(int oldDpi, int newDpi) + private void ApplyDpiScaling() { - var newSize = DpiCalculator.ScaleWithDpi(coreConfiguration.IconSize, newDpi); - contextMenu.ImageScalingSize = newSize; + var scaledIconSize = DpiHelper.ScaleWithDpi(coreConfiguration.IconSize, DpiHelper.GetDpi(Handle)); + contextMenu.ImageScalingSize = scaledIconSize; } /// @@ -929,11 +922,11 @@ namespace Greenshot.Forms { Hide(); ShowInTaskbar = false; - - // TODO: Do we really need this? - //using var loProcess = Process.GetCurrentProcess(); - //loProcess.MaxWorkingSet = (IntPtr)750000; - //loProcess.MinWorkingSet = (IntPtr)300000; + + + using var loProcess = Process.GetCurrentProcess(); + loProcess.MaxWorkingSet = (IntPtr)750000; + loProcess.MinWorkingSet = (IntPtr)300000; } private void CaptureRegion() @@ -943,12 +936,9 @@ namespace Greenshot.Forms private void CaptureFile(IDestination destination = null) { - var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); - var extensions = fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadFromStream).Select(e => $"*{e}").ToList(); - var openFileDialog = new OpenFileDialog { - Filter = @$"Image files ({string.Join(", ", extensions)})|{string.Join("; ", extensions)}" + Filter = @"Image files (*.greenshot, *.png, *.jpg, *.gif, *.bmp, *.ico, *.tiff, *.wmf)|*.greenshot; *.png; *.jpg; *.jpeg; *.gif; *.bmp; *.ico; *.tiff; *.tif; *.wmf" }; if (openFileDialog.ShowDialog() != DialogResult.OK) { @@ -1005,7 +995,7 @@ namespace Greenshot.Forms var factor = DeviceDpi / 96f; contextMenu.Scale(new SizeF(factor, factor)); contextmenu_captureclipboard.Enabled = ClipboardHelper.ContainsImage(); - contextmenu_capturelastregion.Enabled = coreConfiguration.LastCapturedRegion != NativeRect.Empty; + contextmenu_capturelastregion.Enabled = coreConfiguration.LastCapturedRegion != Rectangle.Empty; // IE context menu code try @@ -1127,50 +1117,51 @@ namespace Greenshot.Forms { ToolStripMenuItem captureScreenMenuItem = (ToolStripMenuItem) sender; captureScreenMenuItem.DropDownItems.Clear(); - if (DisplayInfo.AllDisplayInfos.Length <= 1) return; - - var allScreensBounds = DisplayInfo.ScreenBounds; - - var captureScreenItem = new ToolStripMenuItem(Language.GetString(LangKey.contextmenu_capturefullscreen_all)); - captureScreenItem.Click += delegate { - BeginInvoke((MethodInvoker) delegate { - CaptureHelper.CaptureFullscreen(false, ScreenCaptureMode.FullScreen); - }); - }; - - captureScreenMenuItem.DropDownItems.Add(captureScreenItem); - foreach (var displayInfo in DisplayInfo.AllDisplayInfos) + if (Screen.AllScreens.Length > 1) { - var displayToCapture = displayInfo; - string deviceAlignment = displayToCapture.DeviceName; - - if (displayInfo.Bounds.Top == allScreensBounds.Top && displayInfo.Bounds.Bottom != allScreensBounds.Bottom) - { - deviceAlignment += " " + Language.GetString(LangKey.contextmenu_capturefullscreen_top); - } - else if (displayInfo.Bounds.Top != allScreensBounds.Top && displayInfo.Bounds.Bottom == allScreensBounds.Bottom) - { - deviceAlignment += " " + Language.GetString(LangKey.contextmenu_capturefullscreen_bottom); - } + Rectangle allScreensBounds = WindowCapture.GetScreenBounds(); - if (displayInfo.Bounds.Left == allScreensBounds.Left && displayInfo.Bounds.Right != allScreensBounds.Right) - { - deviceAlignment += " " + Language.GetString(LangKey.contextmenu_capturefullscreen_left); - } - else if (displayInfo.Bounds.Left != allScreensBounds.Left && displayInfo.Bounds.Right == allScreensBounds.Right) - { - deviceAlignment += " " + Language.GetString(LangKey.contextmenu_capturefullscreen_right); - } - - captureScreenItem = new ToolStripMenuItem(deviceAlignment); - captureScreenItem.Click += delegate - { - BeginInvoke((MethodInvoker) delegate - { - CaptureHelper.CaptureRegion(false, displayToCapture.Bounds); + var captureScreenItem = new ToolStripMenuItem(Language.GetString(LangKey.contextmenu_capturefullscreen_all)); + captureScreenItem.Click += delegate { + BeginInvoke((MethodInvoker) delegate { + CaptureHelper.CaptureFullscreen(false, ScreenCaptureMode.FullScreen); }); }; + captureScreenMenuItem.DropDownItems.Add(captureScreenItem); + foreach (Screen screen in Screen.AllScreens) + { + Screen screenToCapture = screen; + string deviceAlignment = screenToCapture.DeviceName; + + if (screen.Bounds.Top == allScreensBounds.Top && screen.Bounds.Bottom != allScreensBounds.Bottom) + { + deviceAlignment += " " + Language.GetString(LangKey.contextmenu_capturefullscreen_top); + } + else if (screen.Bounds.Top != allScreensBounds.Top && screen.Bounds.Bottom == allScreensBounds.Bottom) + { + deviceAlignment += " " + Language.GetString(LangKey.contextmenu_capturefullscreen_bottom); + } + + if (screen.Bounds.Left == allScreensBounds.Left && screen.Bounds.Right != allScreensBounds.Right) + { + deviceAlignment += " " + Language.GetString(LangKey.contextmenu_capturefullscreen_left); + } + else if (screen.Bounds.Left != allScreensBounds.Left && screen.Bounds.Right == allScreensBounds.Right) + { + deviceAlignment += " " + Language.GetString(LangKey.contextmenu_capturefullscreen_right); + } + + captureScreenItem = new ToolStripMenuItem(deviceAlignment); + captureScreenItem.Click += delegate + { + BeginInvoke((MethodInvoker) delegate + { + CaptureHelper.CaptureRegion(false, screenToCapture.Bounds); + }); + }; + captureScreenMenuItem.DropDownItems.Add(captureScreenItem); + } } } @@ -1239,7 +1230,7 @@ namespace Greenshot.Forms { menuItem.DropDownItems.Clear(); // check if thumbnailPreview is enabled and DWM is enabled - bool thumbnailPreview = _conf.ThumnailPreview && DwmApi.IsDwmEnabled; + bool thumbnailPreview = _conf.ThumnailPreview && DWM.IsDwmEnabled; foreach (var window in WindowDetails.GetTopLevelWindows()) { @@ -1501,7 +1492,7 @@ namespace Greenshot.Forms if (!_conf.Values["Destinations"].IsFixed) { // screenshot destination - selectList = new ToolStripMenuSelectList("destinations", true, this) + selectList = new ToolStripMenuSelectList("destinations", true) { Text = Language.GetString(LangKey.settings_destination) }; @@ -1518,7 +1509,7 @@ namespace Greenshot.Forms if (!_conf.Values["WindowCaptureMode"].IsFixed) { // Capture Modes - selectList = new ToolStripMenuSelectList("capturemodes", false, this) + selectList = new ToolStripMenuSelectList("capturemodes", false) { Text = Language.GetString(LangKey.settings_window_capture_mode) }; @@ -1533,7 +1524,7 @@ namespace Greenshot.Forms } // print options - selectList = new ToolStripMenuSelectList("printoptions", true, this) + selectList = new ToolStripMenuSelectList("printoptions", true) { Text = Language.GetString(LangKey.settings_printoptions) }; @@ -1558,7 +1549,7 @@ namespace Greenshot.Forms } // effects - selectList = new ToolStripMenuSelectList("effects", true, this) + selectList = new ToolStripMenuSelectList("effects", true) { Text = Language.GetString(LangKey.settings_visualization) }; @@ -1913,7 +1904,7 @@ namespace Greenshot.Forms LOG.Error("Error closing application!", e); } - ImageIO.RemoveTmpFiles(); + ImageOutput.RemoveTmpFiles(); // Store any open configuration changes try diff --git a/src/Greenshot/Forms/PrintOptionsDialog.Designer.cs b/src/Greenshot/Forms/PrintOptionsDialog.Designer.cs index 9f9d0a13a..9cb5cc43d 100644 --- a/src/Greenshot/Forms/PrintOptionsDialog.Designer.cs +++ b/src/Greenshot/Forms/PrintOptionsDialog.Designer.cs @@ -89,7 +89,7 @@ namespace Greenshot.Forms this.checkboxAllowShrink.LanguageKey = "printoptions_allowshrink"; this.checkboxAllowShrink.Location = new System.Drawing.Point(13, 23); this.checkboxAllowShrink.Name = "checkboxAllowShrink"; - this.checkboxAllowShrink.PropertyName = nameof(coreConfiguration.OutputPrintAllowShrink); + this.checkboxAllowShrink.PropertyName = "OutputPrintAllowShrink"; this.checkboxAllowShrink.Size = new System.Drawing.Size(168, 17); this.checkboxAllowShrink.TabIndex = 2; this.checkboxAllowShrink.Text = "Shrink printout to fit paper size"; @@ -103,7 +103,7 @@ namespace Greenshot.Forms this.checkboxAllowEnlarge.LanguageKey = "printoptions_allowenlarge"; this.checkboxAllowEnlarge.Location = new System.Drawing.Point(13, 46); this.checkboxAllowEnlarge.Name = "checkboxAllowEnlarge"; - this.checkboxAllowEnlarge.PropertyName = nameof(coreConfiguration.OutputPrintAllowEnlarge); + this.checkboxAllowEnlarge.PropertyName = "OutputPrintAllowEnlarge"; this.checkboxAllowEnlarge.Size = new System.Drawing.Size(174, 17); this.checkboxAllowEnlarge.TabIndex = 3; this.checkboxAllowEnlarge.Text = "Enlarge printout to fit paper size"; @@ -117,7 +117,7 @@ namespace Greenshot.Forms this.checkboxAllowCenter.LanguageKey = "printoptions_allowcenter"; this.checkboxAllowCenter.Location = new System.Drawing.Point(13, 92); this.checkboxAllowCenter.Name = "checkboxAllowCenter"; - this.checkboxAllowCenter.PropertyName = nameof(coreConfiguration.OutputPrintCenter); + this.checkboxAllowCenter.PropertyName = "OutputPrintCenter"; this.checkboxAllowCenter.Size = new System.Drawing.Size(137, 17); this.checkboxAllowCenter.TabIndex = 5; this.checkboxAllowCenter.Text = "Center printout on page"; @@ -131,7 +131,7 @@ namespace Greenshot.Forms this.checkboxAllowRotate.LanguageKey = "printoptions_allowrotate"; this.checkboxAllowRotate.Location = new System.Drawing.Point(13, 69); this.checkboxAllowRotate.Name = "checkboxAllowRotate"; - this.checkboxAllowRotate.PropertyName = nameof(coreConfiguration.OutputPrintAllowRotate); + this.checkboxAllowRotate.PropertyName = "OutputPrintAllowRotate"; this.checkboxAllowRotate.Size = new System.Drawing.Size(187, 17); this.checkboxAllowRotate.TabIndex = 4; this.checkboxAllowRotate.Text = "Rotate printout to page orientation"; @@ -158,7 +158,7 @@ namespace Greenshot.Forms this.checkboxDateTime.LanguageKey = "printoptions_timestamp"; this.checkboxDateTime.Location = new System.Drawing.Point(13, 115); this.checkboxDateTime.Name = "checkboxDateTime"; - this.checkboxDateTime.PropertyName = nameof(coreConfiguration.OutputPrintFooter); + this.checkboxDateTime.PropertyName = "OutputPrintFooter"; this.checkboxDateTime.Size = new System.Drawing.Size(187, 17); this.checkboxDateTime.TabIndex = 6; this.checkboxDateTime.Text = "Print date / time at bottom of page"; @@ -184,7 +184,7 @@ namespace Greenshot.Forms this.checkboxPrintInverted.LanguageKey = "printoptions_inverted"; this.checkboxPrintInverted.Location = new System.Drawing.Point(13, 88); this.checkboxPrintInverted.Name = "checkboxPrintInverted"; - this.checkboxPrintInverted.PropertyName = nameof(coreConfiguration.OutputPrintInverted); + this.checkboxPrintInverted.PropertyName = "OutputPrintInverted"; this.checkboxPrintInverted.Size = new System.Drawing.Size(141, 17); this.checkboxPrintInverted.TabIndex = 14; this.checkboxPrintInverted.Text = "Print with inverted colors"; @@ -198,7 +198,7 @@ namespace Greenshot.Forms this.radioBtnGrayScale.LanguageKey = "printoptions_printgrayscale"; this.radioBtnGrayScale.Location = new System.Drawing.Point(13, 42); this.radioBtnGrayScale.Name = "radioBtnGrayScale"; - this.radioBtnGrayScale.PropertyName = nameof(coreConfiguration.OutputPrintGrayscale); + this.radioBtnGrayScale.PropertyName = "OutputPrintGrayscale"; this.radioBtnGrayScale.Size = new System.Drawing.Size(137, 17); this.radioBtnGrayScale.TabIndex = 12; this.radioBtnGrayScale.Text = "Force grayscale printing"; @@ -212,7 +212,7 @@ namespace Greenshot.Forms this.radioBtnMonochrome.LanguageKey = "printoptions_printmonochrome"; this.radioBtnMonochrome.Location = new System.Drawing.Point(13, 65); this.radioBtnMonochrome.Name = "radioBtnMonochrome"; - this.radioBtnMonochrome.PropertyName = nameof(coreConfiguration.OutputPrintMonochrome); + this.radioBtnMonochrome.PropertyName = "OutputPrintMonochrome"; this.radioBtnMonochrome.Size = new System.Drawing.Size(148, 17); this.radioBtnMonochrome.TabIndex = 13; this.radioBtnMonochrome.Text = "Force black/white printing"; @@ -255,6 +255,7 @@ namespace Greenshot.Forms this.radioBtnColorPrint.LanguageKey = "printoptions_printcolor"; this.radioBtnColorPrint.Location = new System.Drawing.Point(13, 19); this.radioBtnColorPrint.Name = "radioBtnColorPrint"; + this.radioBtnColorPrint.PropertyName = "OutputPrintColor"; this.radioBtnColorPrint.Size = new System.Drawing.Size(90, 17); this.radioBtnColorPrint.TabIndex = 11; this.radioBtnColorPrint.TextAlign = System.Drawing.ContentAlignment.TopLeft; diff --git a/src/Greenshot/Forms/SettingsForm.Designer.cs b/src/Greenshot/Forms/SettingsForm.Designer.cs index 6bea356df..e8f284850 100644 --- a/src/Greenshot/Forms/SettingsForm.Designer.cs +++ b/src/Greenshot/Forms/SettingsForm.Designer.cs @@ -20,8 +20,6 @@ */ using Greenshot.Base.Controls; -using Greenshot.Base.Core; -using Greenshot.Editor.Configuration; using Greenshot.Editor.Controls; namespace Greenshot.Forms { @@ -237,7 +235,7 @@ namespace Greenshot.Forms { // this.textbox_screenshotname.Location = new System.Drawing.Point(138, 41); this.textbox_screenshotname.Name = "textbox_screenshotname"; - this.textbox_screenshotname.PropertyName = nameof(CoreConfiguration.OutputFileFilenamePattern); + this.textbox_screenshotname.PropertyName = "OutputFileFilenamePattern"; this.textbox_screenshotname.Size = new System.Drawing.Size(233, 20); this.textbox_screenshotname.TabIndex = 3; this.textbox_screenshotname.TextChanged += new System.EventHandler(this.FilenamePatternChanged); @@ -266,7 +264,7 @@ namespace Greenshot.Forms { this.combobox_primaryimageformat.FormattingEnabled = true; this.combobox_primaryimageformat.Location = new System.Drawing.Point(138, 64); this.combobox_primaryimageformat.Name = "combobox_primaryimageformat"; - this.combobox_primaryimageformat.PropertyName = nameof(CoreConfiguration.OutputFileFormat); + this.combobox_primaryimageformat.PropertyName = "OutputFileFormat"; this.combobox_primaryimageformat.Size = new System.Drawing.Size(268, 21); this.combobox_primaryimageformat.TabIndex = 5; // @@ -311,7 +309,7 @@ namespace Greenshot.Forms { this.checkbox_copypathtoclipboard.LanguageKey = "settings_copypathtoclipboard"; this.checkbox_copypathtoclipboard.Location = new System.Drawing.Point(12, 89); this.checkbox_copypathtoclipboard.Name = "checkbox_copypathtoclipboard"; - this.checkbox_copypathtoclipboard.PropertyName = nameof(CoreConfiguration.OutputFileCopyPathToClipboard); + this.checkbox_copypathtoclipboard.PropertyName = "OutputFileCopyPathToClipboard"; this.checkbox_copypathtoclipboard.Size = new System.Drawing.Size(394, 24); this.checkbox_copypathtoclipboard.TabIndex = 6; this.checkbox_copypathtoclipboard.UseVisualStyleBackColor = true; @@ -376,7 +374,7 @@ namespace Greenshot.Forms { this.checkbox_reducecolors.LanguageKey = "settings_reducecolors"; this.checkbox_reducecolors.Location = new System.Drawing.Point(12, 72); this.checkbox_reducecolors.Name = "checkbox_reducecolors"; - this.checkbox_reducecolors.PropertyName = nameof(CoreConfiguration.OutputFileReduceColors); + this.checkbox_reducecolors.PropertyName = "OutputFileReduceColors"; this.checkbox_reducecolors.Size = new System.Drawing.Size(394, 25); this.checkbox_reducecolors.TabIndex = 10; this.checkbox_reducecolors.UseVisualStyleBackColor = true; @@ -386,7 +384,7 @@ namespace Greenshot.Forms { this.checkbox_alwaysshowqualitydialog.LanguageKey = "settings_alwaysshowqualitydialog"; this.checkbox_alwaysshowqualitydialog.Location = new System.Drawing.Point(12, 50); this.checkbox_alwaysshowqualitydialog.Name = "checkbox_alwaysshowqualitydialog"; - this.checkbox_alwaysshowqualitydialog.PropertyName = nameof(CoreConfiguration.OutputFilePromptQuality); + this.checkbox_alwaysshowqualitydialog.PropertyName = "OutputFilePromptQuality"; this.checkbox_alwaysshowqualitydialog.Size = new System.Drawing.Size(394, 25); this.checkbox_alwaysshowqualitydialog.TabIndex = 9; this.checkbox_alwaysshowqualitydialog.UseVisualStyleBackColor = true; @@ -528,7 +526,7 @@ namespace Greenshot.Forms { this.checkbox_usedefaultproxy.LanguageKey = "settings_usedefaultproxy"; this.checkbox_usedefaultproxy.Location = new System.Drawing.Point(7, 11); this.checkbox_usedefaultproxy.Name = "checkbox_usedefaultproxy"; - this.checkbox_usedefaultproxy.PropertyName = nameof(CoreConfiguration.UseProxy); + this.checkbox_usedefaultproxy.PropertyName = "UseProxy"; this.checkbox_usedefaultproxy.Size = new System.Drawing.Size(397, 25); this.checkbox_usedefaultproxy.TabIndex = 7; this.checkbox_usedefaultproxy.UseVisualStyleBackColor = true; @@ -566,7 +564,7 @@ namespace Greenshot.Forms { this.lastregion_hotkeyControl.HotkeyModifiers = System.Windows.Forms.Keys.None; this.lastregion_hotkeyControl.Location = new System.Drawing.Point(224, 94); this.lastregion_hotkeyControl.Name = "lastregion_hotkeyControl"; - this.lastregion_hotkeyControl.PropertyName = nameof(CoreConfiguration.LastregionHotkey); + this.lastregion_hotkeyControl.PropertyName = "LastregionHotkey"; this.lastregion_hotkeyControl.Size = new System.Drawing.Size(179, 20); this.lastregion_hotkeyControl.TabIndex = 5; // @@ -584,7 +582,7 @@ namespace Greenshot.Forms { this.ie_hotkeyControl.HotkeyModifiers = System.Windows.Forms.Keys.None; this.ie_hotkeyControl.Location = new System.Drawing.Point(224, 120); this.ie_hotkeyControl.Name = "ie_hotkeyControl"; - this.ie_hotkeyControl.PropertyName = nameof(CoreConfiguration.IEHotkey); + this.ie_hotkeyControl.PropertyName = "IEHotkey"; this.ie_hotkeyControl.Size = new System.Drawing.Size(179, 20); this.ie_hotkeyControl.TabIndex = 6; // @@ -618,7 +616,7 @@ namespace Greenshot.Forms { this.region_hotkeyControl.HotkeyModifiers = System.Windows.Forms.Keys.None; this.region_hotkeyControl.Location = new System.Drawing.Point(224, 68); this.region_hotkeyControl.Name = "region_hotkeyControl"; - this.region_hotkeyControl.PropertyName = nameof(CoreConfiguration.RegionHotkey); + this.region_hotkeyControl.PropertyName = "RegionHotkey"; this.region_hotkeyControl.Size = new System.Drawing.Size(179, 20); this.region_hotkeyControl.TabIndex = 4; // @@ -628,7 +626,7 @@ namespace Greenshot.Forms { this.window_hotkeyControl.HotkeyModifiers = System.Windows.Forms.Keys.None; this.window_hotkeyControl.Location = new System.Drawing.Point(224, 42); this.window_hotkeyControl.Name = "window_hotkeyControl"; - this.window_hotkeyControl.PropertyName = nameof(CoreConfiguration.WindowHotkey); + this.window_hotkeyControl.PropertyName = "WindowHotkey"; this.window_hotkeyControl.Size = new System.Drawing.Size(179, 20); this.window_hotkeyControl.TabIndex = 3; // @@ -638,7 +636,7 @@ namespace Greenshot.Forms { this.fullscreen_hotkeyControl.HotkeyModifiers = System.Windows.Forms.Keys.None; this.fullscreen_hotkeyControl.Location = new System.Drawing.Point(224, 16); this.fullscreen_hotkeyControl.Name = "fullscreen_hotkeyControl"; - this.fullscreen_hotkeyControl.PropertyName = nameof(CoreConfiguration.FullscreenHotkey); + this.fullscreen_hotkeyControl.PropertyName = "FullscreenHotkey"; this.fullscreen_hotkeyControl.Size = new System.Drawing.Size(179, 20); this.fullscreen_hotkeyControl.TabIndex = 2; // @@ -670,7 +668,7 @@ namespace Greenshot.Forms { this.checkbox_editor_match_capture_size.LanguageKey = "editor_match_capture_size"; this.checkbox_editor_match_capture_size.Location = new System.Drawing.Point(6, 19); this.checkbox_editor_match_capture_size.Name = "checkbox_editor_match_capture_size"; - this.checkbox_editor_match_capture_size.PropertyName = nameof(EditorConfiguration.MatchSizeToCapture); + this.checkbox_editor_match_capture_size.PropertyName = "MatchSizeToCapture"; this.checkbox_editor_match_capture_size.SectionName = "Editor"; this.checkbox_editor_match_capture_size.Size = new System.Drawing.Size(397, 24); this.checkbox_editor_match_capture_size.TabIndex = 11; @@ -691,7 +689,7 @@ namespace Greenshot.Forms { this.checkbox_ie_capture.LanguageKey = "settings_iecapture"; this.checkbox_ie_capture.Location = new System.Drawing.Point(6, 19); this.checkbox_ie_capture.Name = "checkbox_ie_capture"; - this.checkbox_ie_capture.PropertyName = nameof(CoreConfiguration.IECapture); + this.checkbox_ie_capture.PropertyName = "IECapture"; this.checkbox_ie_capture.Size = new System.Drawing.Size(404, 24); this.checkbox_ie_capture.TabIndex = 10; this.checkbox_ie_capture.UseVisualStyleBackColor = true; @@ -734,7 +732,7 @@ namespace Greenshot.Forms { this.radiobuttonInteractiveCapture.LanguageKey = "settings_capture_windows_interactive"; this.radiobuttonInteractiveCapture.Location = new System.Drawing.Point(11, 20); this.radiobuttonInteractiveCapture.Name = "radiobuttonInteractiveCapture"; - this.radiobuttonInteractiveCapture.PropertyName = nameof(CoreConfiguration.CaptureWindowsInteractive); + this.radiobuttonInteractiveCapture.PropertyName = "CaptureWindowsInteractive"; this.radiobuttonInteractiveCapture.Size = new System.Drawing.Size(203, 17); this.radiobuttonInteractiveCapture.TabIndex = 6; this.radiobuttonInteractiveCapture.TabStop = true; @@ -772,7 +770,7 @@ namespace Greenshot.Forms { this.checkbox_zoomer.LanguageKey = "settings_zoom"; this.checkbox_zoomer.Location = new System.Drawing.Point(11, 79); this.checkbox_zoomer.Name = "checkbox_zoomer"; - this.checkbox_zoomer.PropertyName = nameof(CoreConfiguration.ZoomerEnabled); + this.checkbox_zoomer.PropertyName = "ZoomerEnabled"; this.checkbox_zoomer.Size = new System.Drawing.Size(399, 24); this.checkbox_zoomer.TabIndex = 4; this.checkbox_zoomer.UseVisualStyleBackColor = true; @@ -782,7 +780,7 @@ namespace Greenshot.Forms { this.checkbox_notifications.LanguageKey = "settings_shownotify"; this.checkbox_notifications.Location = new System.Drawing.Point(11, 59); this.checkbox_notifications.Name = "checkbox_notifications"; - this.checkbox_notifications.PropertyName = nameof(CoreConfiguration.ShowTrayNotification); + this.checkbox_notifications.PropertyName = "ShowTrayNotification"; this.checkbox_notifications.Size = new System.Drawing.Size(399, 24); this.checkbox_notifications.TabIndex = 3; this.checkbox_notifications.UseVisualStyleBackColor = true; @@ -792,7 +790,7 @@ namespace Greenshot.Forms { this.checkbox_playsound.LanguageKey = "settings_playsound"; this.checkbox_playsound.Location = new System.Drawing.Point(11, 39); this.checkbox_playsound.Name = "checkbox_playsound"; - this.checkbox_playsound.PropertyName = nameof(CoreConfiguration.PlayCameraSound); + this.checkbox_playsound.PropertyName = "PlayCameraSound"; this.checkbox_playsound.Size = new System.Drawing.Size(399, 24); this.checkbox_playsound.TabIndex = 2; this.checkbox_playsound.UseVisualStyleBackColor = true; @@ -802,7 +800,7 @@ namespace Greenshot.Forms { this.checkbox_capture_mousepointer.LanguageKey = "settings_capture_mousepointer"; this.checkbox_capture_mousepointer.Location = new System.Drawing.Point(11, 19); this.checkbox_capture_mousepointer.Name = "checkbox_capture_mousepointer"; - this.checkbox_capture_mousepointer.PropertyName = nameof(CoreConfiguration.CaptureMousepointer); + this.checkbox_capture_mousepointer.PropertyName = "CaptureMousepointer"; this.checkbox_capture_mousepointer.Size = new System.Drawing.Size(394, 24); this.checkbox_capture_mousepointer.TabIndex = 1; this.checkbox_capture_mousepointer.UseVisualStyleBackColor = true; @@ -889,7 +887,7 @@ namespace Greenshot.Forms { this.checkboxPrintInverted.LanguageKey = "printoptions_inverted"; this.checkboxPrintInverted.Location = new System.Drawing.Point(13, 88); this.checkboxPrintInverted.Name = "checkboxPrintInverted"; - this.checkboxPrintInverted.PropertyName = nameof(CoreConfiguration.OutputPrintInverted); + this.checkboxPrintInverted.PropertyName = "OutputPrintInverted"; this.checkboxPrintInverted.Size = new System.Drawing.Size(141, 17); this.checkboxPrintInverted.TabIndex = 14; this.checkboxPrintInverted.TextAlign = System.Drawing.ContentAlignment.TopLeft; @@ -902,6 +900,7 @@ namespace Greenshot.Forms { this.radioBtnColorPrint.LanguageKey = "printoptions_printcolor"; this.radioBtnColorPrint.Location = new System.Drawing.Point(13, 19); this.radioBtnColorPrint.Name = "radioBtnColorPrint"; + this.radioBtnColorPrint.PropertyName = "OutputPrintColor"; this.radioBtnColorPrint.Size = new System.Drawing.Size(90, 17); this.radioBtnColorPrint.TabIndex = 11; this.radioBtnColorPrint.TextAlign = System.Drawing.ContentAlignment.TopLeft; @@ -914,7 +913,7 @@ namespace Greenshot.Forms { this.radioBtnGrayScale.LanguageKey = "printoptions_printgrayscale"; this.radioBtnGrayScale.Location = new System.Drawing.Point(13, 42); this.radioBtnGrayScale.Name = "radioBtnGrayScale"; - this.radioBtnGrayScale.PropertyName = nameof(coreConfiguration.OutputPrintGrayscale); + this.radioBtnGrayScale.PropertyName = "OutputPrintGrayscale"; this.radioBtnGrayScale.Size = new System.Drawing.Size(137, 17); this.radioBtnGrayScale.TabIndex = 12; this.radioBtnGrayScale.Text = "Force grayscale printing"; @@ -928,7 +927,7 @@ namespace Greenshot.Forms { this.radioBtnMonochrome.LanguageKey = "printoptions_printmonochrome"; this.radioBtnMonochrome.Location = new System.Drawing.Point(13, 65); this.radioBtnMonochrome.Name = "radioBtnMonochrome"; - this.radioBtnMonochrome.PropertyName = nameof(coreConfiguration.OutputPrintMonochrome); + this.radioBtnMonochrome.PropertyName = "OutputPrintMonochrome"; this.radioBtnMonochrome.Size = new System.Drawing.Size(148, 17); this.radioBtnMonochrome.TabIndex = 13; this.radioBtnMonochrome.TextAlign = System.Drawing.ContentAlignment.TopLeft; @@ -955,7 +954,7 @@ namespace Greenshot.Forms { this.checkboxDateTime.LanguageKey = "printoptions_timestamp"; this.checkboxDateTime.Location = new System.Drawing.Point(13, 115); this.checkboxDateTime.Name = "checkboxDateTime"; - this.checkboxDateTime.PropertyName = nameof(coreConfiguration.OutputPrintFooter); + this.checkboxDateTime.PropertyName = "OutputPrintFooter"; this.checkboxDateTime.Size = new System.Drawing.Size(187, 17); this.checkboxDateTime.TabIndex = 6; this.checkboxDateTime.TextAlign = System.Drawing.ContentAlignment.TopLeft; @@ -968,7 +967,7 @@ namespace Greenshot.Forms { this.checkboxAllowShrink.LanguageKey = "printoptions_allowshrink"; this.checkboxAllowShrink.Location = new System.Drawing.Point(13, 23); this.checkboxAllowShrink.Name = "checkboxAllowShrink"; - this.checkboxAllowShrink.PropertyName = nameof(coreConfiguration.OutputPrintAllowShrink); + this.checkboxAllowShrink.PropertyName = "OutputPrintAllowShrink"; this.checkboxAllowShrink.Size = new System.Drawing.Size(168, 17); this.checkboxAllowShrink.TabIndex = 2; this.checkboxAllowShrink.TextAlign = System.Drawing.ContentAlignment.TopLeft; @@ -981,7 +980,7 @@ namespace Greenshot.Forms { this.checkboxAllowEnlarge.LanguageKey = "printoptions_allowenlarge"; this.checkboxAllowEnlarge.Location = new System.Drawing.Point(13, 46); this.checkboxAllowEnlarge.Name = "checkboxAllowEnlarge"; - this.checkboxAllowEnlarge.PropertyName = nameof(coreConfiguration.OutputPrintAllowEnlarge); + this.checkboxAllowEnlarge.PropertyName = "OutputPrintAllowEnlarge"; this.checkboxAllowEnlarge.Size = new System.Drawing.Size(174, 17); this.checkboxAllowEnlarge.TabIndex = 3; this.checkboxAllowEnlarge.TextAlign = System.Drawing.ContentAlignment.TopLeft; @@ -994,7 +993,7 @@ namespace Greenshot.Forms { this.checkboxAllowRotate.LanguageKey = "printoptions_allowrotate"; this.checkboxAllowRotate.Location = new System.Drawing.Point(13, 69); this.checkboxAllowRotate.Name = "checkboxAllowRotate"; - this.checkboxAllowRotate.PropertyName = nameof(coreConfiguration.OutputPrintAllowRotate); + this.checkboxAllowRotate.PropertyName = "OutputPrintAllowRotate"; this.checkboxAllowRotate.Size = new System.Drawing.Size(187, 17); this.checkboxAllowRotate.TabIndex = 4; this.checkboxAllowRotate.TextAlign = System.Drawing.ContentAlignment.TopLeft; @@ -1007,7 +1006,7 @@ namespace Greenshot.Forms { this.checkboxAllowCenter.LanguageKey = "printoptions_allowcenter"; this.checkboxAllowCenter.Location = new System.Drawing.Point(13, 92); this.checkboxAllowCenter.Name = "checkboxAllowCenter"; - this.checkboxAllowCenter.PropertyName = nameof(coreConfiguration.OutputPrintCenter); + this.checkboxAllowCenter.PropertyName = "OutputPrintCenter"; this.checkboxAllowCenter.Size = new System.Drawing.Size(137, 17); this.checkboxAllowCenter.TabIndex = 5; this.checkboxAllowCenter.TextAlign = System.Drawing.ContentAlignment.TopLeft; @@ -1018,7 +1017,7 @@ namespace Greenshot.Forms { this.checkbox_alwaysshowprintoptionsdialog.LanguageKey = "settings_alwaysshowprintoptionsdialog"; this.checkbox_alwaysshowprintoptionsdialog.Location = new System.Drawing.Point(19, 293); this.checkbox_alwaysshowprintoptionsdialog.Name = "checkbox_alwaysshowprintoptionsdialog"; - this.checkbox_alwaysshowprintoptionsdialog.PropertyName = nameof(coreConfiguration.OutputPrintPromptOptions); + this.checkbox_alwaysshowprintoptionsdialog.PropertyName = "OutputPrintPromptOptions"; this.checkbox_alwaysshowprintoptionsdialog.Size = new System.Drawing.Size(394, 20); this.checkbox_alwaysshowprintoptionsdialog.TabIndex = 15; this.checkbox_alwaysshowprintoptionsdialog.Text = "Show print options dialog every time an image is printed"; @@ -1115,7 +1114,7 @@ namespace Greenshot.Forms { this.checkbox_reuseeditor.LanguageKey = "expertsettings_reuseeditorifpossible"; this.checkbox_reuseeditor.Location = new System.Drawing.Point(10, 225); this.checkbox_reuseeditor.Name = "checkbox_reuseeditor"; - this.checkbox_reuseeditor.PropertyName = nameof(EditorConfiguration.ReuseEditor); + this.checkbox_reuseeditor.PropertyName = "ReuseEditor"; this.checkbox_reuseeditor.SectionName = "Editor"; this.checkbox_reuseeditor.Size = new System.Drawing.Size(394, 24); this.checkbox_reuseeditor.TabIndex = 9; @@ -1126,7 +1125,7 @@ namespace Greenshot.Forms { this.checkbox_minimizememoryfootprint.LanguageKey = "expertsettings_minimizememoryfootprint"; this.checkbox_minimizememoryfootprint.Location = new System.Drawing.Point(10, 206); this.checkbox_minimizememoryfootprint.Name = "checkbox_minimizememoryfootprint"; - this.checkbox_minimizememoryfootprint.PropertyName = nameof(coreConfiguration.MinimizeWorkingSetSize); + this.checkbox_minimizememoryfootprint.PropertyName = "MinimizeWorkingSetSize"; this.checkbox_minimizememoryfootprint.Size = new System.Drawing.Size(394, 24); this.checkbox_minimizememoryfootprint.TabIndex = 8; this.checkbox_minimizememoryfootprint.UseVisualStyleBackColor = true; @@ -1136,7 +1135,7 @@ namespace Greenshot.Forms { this.checkbox_checkunstableupdates.LanguageKey = "expertsettings_checkunstableupdates"; this.checkbox_checkunstableupdates.Location = new System.Drawing.Point(10, 187); this.checkbox_checkunstableupdates.Name = "checkbox_checkunstableupdates"; - this.checkbox_checkunstableupdates.PropertyName = nameof(coreConfiguration.CheckForUnstable); + this.checkbox_checkunstableupdates.PropertyName = "CheckForUnstable"; this.checkbox_checkunstableupdates.Size = new System.Drawing.Size(394, 24); this.checkbox_checkunstableupdates.TabIndex = 7; this.checkbox_checkunstableupdates.UseVisualStyleBackColor = true; @@ -1146,7 +1145,7 @@ namespace Greenshot.Forms { this.checkbox_suppresssavedialogatclose.LanguageKey = "expertsettings_suppresssavedialogatclose"; this.checkbox_suppresssavedialogatclose.Location = new System.Drawing.Point(10, 168); this.checkbox_suppresssavedialogatclose.Name = "checkbox_suppresssavedialogatclose"; - this.checkbox_suppresssavedialogatclose.PropertyName = nameof(EditorConfiguration.SuppressSaveDialogAtClose); + this.checkbox_suppresssavedialogatclose.PropertyName = "SuppressSaveDialogAtClose"; this.checkbox_suppresssavedialogatclose.SectionName = "Editor"; this.checkbox_suppresssavedialogatclose.Size = new System.Drawing.Size(394, 24); this.checkbox_suppresssavedialogatclose.TabIndex = 6; @@ -1164,7 +1163,7 @@ namespace Greenshot.Forms { // this.textbox_counter.Location = new System.Drawing.Point(259, 282); this.textbox_counter.Name = "textbox_counter"; - this.textbox_counter.PropertyName = nameof(coreConfiguration.OutputFileIncrementingNumber); + this.textbox_counter.PropertyName = "OutputFileIncrementingNumber"; this.textbox_counter.Size = new System.Drawing.Size(141, 20); this.textbox_counter.TabIndex = 11; // @@ -1181,7 +1180,7 @@ namespace Greenshot.Forms { // this.textbox_footerpattern.Location = new System.Drawing.Point(138, 256); this.textbox_footerpattern.Name = "textbox_footerpattern"; - this.textbox_footerpattern.PropertyName = nameof(coreConfiguration.OutputPrintFooterPattern); + this.textbox_footerpattern.PropertyName = "OutputPrintFooterPattern"; this.textbox_footerpattern.Size = new System.Drawing.Size(262, 20); this.textbox_footerpattern.TabIndex = 10; // @@ -1190,7 +1189,7 @@ namespace Greenshot.Forms { this.checkbox_thumbnailpreview.LanguageKey = "expertsettings_thumbnailpreview"; this.checkbox_thumbnailpreview.Location = new System.Drawing.Point(10, 149); this.checkbox_thumbnailpreview.Name = "checkbox_thumbnailpreview"; - this.checkbox_thumbnailpreview.PropertyName = nameof(coreConfiguration.ThumnailPreview); + this.checkbox_thumbnailpreview.PropertyName = "ThumnailPreview"; this.checkbox_thumbnailpreview.Size = new System.Drawing.Size(394, 24); this.checkbox_thumbnailpreview.TabIndex = 5; this.checkbox_thumbnailpreview.UseVisualStyleBackColor = true; @@ -1200,7 +1199,7 @@ namespace Greenshot.Forms { this.checkbox_optimizeforrdp.LanguageKey = "expertsettings_optimizeforrdp"; this.checkbox_optimizeforrdp.Location = new System.Drawing.Point(10, 130); this.checkbox_optimizeforrdp.Name = "checkbox_optimizeforrdp"; - this.checkbox_optimizeforrdp.PropertyName = nameof(coreConfiguration.OptimizeForRDP); + this.checkbox_optimizeforrdp.PropertyName = "OptimizeForRDP"; this.checkbox_optimizeforrdp.Size = new System.Drawing.Size(394, 24); this.checkbox_optimizeforrdp.TabIndex = 4; this.checkbox_optimizeforrdp.UseVisualStyleBackColor = true; @@ -1210,7 +1209,7 @@ namespace Greenshot.Forms { this.checkbox_autoreducecolors.LanguageKey = "expertsettings_autoreducecolors"; this.checkbox_autoreducecolors.Location = new System.Drawing.Point(10, 111); this.checkbox_autoreducecolors.Name = "checkbox_autoreducecolors"; - this.checkbox_autoreducecolors.PropertyName = nameof(coreConfiguration.OutputFileAutoReduceColors); + this.checkbox_autoreducecolors.PropertyName = "OutputFileAutoReduceColors"; this.checkbox_autoreducecolors.Size = new System.Drawing.Size(408, 24); this.checkbox_autoreducecolors.TabIndex = 3; this.checkbox_autoreducecolors.UseVisualStyleBackColor = true; @@ -1258,7 +1257,7 @@ namespace Greenshot.Forms { // // SettingsForm // - this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 14F); + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(451, 431); this.Controls.Add(this.tabcontrol); diff --git a/src/Greenshot/Forms/SettingsForm.cs b/src/Greenshot/Forms/SettingsForm.cs index 12f2f07cb..7fecda8e2 100644 --- a/src/Greenshot/Forms/SettingsForm.cs +++ b/src/Greenshot/Forms/SettingsForm.cs @@ -28,8 +28,6 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Windows.Forms; -using Dapplo.Windows.DesktopWindowsManager; -using Dapplo.Windows.Dpi; using Greenshot.Base; using Greenshot.Base.Controls; using Greenshot.Base.Core; @@ -37,7 +35,9 @@ using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Plugin; +using Greenshot.Base.UnmanagedHelpers; using Greenshot.Configuration; +using Greenshot.Destinations; using Greenshot.Helpers; using log4net; @@ -225,7 +225,7 @@ namespace Greenshot.Forms private void SetWindowCaptureMode(WindowCaptureMode selectedWindowCaptureMode) { WindowCaptureMode[] availableModes; - if (!DwmApi.IsDwmEnabled) + if (!DWM.IsDwmEnabled) { // Remove DWM from configuration, as DWM is disabled! if (coreConfiguration.WindowCaptureMode == WindowCaptureMode.Aero || coreConfiguration.WindowCaptureMode == WindowCaptureMode.AeroTransparent) @@ -320,7 +320,7 @@ namespace Greenshot.Forms combobox_language.SelectedValue = Language.CurrentLanguage; } - // Delaying the SelectedIndexChanged events until all is initiated + // Delaying the SelectedIndexChanged events untill all is initiated combobox_language.SelectedIndexChanged += Combobox_languageSelectedIndexChanged; UpdateDestinationDescriptions(); UpdateClipboardFormatDescriptions(); @@ -421,7 +421,7 @@ namespace Greenshot.Forms checkbox_picker.Checked = false; listview_destinations.Items.Clear(); - var scaledIconSize = DpiCalculator.ScaleWithDpi(coreConfiguration.IconSize, NativeDpiMethods.GetDpi(Handle)); + var scaledIconSize = DpiHelper.ScaleWithDpi(coreConfiguration.IconSize, DpiHelper.GetDpi(Handle)); listview_destinations.ListViewItemSorter = new ListviewWithDestinationComparer(); ImageList imageList = new ImageList { @@ -829,19 +829,22 @@ namespace Greenshot.Forms { public int Compare(object x, object y) { - if (x is not ListViewItem listViewItemX) + if (!(x is ListViewItem)) { return 0; } - if (y is not ListViewItem listViewItemY) + if (!(y is ListViewItem)) { return 0; } - IDestination firstDestination = listViewItemX.Tag as IDestination; + ListViewItem l1 = (ListViewItem) x; + ListViewItem l2 = (ListViewItem) y; - if (listViewItemY.Tag is not IDestination secondDestination) + IDestination firstDestination = l1.Tag as IDestination; + + if (!(l2.Tag is IDestination secondDestination)) { return 1; } diff --git a/src/Greenshot/Forms/ToolStripMenuSelectList.cs b/src/Greenshot/Forms/ToolStripMenuSelectList.cs index 2e2c9845c..db6f8a184 100644 --- a/src/Greenshot/Forms/ToolStripMenuSelectList.cs +++ b/src/Greenshot/Forms/ToolStripMenuSelectList.cs @@ -23,10 +23,8 @@ using System; using System.Collections; using System.Drawing; using System.Windows.Forms; -using Dapplo.Windows.Dpi; using Greenshot.Base.Core; using Greenshot.Base.IniFile; -using Greenshot.Base.Interfaces; namespace Greenshot.Forms { @@ -37,7 +35,6 @@ namespace Greenshot.Forms { private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); private readonly bool _multiCheckAllowed; - private readonly IProvideDeviceDpi _provideDeviceDpi; private bool _updateInProgress; private static Image _defaultImage; @@ -48,29 +45,25 @@ namespace Greenshot.Forms public object Identifier { get; private set; } - public ToolStripMenuSelectList(object identifier, bool allowMultiCheck, IProvideDeviceDpi provideDeviceDpi) + public ToolStripMenuSelectList(object identifier, bool allowMultiCheck) { Identifier = identifier; CheckOnClick = false; _multiCheckAllowed = allowMultiCheck; - _provideDeviceDpi = provideDeviceDpi; - UpdateImage(); - } - - - private void UpdateImage() - { - var newSize = DpiCalculator.ScaleWithDpi(CoreConfig.IconSize, _provideDeviceDpi.DeviceDpi); - if (_defaultImage == null || _defaultImage.Size != newSize) + if (_defaultImage == null || _defaultImage.Size != CoreConfig.ScaledIconSize) { _defaultImage?.Dispose(); - _defaultImage = ImageHelper.CreateEmpty(newSize.Width, newSize.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb, + _defaultImage = ImageHelper.CreateEmpty(CoreConfig.ScaledIconSize.Width, CoreConfig.ScaledIconSize.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb, Color.Transparent, 96f, 96f); } Image = _defaultImage; } + public ToolStripMenuSelectList() : this(null, false) + { + } + private void ItemCheckStateChanged(object sender, EventArgs e) { if (_updateInProgress) diff --git a/src/Greenshot/Greenshot.csproj b/src/Greenshot/Greenshot.csproj index f0d97afe9..78fa3f6c6 100644 --- a/src/Greenshot/Greenshot.csproj +++ b/src/Greenshot/Greenshot.csproj @@ -9,10 +9,6 @@ false false - - none - false - @@ -21,8 +17,7 @@ - - + @@ -80,7 +75,6 @@ - diff --git a/src/Greenshot/Helpers/CaptureHelper.cs b/src/Greenshot/Helpers/CaptureHelper.cs index e51b51ebe..3d0d3385d 100644 --- a/src/Greenshot/Helpers/CaptureHelper.cs +++ b/src/Greenshot/Helpers/CaptureHelper.cs @@ -28,16 +28,12 @@ using System.IO; using System.Text; using System.Threading; using System.Windows.Forms; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; -using Dapplo.Windows.DesktopWindowsManager; -using Dapplo.Windows.Kernel32; -using Dapplo.Windows.User32; using Greenshot.Base; using Greenshot.Base.Core; using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; +using Greenshot.Base.UnmanagedHelpers; using Greenshot.Configuration; using Greenshot.Editor.Destinations; using Greenshot.Editor.Drawing; @@ -56,7 +52,7 @@ namespace Greenshot.Helpers private List _windows = new(); private WindowDetails _selectedCaptureWindow; - private NativeRect _captureRect = NativeRect.Empty; + private Rectangle _captureRect = Rectangle.Empty; private readonly bool _captureMouseCursor; private ICapture _capture; private CaptureMode _captureMode; @@ -93,7 +89,7 @@ namespace Greenshot.Helpers // Empty working set after capturing if (CoreConfig.MinimizeWorkingSetSize) { - PsApi.EmptyWorkingSet(); + PsAPI.EmptyWorkingSet(); } } @@ -120,7 +116,7 @@ namespace Greenshot.Helpers captureHelper.MakeCapture(); } - public static void CaptureRegion(bool captureMouse, NativeRect region) + public static void CaptureRegion(bool captureMouse, Rectangle region) { using CaptureHelper captureHelper = new CaptureHelper(CaptureMode.Region, captureMouse); captureHelper.MakeCapture(region); @@ -247,8 +243,8 @@ namespace Greenshot.Helpers /// /// Make Capture for region /// - /// NativeRect - private void MakeCapture(NativeRect region) + /// Rectangle + private void MakeCapture(Rectangle region) { _captureRect = region; MakeCapture(); @@ -280,7 +276,7 @@ namespace Greenshot.Helpers { case CaptureMode.Region: // Check if a region is pre-supplied! - if (_captureRect.IsEmpty) + if (Rectangle.Empty.Equals(_captureRect)) { retrieveWindowDetailsThread = PrepareForCaptureWithFeedback(); } @@ -355,7 +351,7 @@ namespace Greenshot.Helpers switch (_screenCaptureMode) { case ScreenCaptureMode.Auto: - NativePoint mouseLocation = User32Api.GetCursorLocation(); + Point mouseLocation = User32.GetCursorLocation(); foreach (Screen screen in Screen.AllScreens) { if (screen.Bounds.Contains(mouseLocation)) @@ -363,7 +359,8 @@ namespace Greenshot.Helpers _capture = WindowCapture.CaptureRectangle(_capture, screen.Bounds); captureTaken = true; // As the screen shot might be on a different monitor we need to correct the mouse location - var correctedCursorLocation = _capture.CursorLocation.Offset(-screen.Bounds.Location.X, -screen.Bounds.Location.Y); + var correctedCursorLocation = _capture.CursorLocation; + correctedCursorLocation.Offset(-screen.Bounds.Location.X, -screen.Bounds.Location.Y); _capture.CursorLocation = correctedCursorLocation; break; } @@ -392,7 +389,6 @@ namespace Greenshot.Helpers HandleCapture(); break; case CaptureMode.Clipboard: - // TODO: Fix getting image vs. drawablecontainer Image clipboardImage = ClipboardHelper.GetImage(); if (clipboardImage != null) { @@ -434,13 +430,12 @@ namespace Greenshot.Helpers if (!string.IsNullOrEmpty(filename)) { - // TODO: Fix that the Greenshot format needs a separate code path try { if (filename.ToLower().EndsWith("." + OutputFormat.greenshot)) { ISurface surface = new Surface(); - surface = ImageIO.LoadGreenshotSurface(filename, surface); + surface = ImageOutput.LoadGreenshotSurface(filename, surface); surface.CaptureDetails = _capture.CaptureDetails; DestinationHelper.GetDestination(EditorDestination.DESIGNATION).ExportCapture(true, surface, _capture.CaptureDetails); break; @@ -452,10 +447,9 @@ namespace Greenshot.Helpers MessageBox.Show(Language.GetFormattedString(LangKey.error_openfile, filename)); } - // TODO: Remove Image loading for here try { - fileImage = ImageIO.LoadImage(filename); + fileImage = ImageHelper.LoadImage(filename); } catch (Exception e) { @@ -507,12 +501,14 @@ namespace Greenshot.Helpers // Set capture title, fixing bug #3569703 foreach (WindowDetails window in WindowDetails.GetVisibleWindows()) { - NativePoint estimatedLocation = new NativePoint(CoreConfig.LastCapturedRegion.X + CoreConfig.LastCapturedRegion.Width / 2, + Point estimatedLocation = new Point(CoreConfig.LastCapturedRegion.X + CoreConfig.LastCapturedRegion.Width / 2, CoreConfig.LastCapturedRegion.Y + CoreConfig.LastCapturedRegion.Height / 2); - if (!window.Contains(estimatedLocation)) continue; - _selectedCaptureWindow = window; - _capture.CaptureDetails.Title = _selectedCaptureWindow.Text; - break; + if (window.Contains(estimatedLocation)) + { + _selectedCaptureWindow = window; + _capture.CaptureDetails.Title = _selectedCaptureWindow.Text; + break; + } } // Move cursor, fixing bug #3569703 @@ -527,7 +523,7 @@ namespace Greenshot.Helpers break; case CaptureMode.Region: // Check if a region is pre-supplied! - if (_captureRect.IsEmpty) + if (Rectangle.Empty.Equals(_captureRect)) { _capture = WindowCapture.CaptureScreen(_capture); _capture.CaptureDetails.AddMetaData("source", "screen"); @@ -564,6 +560,14 @@ namespace Greenshot.Helpers { _windows = new List(); + // If the App Launcher is visible, no other windows are active + WindowDetails appLauncherWindow = WindowDetails.GetAppLauncher(); + if (appLauncherWindow != null && appLauncherWindow.Visible) + { + _windows.Add(appLauncherWindow); + return null; + } + Thread getWindowDetailsThread = new Thread(RetrieveWindowDetails) { Name = "Retrieve window details", @@ -678,7 +682,7 @@ namespace Greenshot.Helpers if (_capture.CaptureDetails.CaptureMode == CaptureMode.Text) { - var selectionRectangle = new NativeRect(NativePoint.Empty, _capture.Image.Size); + var selectionRectangle = new Rectangle(Point.Empty, _capture.Image.Size); var ocrInfo = _capture.CaptureDetails.OcrInformation; if (ocrInfo != null) { @@ -705,10 +709,7 @@ namespace Greenshot.Helpers textResult.AppendLine(); } - if (textResult.Length > 0) - { - Clipboard.SetText(textResult.ToString()); - } + Clipboard.SetText(textResult.ToString()); } // Disable capturing @@ -869,7 +870,7 @@ namespace Greenshot.Helpers /// WindowDetails with the target Window OR a replacement public static WindowDetails SelectCaptureWindow(WindowDetails windowToCapture) { - NativeRect windowRectangle = windowToCapture.WindowRectangle; + Rectangle windowRectangle = windowToCapture.WindowRectangle; if (windowRectangle.Width == 0 || windowRectangle.Height == 0) { Log.WarnFormat("Window {0} has nothing to capture, using workaround to find other window of same process.", windowToCapture.Text); @@ -935,10 +936,10 @@ namespace Greenshot.Helpers captureForWindow = new Capture(); } - NativeRect windowRectangle = windowToCapture.WindowRectangle; + Rectangle windowRectangle = windowToCapture.WindowRectangle; // When Vista & DWM (Aero) enabled - bool dwmEnabled = DwmApi.IsDwmEnabled; + bool dwmEnabled = DWM.IsDwmEnabled; // get process name to be able to exclude certain processes from certain capture modes using (Process process = windowToCapture.Process) { @@ -976,7 +977,7 @@ namespace Greenshot.Helpers else { // Change to GDI, if allowed - if (WindowCapture.IsGdiAllowed(process)) + if (!windowToCapture.IsMetroApp && WindowCapture.IsGdiAllowed(process)) { if (!dwmEnabled && IsWpf(process)) { @@ -992,7 +993,7 @@ namespace Greenshot.Helpers // Change to DWM, if enabled and allowed if (dwmEnabled) { - if (WindowCapture.IsDwmAllowed(process)) + if (windowToCapture.IsMetroApp || WindowCapture.IsDwmAllowed(process)) { windowCaptureMode = WindowCaptureMode.Aero; } @@ -1001,7 +1002,7 @@ namespace Greenshot.Helpers } else if (windowCaptureMode == WindowCaptureMode.Aero || windowCaptureMode == WindowCaptureMode.AeroTransparent) { - if (!dwmEnabled || !WindowCapture.IsDwmAllowed(process)) + if (!dwmEnabled || (!windowToCapture.IsMetroApp && !WindowCapture.IsDwmAllowed(process))) { // Take default screen windowCaptureMode = WindowCaptureMode.Screen; @@ -1020,7 +1021,7 @@ namespace Greenshot.Helpers Log.InfoFormat("Capturing window with mode {0}", windowCaptureMode); bool captureTaken = false; - windowRectangle = windowRectangle.Intersect(captureForWindow.ScreenBounds); + windowRectangle.Intersect(captureForWindow.ScreenBounds); // Try to capture while (!captureTaken) { @@ -1107,7 +1108,7 @@ namespace Greenshot.Helpers break; case WindowCaptureMode.Aero: case WindowCaptureMode.AeroTransparent: - if (WindowCapture.IsDwmAllowed(process)) + if (windowToCapture.IsMetroApp || WindowCapture.IsDwmAllowed(process)) { tmpCapture = windowToCapture.CaptureDwmWindow(captureForWindow, windowCaptureMode, isAutoMode); } @@ -1212,7 +1213,8 @@ namespace Greenshot.Helpers // save for re-capturing later and show recapture context menu option // Important here is that the location needs to be offsetted back to screen coordinates! - NativeRect tmpRectangle = _captureRect.Offset(_capture.ScreenBounds.Location.X, _capture.ScreenBounds.Location.Y); + Rectangle tmpRectangle = _captureRect; + tmpRectangle.Offset(_capture.ScreenBounds.Location.X, _capture.ScreenBounds.Location.Y); CoreConfig.LastCapturedRegion = tmpRectangle; } diff --git a/src/Greenshot/Helpers/IECaptureHelper.cs b/src/Greenshot/Helpers/IECaptureHelper.cs index 0da3d35dc..3bb5e4eee 100644 --- a/src/Greenshot/Helpers/IECaptureHelper.cs +++ b/src/Greenshot/Helpers/IECaptureHelper.cs @@ -25,17 +25,14 @@ using System.Drawing; using System.Drawing.Imaging; using System.Linq; using System.Runtime.InteropServices; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; -using Dapplo.Windows.Messages; -using Dapplo.Windows.User32; -using Dapplo.Windows.User32.Enums; using Greenshot.Base.Controls; using Greenshot.Base.Core; using Greenshot.Base.IEInterop; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using Greenshot.Base.Interop; +using Greenshot.Base.UnmanagedHelpers; +using Greenshot.Base.UnmanagedHelpers.Enums; using Greenshot.Configuration; using Greenshot.Helpers.IEInterop; using log4net; @@ -208,7 +205,7 @@ namespace Greenshot.Helpers return null; } - uint windowMessage = WindowsMessage.RegisterWindowsMessage("WM_HTML_GETOBJECT"); + uint windowMessage = User32.RegisterWindowMessage("WM_HTML_GETOBJECT"); if (windowMessage == 0) { Log.WarnFormat("Couldn't register WM_HTML_GETOBJECT"); @@ -216,7 +213,7 @@ namespace Greenshot.Helpers } Log.DebugFormat("Trying WM_HTML_GETOBJECT on {0}", ieServer.ClassName); - User32Api.SendMessageTimeout(ieServer.Handle, windowMessage, IntPtr.Zero, IntPtr.Zero, SendMessageTimeoutFlags.Normal, 5000, out UIntPtr response); + User32.SendMessageTimeout(ieServer.Handle, windowMessage, IntPtr.Zero, IntPtr.Zero, SendMessageTimeoutFlags.SMTO_NORMAL, 5000, out var response); IHTMLDocument2 document2; if (response != UIntPtr.Zero) { @@ -722,14 +719,14 @@ namespace Greenshot.Helpers int horizontalPage = 0; // The location of the browser, used as the destination into the bitmap target - NativePoint targetOffset = NativePoint.Empty; + Point targetOffset = new Point(); // Loop of the pages and make a copy of the visible viewport while (horizontalPage * viewportWidth < pageWidth) { // Scroll to location documentContainer.ScrollLeft = viewportWidth * horizontalPage; - targetOffset = targetOffset.ChangeX(documentContainer.ScrollLeft); + targetOffset.X = documentContainer.ScrollLeft; // Variable used for looping vertically int verticalPage = 0; @@ -738,11 +735,11 @@ namespace Greenshot.Helpers // Scroll to location documentContainer.ScrollTop = viewportHeight * verticalPage; //Shoot visible window - targetOffset = targetOffset.ChangeY(documentContainer.ScrollTop); + targetOffset.Y = documentContainer.ScrollTop; // Draw the captured fragment to the target, but "crop" the scrollbars etc while capturing - NativeSize viewPortSize = new NativeSize(viewportWidth, viewportHeight); - NativeRect clientRectangle = new NativeRect(documentContainer.SourceLocation, viewPortSize); + Size viewPortSize = new Size(viewportWidth, viewportHeight); + Rectangle clientRectangle = new Rectangle(documentContainer.SourceLocation, viewPortSize); Image fragment = contentWindowDetails.PrintWindow(); if (fragment != null) { @@ -750,7 +747,7 @@ namespace Greenshot.Helpers try { // cut all junk, due to IE "border" we need to remove some parts - NativeRect viewportRect = documentContainer.ViewportRectangle; + Rectangle viewportRect = documentContainer.ViewportRectangle; if (!viewportRect.IsEmpty) { Log.DebugFormat("Cropping to viewport: {0}", viewportRect); @@ -761,9 +758,9 @@ namespace Greenshot.Helpers // Crop to clientRectangle if (ImageHelper.Crop(ref fragment, ref clientRectangle)) { - NativePoint targetLocation = new NativePoint(documentContainer.DestinationLocation.X, documentContainer.DestinationLocation.Y); + Point targetLocation = new Point(documentContainer.DestinationLocation.X, documentContainer.DestinationLocation.Y); Log.DebugFormat("Fragment targetLocation is {0}", targetLocation); - targetLocation = targetLocation.Offset(targetOffset); + targetLocation.Offset(targetOffset); Log.DebugFormat("After offsetting the fragment targetLocation is {0}", targetLocation); Log.DebugFormat("Drawing fragment of size {0} to {1}", fragment.Size, targetLocation); graphicsTarget.DrawImage(fragment, targetLocation); diff --git a/src/Greenshot/Helpers/IEInterop/IEContainer.cs b/src/Greenshot/Helpers/IEInterop/IEContainer.cs index 178dc5643..77e20d695 100644 --- a/src/Greenshot/Helpers/IEInterop/IEContainer.cs +++ b/src/Greenshot/Helpers/IEInterop/IEContainer.cs @@ -24,8 +24,6 @@ using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.Runtime.InteropServices; -using Dapplo.Windows.Common.Extensions; -using Dapplo.Windows.Common.Structs; using Greenshot.Base.Core; using Greenshot.Base.IEInterop; using log4net; @@ -40,12 +38,12 @@ namespace Greenshot.Helpers.IEInterop private static readonly Guid IID_IWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046"); private static readonly Guid IID_IWebBrowser2 = new Guid("D30C1661-CDAF-11D0-8A3E-00C04FC9E26E"); private static int _counter; - private readonly NativePoint _startLocation = NativePoint.Empty; private IHTMLDocument2 _document2; private IHTMLDocument3 _document3; - private NativePoint _sourceLocation; - private NativePoint _destinationLocation; - private NativeRect _viewportRectangle = NativeRect.Empty; + private Point _sourceLocation; + private Point _destinationLocation; + private Point _startLocation = Point.Empty; + private Rectangle _viewportRectangle = Rectangle.Empty; private bool _isDtd; private DocumentContainer _parent; private WindowDetails _contentWindow; @@ -90,15 +88,15 @@ namespace Greenshot.Helpers.IEInterop // Calculate startLocation for the frames IHTMLWindow2 window2 = document2.parentWindow; IHTMLWindow3 window3 = (IHTMLWindow3) window2; - NativePoint contentWindowLocation = contentWindow.WindowRectangle.Location; + Point contentWindowLocation = contentWindow.WindowRectangle.Location; int x = window3.screenLeft - contentWindowLocation.X; int y = window3.screenTop - contentWindowLocation.Y; // Release IHTMLWindow 2+3 com objects - ReleaseCom(window2); - ReleaseCom(window3); + releaseCom(window2); + releaseCom(window3); - _startLocation = new NativePoint(x, y); + _startLocation = new Point(x, y); Init(document2, contentWindow); } @@ -112,7 +110,7 @@ namespace Greenshot.Helpers.IEInterop /// Helper method to release com objects /// /// - private void ReleaseCom(object comObject) + private void releaseCom(object comObject) { if (comObject != null) { @@ -150,7 +148,7 @@ namespace Greenshot.Helpers.IEInterop // Do not release IHTMLDocument5 com object, as this also gives problems with the document2! //Marshal.ReleaseComObject(document5); - NativeRect clientRectangle = contentWindow.WindowRectangle; + Rectangle clientRectangle = contentWindow.WindowRectangle; try { IHTMLWindow2 window2 = document2.parentWindow; @@ -182,20 +180,20 @@ namespace Greenshot.Helpers.IEInterop int diffY = clientRectangle.Height - ClientHeight; // If there is a border around the inner window, the diff == 4 // If there is a border AND a scrollbar the diff == 20 - if (diffX is 4 or >= 20 && diffY is 4 or >= 20) + if ((diffX == 4 || diffX >= 20) && (diffY == 4 || diffY >= 20)) { - var viewportOffset = new NativePoint(2, 2); - var viewportSize = new NativeSize(ClientWidth, ClientHeight); - _viewportRectangle = new NativeRect(viewportOffset, viewportSize); + Point viewportOffset = new Point(2, 2); + Size viewportSize = new Size(ClientWidth, ClientHeight); + _viewportRectangle = new Rectangle(viewportOffset, viewportSize); LOG.DebugFormat("viewportRect {0}", _viewportRectangle); } } LOG.DebugFormat("Zoomlevel {0}, {1}", _zoomLevelX, _zoomLevelY); // Release com objects - ReleaseCom(window2); - ReleaseCom(screen); - ReleaseCom(screen2); + releaseCom(window2); + releaseCom(screen); + releaseCom(screen2); } catch (Exception e) { @@ -225,8 +223,8 @@ namespace Greenshot.Helpers.IEInterop LOG.Warn("Problem while trying to get document url!", e); } - _sourceLocation = new NativePoint(ScaleX(_startLocation.X), ScaleY(_startLocation.Y)); - _destinationLocation = new NativePoint(ScaleX(_startLocation.X), ScaleY(_startLocation.Y)); + _sourceLocation = new Point(ScaleX(_startLocation.X), ScaleY(_startLocation.Y)); + _destinationLocation = new Point(ScaleX(_startLocation.X), ScaleY(_startLocation.Y)); if (_parent != null) { @@ -254,7 +252,7 @@ namespace Greenshot.Helpers.IEInterop } // Clean up frameWindow - ReleaseCom(frameWindow); + releaseCom(frameWindow); } catch (Exception e) { @@ -263,7 +261,7 @@ namespace Greenshot.Helpers.IEInterop } // Clean up collection - ReleaseCom(frameCollection); + releaseCom(frameCollection); } catch (Exception ex) { @@ -279,7 +277,7 @@ namespace Greenshot.Helpers.IEInterop { CorrectFrameLocations(frameElement); // Clean up frameElement - ReleaseCom(frameElement); + releaseCom(frameElement); } catch (Exception e) { @@ -294,7 +292,7 @@ namespace Greenshot.Helpers.IEInterop } /// - /// Correct the frame locations with the information + /// Corrent the frame locations with the information /// /// private void CorrectFrameLocations(IHTMLElement frameElement) @@ -311,22 +309,22 @@ namespace Greenshot.Helpers.IEInterop // Release element, but prevent the frameElement to be released if (oldElement != null) { - ReleaseCom(oldElement); + releaseCom(oldElement); } oldElement = element; } while (element != null); - var elementLocation = new NativePoint((int) x, (int) y); + Point elementLocation = new Point((int) x, (int) y); IHTMLElement2 element2 = (IHTMLElement2) frameElement; IHTMLRect rec = element2.getBoundingClientRect(); - var elementBoundingLocation = new NativePoint(rec.left, rec.top); + Point elementBoundingLocation = new Point(rec.left, rec.top); // Release IHTMLRect - ReleaseCom(rec); + releaseCom(rec); LOG.DebugFormat("Looking for iframe to correct at {0}", elementBoundingLocation); foreach (DocumentContainer foundFrame in _frames) { - NativePoint frameLocation = foundFrame.SourceLocation; + Point frameLocation = foundFrame.SourceLocation; if (frameLocation.Equals(elementBoundingLocation)) { // Match found, correcting location @@ -427,7 +425,7 @@ namespace Greenshot.Helpers.IEInterop } } - public NativeRect ViewportRectangle => _viewportRectangle; + public Rectangle ViewportRectangle => _viewportRectangle; public WindowDetails ContentWindow => _contentWindow; @@ -475,7 +473,7 @@ namespace Greenshot.Helpers.IEInterop var element = !_isDtd ? _document2.body : _document3.documentElement; element.setAttribute(attribute, value, 1); // Release IHTMLElement com object - ReleaseCom(element); + releaseCom(element); } /// @@ -488,7 +486,7 @@ namespace Greenshot.Helpers.IEInterop var element = !_isDtd ? _document2.body : _document3.documentElement; var retVal = element.getAttribute(attribute, 1); // Release IHTMLElement com object - ReleaseCom(element); + releaseCom(element); return retVal; } @@ -517,15 +515,15 @@ namespace Greenshot.Helpers.IEInterop public int ScrollHeight => ScaleY(GetAttributeAsInt("scrollHeight")); - public NativePoint SourceLocation + public Point SourceLocation { get { return _sourceLocation; } set { _sourceLocation = value; } } - public NativeSize SourceSize => new NativeSize(ClientWidth, ClientHeight); + public Size SourceSize => new Size(ClientWidth, ClientHeight); - public NativeRect SourceRectangle => new NativeRect(SourceLocation, SourceSize); + public Rectangle SourceRectangle => new Rectangle(SourceLocation, SourceSize); public int SourceLeft => _sourceLocation.X; @@ -535,26 +533,26 @@ namespace Greenshot.Helpers.IEInterop public int SourceBottom => _sourceLocation.Y + ClientHeight; - public NativePoint DestinationLocation + public Point DestinationLocation { get { return _destinationLocation; } set { _destinationLocation = value; } } - public NativeSize DestinationSize => new NativeSize(ScrollWidth, ScrollHeight); + public Size DestinationSize => new Size(ScrollWidth, ScrollHeight); - public NativeRect DestinationRectangle => new NativeRect(DestinationLocation, DestinationSize); + public Rectangle DestinationRectangle => new Rectangle(DestinationLocation, DestinationSize); public int DestinationLeft { get { return _destinationLocation.X; } - set { _destinationLocation = _destinationLocation.ChangeX(value); } + set { _destinationLocation.X = value; } } public int DestinationTop { get { return _destinationLocation.Y; } - set { _destinationLocation = _destinationLocation.ChangeY(value); } + set { _destinationLocation.Y = value; } } public int DestinationRight => _destinationLocation.X + ScrollWidth; diff --git a/src/Greenshot/Helpers/MailHelper.cs b/src/Greenshot/Helpers/MailHelper.cs index 39aa3c150..f3975a205 100644 --- a/src/Greenshot/Helpers/MailHelper.cs +++ b/src/Greenshot/Helpers/MailHelper.cs @@ -82,7 +82,7 @@ namespace Greenshot.Helpers /// ICaptureDetails public static void SendImage(ISurface surface, ICaptureDetails captureDetails) { - string tmpFile = ImageIO.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings()); + string tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings()); if (tmpFile == null) return; diff --git a/src/Greenshot/Helpers/PluginHelper.cs b/src/Greenshot/Helpers/PluginHelper.cs index e5724a4f5..3ed3d97c3 100644 --- a/src/Greenshot/Helpers/PluginHelper.cs +++ b/src/Greenshot/Helpers/PluginHelper.cs @@ -219,15 +219,18 @@ namespace Greenshot.Helpers { var assembly = Assembly.LoadFrom(pluginFile); - if (IsPluginExcludedByConfig(assembly, pluginFile) ) - { - continue; - } - var assemblyName = assembly.GetName().Name; + var pluginEntryName = $"{assemblyName}.{assemblyName.Replace("Greenshot.Plugin.", string.Empty)}Plugin"; var pluginEntryType = assembly.GetType(pluginEntryName, false, true); + if (CoreConfig.ExcludePlugins != null && CoreConfig.ExcludePlugins.Contains(pluginEntryName)) + { + Log.WarnFormat("Exclude list: {0}", string.Join(",", CoreConfig.ExcludePlugins)); + Log.WarnFormat("Skipping the excluded plugin {0} with version {1} from {2}", pluginEntryName, assembly.GetName().Version, pluginFile); + continue; + } + var plugin = (IGreenshotPlugin) Activator.CreateInstance(pluginEntryType); if (plugin != null) { @@ -252,54 +255,5 @@ namespace Greenshot.Helpers } } } - /// - /// This method checks the plugin against the configured include and exclude plugin - /// lists. If a plugin is excluded, a warning is logged with details about the exclusion. - /// - private bool IsPluginExcludedByConfig(Assembly assembly, string pluginFile) - { - // Get plugin identifier from assembly attributes - string pluginConfigIdentifier = GetPluginIdentifier(assembly, pluginFile); - - if (CoreConfig.IncludePlugins is { } includePlugins - && includePlugins.Count(p => !string.IsNullOrWhiteSpace(p)) > 0 // ignore empty entries i.e. a whitespace - && !includePlugins.Contains(pluginConfigIdentifier)) - { - Log.WarnFormat("Include plugin list: {0}", string.Join(",", includePlugins)); - Log.WarnFormat("Skipping the not included plugin '{0}' with version {1} from {2}", pluginConfigIdentifier, assembly.GetName().Version, pluginFile); - return true; - } - - if (CoreConfig.ExcludePlugins is { } excludePlugins - && excludePlugins.Contains(pluginConfigIdentifier)) - { - Log.WarnFormat("Exclude plugin list: {0}", string.Join(",", excludePlugins)); - Log.WarnFormat("Skipping the excluded plugin '{0}' with version {1} from {2}", pluginConfigIdentifier, assembly.GetName().Version, pluginFile); - return true; - } - - return false; - } - - /// - /// Retrieves the plugin identifier for the specified assembly. - /// - private string GetPluginIdentifier(Assembly assembly, string pluginFile) - { - // Try to find PluginIdentifierAttribute - var attribute = assembly - .GetCustomAttributes() - .FirstOrDefault(); - - if (!string.IsNullOrEmpty(attribute?.Identifier)) - { - return attribute.Identifier; - } - - // If no attribute found, fall back to the sub namespace - var pluginSubNamespace = assembly.GetName().Name.Replace("Greenshot.Plugin.", string.Empty); - Log.WarnFormat("No '{0}' found in '{1}'. Use plugin namespace '{2}' as fallback.", nameof(AssemblyPluginIdentifierAttribute), pluginFile, pluginSubNamespace); - return pluginSubNamespace; - } } } \ No newline at end of file diff --git a/src/Greenshot/Helpers/PrintHelper.cs b/src/Greenshot/Helpers/PrintHelper.cs index 36bf8257b..9e6a43a7f 100644 --- a/src/Greenshot/Helpers/PrintHelper.cs +++ b/src/Greenshot/Helpers/PrintHelper.cs @@ -187,7 +187,7 @@ namespace Greenshot.Helpers ApplyEffects(printOutputSettings); - bool disposeImage = ImageIO.CreateImageFromSurface(_surface, printOutputSettings, out var image); + bool disposeImage = ImageOutput.CreateImageFromSurface(_surface, printOutputSettings, out var image); try { ContentAlignment alignment = CoreConfig.OutputPrintCenter ? ContentAlignment.MiddleCenter : ContentAlignment.TopLeft; diff --git a/src/Greenshot/Helpers/SoundHelper.cs b/src/Greenshot/Helpers/SoundHelper.cs index 796aec077..69e42850f 100644 --- a/src/Greenshot/Helpers/SoundHelper.cs +++ b/src/Greenshot/Helpers/SoundHelper.cs @@ -24,10 +24,10 @@ using System.Reflection; using System.Resources; using System.Runtime.InteropServices; using System.IO; -using Dapplo.Windows.Multimedia; -using Dapplo.Windows.Multimedia.Enums; using Greenshot.Base.Core; using Greenshot.Base.IniFile; +using Greenshot.Base.UnmanagedHelpers; +using Greenshot.Base.UnmanagedHelpers.Enums; using log4net; namespace Greenshot.Helpers @@ -82,10 +82,10 @@ namespace Greenshot.Helpers if (_soundBuffer != null) { //Thread playSoundThread = new Thread(delegate() { - var flags = SoundSettings.Async | SoundSettings.Memory| SoundSettings.NoWait| SoundSettings.NoStop; + SoundFlags flags = SoundFlags.SND_ASYNC | SoundFlags.SND_MEMORY | SoundFlags.SND_NOWAIT | SoundFlags.SND_NOSTOP; try { - if (_gcHandle != null) WinMm.Play(_gcHandle.Value.AddrOfPinnedObject(), flags); + if (_gcHandle != null) WinMM.PlaySound(_gcHandle.Value.AddrOfPinnedObject(), (UIntPtr) 0, (uint) flags); } catch (Exception e) { @@ -105,14 +105,14 @@ namespace Greenshot.Helpers { if (_gcHandle != null) { - WinMm.StopPlaying(); + WinMM.PlaySound(null, (UIntPtr) 0, 0); _gcHandle.Value.Free(); _gcHandle = null; } } catch (Exception e) { - Log.Error("Error in de-initialize.", e); + Log.Error("Error in deinitialize.", e); } } } diff --git a/src/Greenshot/Languages/language-ar-SY.xml b/src/Greenshot/Languages/language-ar-SY.xml index a7363a326..c5c6edc84 100644 --- a/src/Greenshot/Languages/language-ar-SY.xml +++ b/src/Greenshot/Languages/language-ar-SY.xml @@ -62,7 +62,6 @@ انسخ المسار الي الذاكرة نسخ قص (C) - قص اداة التحديد (ESC) قص احذف diff --git a/src/Greenshot/Languages/language-ca-CA.xml b/src/Greenshot/Languages/language-ca-CA.xml index 323f7849f..9f92f7446 100644 --- a/src/Greenshot/Languages/language-ca-CA.xml +++ b/src/Greenshot/Languages/language-ca-CA.xml @@ -92,8 +92,6 @@ Abans de crear un nou informe d'error, us agrairem que comproveu que l'error no Copia el camí al porta-retalls Copia Retalla(C) - Retalla - Autorretalla Eina de selecció (ESC) Retalla Esborra @@ -305,7 +303,7 @@ Malgrat això, encara es poden utilitzar totes les característiques de Greensho Afegeix un comptador (I) Afegeix una bafarada (S) - Canvia la mida (Z) + Canvia la mida Configuració del canvi de mida Manté la relació d'aspecte Amplada diff --git a/src/Greenshot/Languages/language-cs-CZ.xml b/src/Greenshot/Languages/language-cs-CZ.xml index cc409c66f..915fd3fe0 100644 --- a/src/Greenshot/Languages/language-cs-CZ.xml +++ b/src/Greenshot/Languages/language-cs-CZ.xml @@ -93,8 +93,6 @@ Také bychom velmi ocenili, kdybyste zkontrolovali, zda tato chyba již není ev Kopírovat cestu do schránky Kopírovat Oříznout (C) - Oříznout - Automatické oříznutí Výběr objektů (ESC) Vystřihnout Odstranit @@ -308,7 +306,7 @@ Všechny funkce Greenshotu jsou stále dostupné přímo z místní nabídky bez Přidat počítadlo (I) Přidat textovou bublinu (S) - Změnit velikost (Z) + Změnit velikost Nastavení změny velikosti Zachovat poměr stran Šířka diff --git a/src/Greenshot/Languages/language-da-DK.xml b/src/Greenshot/Languages/language-da-DK.xml index b032e8aff..166774d34 100644 --- a/src/Greenshot/Languages/language-da-DK.xml +++ b/src/Greenshot/Languages/language-da-DK.xml @@ -469,9 +469,6 @@ tidspunktet, fx 11_58_32 (plus filendelsen angivet i indstillingerne). Beskær (C) - - Beskær - Åbn billede fra udklipsholder diff --git a/src/Greenshot/Languages/language-de-DE.xml b/src/Greenshot/Languages/language-de-DE.xml index b636da112..062cdd6a5 100644 --- a/src/Greenshot/Languages/language-de-DE.xml +++ b/src/Greenshot/Languages/language-de-DE.xml @@ -77,7 +77,6 @@ schnell zu finden. Vielen Dank :) Keine Anfangspunkt Automatisch zuschneiden - Automatisches Zuschneiden nicht möglich Hintergrundfarbe (0-9) Weichzeichner-Radius Fett @@ -93,11 +92,6 @@ schnell zu finden. Vielen Dank :) Pfad in Zwischenablage kopieren Kopieren Zuschneiden (C) - Zuschneiden - Modus - Zuschneiden - Vertikal ausschneiden - Horizontal ausschneiden - Automatisch zuschneiden Auswahlwerkzeug (Esc) Ausschneiden Gewähltes Element löschen @@ -312,7 +306,7 @@ Sie können aber auch alle Greenshot-Funktionen über das Kontextmenü des Green Zähler hinzufügen (I) Sprechblase hinzufügen (S) - Skalieren (Z) + Skalieren Einstellungen für Skalierung Seitenverhältnis beibehalten Breite diff --git a/src/Greenshot/Languages/language-de-x-franconia.xml b/src/Greenshot/Languages/language-de-x-franconia.xml index 34da6f0b7..b989bcfe7 100644 --- a/src/Greenshot/Languages/language-de-x-franconia.xml +++ b/src/Greenshot/Languages/language-de-x-franconia.xml @@ -88,8 +88,6 @@ Dangschee, wassd scho :) Bfad in däi Zwischnblach nei Kobiern Zamschneidn (C) - Zamschneidn - Audomadisch zamschneidn Angriffln (ESC) Ausschneidn Wech mid dem Ding! diff --git a/src/Greenshot/Languages/language-el-GR.xml b/src/Greenshot/Languages/language-el-GR.xml index 1feb74b51..14cb68bef 100644 --- a/src/Greenshot/Languages/language-el-GR.xml +++ b/src/Greenshot/Languages/language-el-GR.xml @@ -93,8 +93,6 @@ Αντιγραφή της θέσης του αρχείου στο πρόχειρο Αντιγραφή Περικοπή (C) - Περικοπή - Αυτόματη Περικοπή Εργαλείο Επιλογής (ESC) Αποκοπή Διαγραφή diff --git a/src/Greenshot/Languages/language-en-US.xml b/src/Greenshot/Languages/language-en-US.xml index 1403d3e9c..b575f549b 100644 --- a/src/Greenshot/Languages/language-en-US.xml +++ b/src/Greenshot/Languages/language-en-US.xml @@ -78,7 +78,6 @@ Also, we would highly appreciate if you checked whether a tracker item already e None Start point Auto crop - Auto crop not possible Fill color (0-9) Blur radius Bold @@ -94,11 +93,6 @@ Also, we would highly appreciate if you checked whether a tracker item already e Copy path to clipboard Copy Crop (C) - Crop mode - Crop - Crop out vertically - Crop out horizontally - Auto crop Selection Tool (ESC) Cut Delete @@ -312,7 +306,7 @@ All Greenshot features still work directly from the tray icon context menu witho Add counter (I) Add speechbubble (S) - Resize (Z) + Resize Resize settings Maintain aspect ratio Width diff --git a/src/Greenshot/Languages/language-es-ES.xml b/src/Greenshot/Languages/language-es-ES.xml index 0badf64a4..b84aee912 100644 --- a/src/Greenshot/Languages/language-es-ES.xml +++ b/src/Greenshot/Languages/language-es-ES.xml @@ -80,8 +80,6 @@ Antes de crear un nuevo informe de error, te agradeceríamos que comprobaras que Copiar ruta al portapapeles Copiar Recortar(C) - Recortar - Autorrecortar Herramienta de selección (ESC) Cortar Borrar diff --git a/src/Greenshot/Languages/language-et-EE.xml b/src/Greenshot/Languages/language-et-EE.xml index 7a4b0dcc3..b358977e8 100644 --- a/src/Greenshot/Languages/language-et-EE.xml +++ b/src/Greenshot/Languages/language-et-EE.xml @@ -92,8 +92,6 @@ Me oleksime väga tänulik, kui te enne kontrolliksite, ega sellest veast pole j Kopeerige asukoht lõikelauale Kopeeri Lõika (C) - Lõika - Automaatne lõikus Valiku tööriist (ESC) Lõika Kustuta diff --git a/src/Greenshot/Languages/language-fa-IR.xml b/src/Greenshot/Languages/language-fa-IR.xml index 2ab2c8242..20e1da09f 100644 --- a/src/Greenshot/Languages/language-fa-IR.xml +++ b/src/Greenshot/Languages/language-fa-IR.xml @@ -67,7 +67,6 @@ Could not save Greenshot's configuration file. Please check access permissions f رونویس چیدن (C دکمه) - چیدن انتخابگر (ESC دکمه) برش diff --git a/src/Greenshot/Languages/language-fi-FI.xml b/src/Greenshot/Languages/language-fi-FI.xml index a2f211b03..f66cc5013 100644 --- a/src/Greenshot/Languages/language-fi-FI.xml +++ b/src/Greenshot/Languages/language-fi-FI.xml @@ -62,7 +62,6 @@ Olisi myös hyvä jos voisit tarkistaa onko virhe jo raportoitu aikaisemmin (voi Kopioi tiedostopolku leikepöydälle Kopioi Rajaa (C) - Rajaa Valintatyökalu (ESC) Leikkaa Poista diff --git a/src/Greenshot/Languages/language-fr-FR.xml b/src/Greenshot/Languages/language-fr-FR.xml index 4ef502438..53f8e21f6 100644 --- a/src/Greenshot/Languages/language-fr-FR.xml +++ b/src/Greenshot/Languages/language-fr-FR.xml @@ -93,8 +93,6 @@ De plus, nous apprécierions beaucoup que vous preniez la peine de vérifier si Copier Ajouter un compteur Recadrer (C) - Recadrer - Cadrage automatique Outil de sélection (ESC) Couper Supprimer @@ -144,7 +142,7 @@ De plus, nous apprécierions beaucoup que vous preniez la peine de vérifier si Imprimer Rétablir {0} Réinitialiser la taille - Redimensionner (Z) + Redimensionner Maintenir le rapport L / H Hauteur Pourcentage diff --git a/src/Greenshot/Languages/language-fr-QC.xml b/src/Greenshot/Languages/language-fr-QC.xml index cf822fa16..17c0e5537 100644 --- a/src/Greenshot/Languages/language-fr-QC.xml +++ b/src/Greenshot/Languages/language-fr-QC.xml @@ -78,8 +78,6 @@ De plus, nous apprécierions beaucoup que vous preniez la peine de vérifier si Copier le chemin vers le presse-papier Copier Rogner (C) - Rogner - Rognage automatique Outil de sélection (ESC) Couper Supprimer diff --git a/src/Greenshot/Languages/language-he-IL.xml b/src/Greenshot/Languages/language-he-IL.xml index 537f77067..4df909d37 100644 --- a/src/Greenshot/Languages/language-he-IL.xml +++ b/src/Greenshot/Languages/language-he-IL.xml @@ -63,7 +63,6 @@ Details about the GNU General Public License: העתק נתיב אל הלוח העתקה חתוך (C) - חתוך כלי בחירה (ESC) חיתוך מחיקה diff --git a/src/Greenshot/Languages/language-hu-HU.xml b/src/Greenshot/Languages/language-hu-HU.xml index 20035f3c5..0d5a06df9 100644 --- a/src/Greenshot/Languages/language-hu-HU.xml +++ b/src/Greenshot/Languages/language-hu-HU.xml @@ -63,7 +63,6 @@ Kérjük adjon összefoglaló leírást és csatoljon minden olyan információt Másolja a vágólapra Másolás Vágás eszköz (C) - Vágás eszköz Kiválasztó eszköz (ESC) Kivágás Törlés diff --git a/src/Greenshot/Languages/language-id-ID.xml b/src/Greenshot/Languages/language-id-ID.xml index fcb887082..e474cae59 100644 --- a/src/Greenshot/Languages/language-id-ID.xml +++ b/src/Greenshot/Languages/language-id-ID.xml @@ -93,8 +93,6 @@ Juga, kami sangat terbantu apabila anda mengecek laporan lain yang sama dengan k Kopi Buat penomor Potong (C) - Potong - Auto potong Alat seleksi (ESC) Gunting Hapus @@ -144,7 +142,7 @@ Juga, kami sangat terbantu apabila anda mengecek laporan lain yang sama dengan k Cetak Ulang {0} Reset ukuran - Ubah ukuran (Z) + Ubah ukuran Pertahankan aspek rasio Tinggi Persen diff --git a/src/Greenshot/Languages/language-it-IT.xml b/src/Greenshot/Languages/language-it-IT.xml index 89d68c988..53fa10837 100644 --- a/src/Greenshot/Languages/language-it-IT.xml +++ b/src/Greenshot/Languages/language-it-IT.xml @@ -6,7 +6,7 @@ Greenshot è disponibile nel repository GitHub Libreria icone del set icone Fugue di Yusuke Kamiyamane (Creative Commons Attribution 3.0 license) Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom -Greenshot viene fornito SENZA ALCUNA GARANZIA. +Greenshot viene fornito SENZA ALCUNA GARANZIA. Questo software è gratuito", e potete ri-distribuirlo secondo certe condizioni. Dettagli della General Public License GNU: Info su Greenshot @@ -19,7 +19,8 @@ La buona notizia è che puoi aiutarci ad eliminarlo segnalandoci l'errore. Visita la pagina internet qui sotto, crea una nuova segnalazione errore e copia nella descrizione il contenuto preso dall'area di testo. Aggiungi un riepilogo significativo e includi qualsiasi informazione tu consideri possa esserci d'aiuto per risolvere il problema. -Inoltre apprezzeremo molto, se prima di inserire la segnalazione, controllassi se esiste già una segnalazione per questo tipo di errore (puoi usare la ricerca). Grazie :) +Inoltre apprezzeremo molto, se prima di inserire la segnalazione, controllassi se esiste già una segnalazione per questo tipo di errore (puoi usare la ricerca) +Grazie :) Errore Annulla Si è verificato un errore inaspettato durante la scrittura negli Appunti. @@ -81,8 +82,7 @@ Controlla i permessi di accesso per '{0}'. Punto finale Nessuna Punto iniziale - Ritaglia automaticamente - Ritaglio automatico non possibile + Ritaglia Automaticamente Colore di riempimento Raggio sfumatura Grassetto @@ -98,10 +98,6 @@ Controlla i permessi di accesso per '{0}'. Copia percorso negli appunti Copia Ritaglia (C) - Ritaglia - Ritaglia verticalmente - Ritaglia orizzontalmente - Ritaglia automaticamente Strumento di selezione (ESC) Taglia Elimina @@ -133,8 +129,8 @@ Controlla i permessi di accesso per '{0}'. Negativo Corsivo Carica oggetti da file - Fattore ingrandimento - Adatta a dimensioni cattura + Fattore di ingrandimento + Adatta a dimensioni di cattura Offusca (O) Sfuma Modalità offuscamento @@ -179,17 +175,17 @@ Verifica l'accesso in scrittura nel percorso di salvataggio. Il nome file o cartella generato non è valido. Correggi la maschera noem file e riprova. Utente esperto - Crea immagine a 8bit se immagine ha meno di 256 colori + Crea una immagine a 8bit se i colori sono meno di 256 e l'immagine ha > 8 bit Controlla disponibilità aggiornamenti versioni beta - Formato Appunti - Numero var. ${NUM} modello nome file + Formati degli Appunti + Numero per var. ${NUM} nel modello del nome file Sono consapevole di ciò che sto facendo! - Modello piè pagina + Modello piè di pagina Riduci uso della memoria perdendo però in prestazioni (sconsigliato). Esegui ottimizzazioni per uso con desktop remoto Riutilizza se possibile la gestione immagini Evita avviso di salvataggio in chiusura della gestione immagini - Visualizza anteprima finestra menù contestuale (Vista/Windows 7) + Visualizza anteprima finestra nel menù contestuale (Vista e Windows 7) Esportato in: {0} Si è verificato un errore durante l'esportazione in {0}: Guida in linea Greenshot @@ -219,7 +215,7 @@ Correggi la maschera noem file e riprova. Esegui Greenshot all'avvio del sistema Cattura Cattura puntatore mouse - Usa modalità cattura via finestra interattiva + Usa la modalità di cattura via finestra interattiva Intervallo controllo aggiornamento in giorni (0=nessun controllo) Imposta Ogni volta che una immagine viene salvata copia percorso file negli Appunti @@ -269,7 +265,7 @@ impostazioni) Stampante Opzioni di stampa Impostazioni qualità - Riduci i colori ad un massimo di 256 + Riduci i colori a un massimo di 256 Registra scorciatoie tastiera Visualizza torcia elettrica Visualizza notifiche @@ -298,8 +294,8 @@ In alternativa alle scorciatoie di tastiera, tutte le funzioni di Greenshot sono Usa colori personalizzati Mantieni trasparenza - Automatica - Usa colori predefiniti + Automaticamente + Usa i colori predefiniti Come visualizzata @@ -322,12 +318,12 @@ In alternativa alle scorciatoie di tastiera, tutte le funzioni di Greenshot sono Aggiungi conteggio Aggiungi nuvoletta - Ridimensiona (Z) + Ridimensiona Impostazioni ridimensionamento Mantieni rapporto dimensioni Larghezza Altezza - Dimensione icona (pixel) + Dimensione icona diff --git a/src/Greenshot/Languages/language-ja-JP.xml b/src/Greenshot/Languages/language-ja-JP.xml index 36f5231f2..c894b9573 100644 --- a/src/Greenshot/Languages/language-ja-JP.xml +++ b/src/Greenshot/Languages/language-ja-JP.xml @@ -92,8 +92,6 @@ Greenshot には一切の保障がありません。GNU General Public License コピー カウンターを挿入する (I) 切り抜き (C) - 切り抜き - 自動切り抜き 選択ツール (ESC) 切り取り 削除 @@ -143,7 +141,7 @@ Greenshot には一切の保障がありません。GNU General Public License 印刷 やり直し{0} サイズをリセット - リサイズ (Z) + リサイズ 縦横比を維持する 高さ パーセント diff --git a/src/Greenshot/Languages/language-kab-DZ.xml b/src/Greenshot/Languages/language-kab-DZ.xml index 8a690952c..31930d2db 100644 --- a/src/Greenshot/Languages/language-kab-DZ.xml +++ b/src/Greenshot/Languages/language-kab-DZ.xml @@ -93,8 +93,6 @@ Rnu ɣur-s, nḥemmel aṭas ma yella tesneqdeḍ aneqqis igebren ugur-agi. (Tze Nɣel Rnu Amesmiḍan Seggem (C) - Seggem - Aseggem awurman Afecku n ufran (ESC) Gzem Kkes @@ -144,7 +142,7 @@ Rnu ɣur-s, nḥemmel aṭas ma yella tesneqdeḍ aneqqis igebren ugur-agi. (Tze Siggez Err-d {0} Wennez teɣzi - Snifel tahri/teɣzi (Z) + Snifel tahri/teɣzi Eǧǧ afmiḍi Teɣ / Teh Awrir Afmiḍi diff --git a/src/Greenshot/Languages/language-ko-KR.xml b/src/Greenshot/Languages/language-ko-KR.xml index 144188ad8..a332cea33 100644 --- a/src/Greenshot/Languages/language-ko-KR.xml +++ b/src/Greenshot/Languages/language-ko-KR.xml @@ -92,8 +92,6 @@ Also, we would highly appreciate if you checked whether a tracker item already e 경로를 클립보드로 복사 복사 잘라내기 (C) - 잘라내기 - 자동 잘라내기 선택도구 (ESC) 잘라내기 삭제 @@ -304,7 +302,7 @@ ${hostname} PC명 카운터 더하기 (I) 설명선 더하기(S) - 크기조정 (Z) + 크기조정 크기조정 설정 종횡비 유지 너비 diff --git a/src/Greenshot/Languages/language-lt-LT.xml b/src/Greenshot/Languages/language-lt-LT.xml index 9b1089811..e84388901 100644 --- a/src/Greenshot/Languages/language-lt-LT.xml +++ b/src/Greenshot/Languages/language-lt-LT.xml @@ -61,7 +61,6 @@ Dėkojame už pagalbą :) Kopijuoti pilną failo vardą Коpijuoti Iškirpti (C) - Iškirpti Objektų pasirinkimas (ESC) Apkirpti Naikinti diff --git a/src/Greenshot/Languages/language-lv-LV.xml b/src/Greenshot/Languages/language-lv-LV.xml index dea057944..c74e41326 100644 --- a/src/Greenshot/Languages/language-lv-LV.xml +++ b/src/Greenshot/Languages/language-lv-LV.xml @@ -92,8 +92,6 @@ Mēs būtu Tev pateicīgi, ja Tu vispirms pārbaudītu, vai kāds cits jau nav z Ievietot ceļu starpliktuvē Kopēt Apcirst (C) - Apcirst - Automātiski apcirst Atlases rīks (ESC) Izgriezt Izdzēst @@ -306,7 +304,7 @@ Arī bez karstiem taustiņiem visas darbības iespējams veikt izmantojot „Gre Pievienot skaitli (I) Pievienot teksta norādi (S) - Mainīt izmēru (Z) + Mainīt izmēru Izmēra maiņas iestatījumi Saglabāt izmēru attiecības Platums diff --git a/src/Greenshot/Languages/language-nl-NL.xml b/src/Greenshot/Languages/language-nl-NL.xml index d640b0a42..52e12a895 100644 --- a/src/Greenshot/Languages/language-nl-NL.xml +++ b/src/Greenshot/Languages/language-nl-NL.xml @@ -93,8 +93,6 @@ Controleer ook even of dit probleem mogelijk al gemeld is! Gebruik de zoekfuncti Locatie naar klembord kopiëren Kopiëren Bijsnijden (C) - Bijsnijden - Automatisch bijsnijden Selectiegereedschap (ESC) Knippen Verwijderen @@ -307,7 +305,7 @@ Alle Greenshot functies werken ook zonder sneltoetsen via het context menu.Teller toevoegen (I) Tekstballon toevoegen (S) - Grootte (Z) + Grootte Vergrotingsinstellingen Verhouding behouden Breedte diff --git a/src/Greenshot/Languages/language-nn-NO.xml b/src/Greenshot/Languages/language-nn-NO.xml index edd8f31e7..003b11b9a 100644 --- a/src/Greenshot/Languages/language-nn-NO.xml +++ b/src/Greenshot/Languages/language-nn-NO.xml @@ -80,8 +80,6 @@ Me sett òg pris på om du ved hjelp av søkefunksjonen på sida kan sjekke om d Kopier filstigen til utklyppstavla Kopier Skjer bildet [C] - Skjer bildet - Auto-skjer Peikarverktøy [Esc] Klypp ut Slett diff --git a/src/Greenshot/Languages/language-pl-PL.xml b/src/Greenshot/Languages/language-pl-PL.xml index 7f908dba8..54e2fd993 100644 --- a/src/Greenshot/Languages/language-pl-PL.xml +++ b/src/Greenshot/Languages/language-pl-PL.xml @@ -93,8 +93,6 @@ Będziemy wdzięczni, jeśli najpierw sprawdzisz, czy takie zdarzenie nie zosta Kopiuj ścieżkę do schowka Kopiuj Przytnij (C) - Przytnij (C) - Przytnij automatycznie Narzędzie wyboru (ESC) Wytnij Usuń diff --git a/src/Greenshot/Languages/language-pt-BR.xml b/src/Greenshot/Languages/language-pt-BR.xml index e53603ccc..8bfd938ed 100644 --- a/src/Greenshot/Languages/language-pt-BR.xml +++ b/src/Greenshot/Languages/language-pt-BR.xml @@ -89,7 +89,6 @@ Copiar o caminho da pasta atual do arquivo para a Área de transferência Copiar Cortar (C) - Cortar Ferramenta de Seleção (ESC) Cortar Apagar diff --git a/src/Greenshot/Languages/language-pt-PT.xml b/src/Greenshot/Languages/language-pt-PT.xml index 4da21743d..dabc42762 100644 --- a/src/Greenshot/Languages/language-pt-PT.xml +++ b/src/Greenshot/Languages/language-pt-PT.xml @@ -92,8 +92,6 @@ Também apreciaremos muito se puder verificar se não existe já um relatório d Copiar atalho para a Área de transferência Copiar Recortar (C) - Recortar - Recortar auto Ferramenta de Selecção (ESC) Cortar Eliminar @@ -305,7 +303,7 @@ Todas as funcionalidades do Greenshot funcionam directamente através do menu de Adicionar contador (I) Adicionar balão de texto (S) - Redimensionar (Z) + Redimensionar Definições de Redimensionamento Manter proporções Largura diff --git a/src/Greenshot/Languages/language-ro-RO.xml b/src/Greenshot/Languages/language-ro-RO.xml index 02fe86c3f..7e67ecb52 100644 --- a/src/Greenshot/Languages/language-ro-RO.xml +++ b/src/Greenshot/Languages/language-ro-RO.xml @@ -471,9 +471,6 @@ timpul curent, ex. 11_58_32 (plus extensia fișierului definită în setări) Taie (C) - - Taie - Deschide imaginea din clipboard diff --git a/src/Greenshot/Languages/language-ru-RU.xml b/src/Greenshot/Languages/language-ru-RU.xml index f9973f349..42d85df80 100644 --- a/src/Greenshot/Languages/language-ru-RU.xml +++ b/src/Greenshot/Languages/language-ru-RU.xml @@ -93,8 +93,6 @@ Greenshot поставляется БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ. Копировать путь в буфер обмена Копировать Обрезка (C) - Обрезка - Автообрезка Инструмент «Выделение» (ESC) Вырезать Удалить diff --git a/src/Greenshot/Languages/language-sk-SK.xml b/src/Greenshot/Languages/language-sk-SK.xml index 01c68e9ed..f47c2bd5b 100644 --- a/src/Greenshot/Languages/language-sk-SK.xml +++ b/src/Greenshot/Languages/language-sk-SK.xml @@ -80,8 +80,6 @@ Tiež by sme velmi ocenili, keby ste najskôr skontrolovali, či už neexistuje Kopírovať cestu do schránky Kopírovať Orezať (C) - Orezať - Automatické orezanie Nástroj pre výber (ESC) Vystrihnúť Zmazať diff --git a/src/Greenshot/Languages/language-sl-SI.xml b/src/Greenshot/Languages/language-sl-SI.xml index ac102397a..fd0dc2bc4 100644 --- a/src/Greenshot/Languages/language-sl-SI.xml +++ b/src/Greenshot/Languages/language-sl-SI.xml @@ -78,8 +78,6 @@ Pred objavo preverite tudi ali je napaka že prijavlja s strani kakšnega drugeg Kopiraj pot na odložišče Kopiraj Obreži (C) - Obreži - Samodejno obreži Orodje za izbor (ESC) Kopiraj Briši diff --git a/src/Greenshot/Languages/language-sr-RS.xml b/src/Greenshot/Languages/language-sr-RS.xml index 9c669dba8..6b624dd9c 100644 --- a/src/Greenshot/Languages/language-sr-RS.xml +++ b/src/Greenshot/Languages/language-sr-RS.xml @@ -78,8 +78,6 @@ Умножи путању Умножи Опсеци (C) - Опсеци - Аутоматско опсецање Алатка за одабир (ESC) Исеци Обриши diff --git a/src/Greenshot/Languages/language-sv-SE.xml b/src/Greenshot/Languages/language-sv-SE.xml index c5b1517e2..3fe2df709 100644 --- a/src/Greenshot/Languages/language-sv-SE.xml +++ b/src/Greenshot/Languages/language-sv-SE.xml @@ -93,8 +93,6 @@ Innan du skickar uppskattar vi verkligen om du kontrollerar om felet redan blivi Kopiera sökväg till urklipp Kopiera Beskär (C) - Beskär - Autobeskärning Markeringsverktyg (ESC) Klipp ut Radera @@ -305,7 +303,7 @@ Alla Greenshots funktioner fungerar fortfarande från snabbmenyn i aktivitetsfä Lägg till räknare Lägg till pratbubbla - Anpassa storlek (Z) + Anpassa storlek Storleksinställningar Behåll bildförhållande Bredd diff --git a/src/Greenshot/Languages/language-tr-TR.xml b/src/Greenshot/Languages/language-tr-TR.xml index 56011063c..b5a43f083 100644 --- a/src/Greenshot/Languages/language-tr-TR.xml +++ b/src/Greenshot/Languages/language-tr-TR.xml @@ -78,8 +78,6 @@ Ayrıca bu hata için bir izleyici kaydının açılmış olup olmadığını da Yolu panoya kopyala Kopyala Kırp (C) - Kırp - Otomatik kırpma Seçim Aracı (ESC) Kes Sil diff --git a/src/Greenshot/Languages/language-uk-UA.xml b/src/Greenshot/Languages/language-uk-UA.xml index 057927676..850a5ea7f 100644 --- a/src/Greenshot/Languages/language-uk-UA.xml +++ b/src/Greenshot/Languages/language-uk-UA.xml @@ -92,8 +92,6 @@ Greenshot постачається АБСОЛЮТНО БЕЗ ГАРАНТІЇ. Копіювати шлях у буфер обміну Копіювати Обрізати (С) - Обрізати - Автоматичне обрізання Інструмент вибору (Esc) Вирізати Видалити @@ -306,7 +304,7 @@ ${hostname} назва комп’ютера Додати лічильник (Ш) Додати словесну бульбашку (І) - Змінити розмір (Z) + Змінити розмір Параметри зміни розміру Зберігати пропорції Ширина diff --git a/src/Greenshot/Languages/language-vi-VN.xml b/src/Greenshot/Languages/language-vi-VN.xml index 6ddfc7f9d..a4b1a73c2 100644 --- a/src/Greenshot/Languages/language-vi-VN.xml +++ b/src/Greenshot/Languages/language-vi-VN.xml @@ -58,7 +58,6 @@ Chép đuờng dẫn tới clipboard. Chép Cắt (C) - Cắt Công cụ chọn (ESC) Cắt Xóa diff --git a/src/Greenshot/Languages/language-zh-CN.xml b/src/Greenshot/Languages/language-zh-CN.xml index e69489c8c..7325fff33 100644 --- a/src/Greenshot/Languages/language-zh-CN.xml +++ b/src/Greenshot/Languages/language-zh-CN.xml @@ -84,8 +84,6 @@ 复制路径到剪贴板 复制 裁剪 (C) - 裁剪 - 自动裁剪 选择工具 (ESC) 剪切 刪除物件 diff --git a/src/Greenshot/Languages/language-zh-TW.xml b/src/Greenshot/Languages/language-zh-TW.xml index 63f8fce2f..da3881d2c 100644 --- a/src/Greenshot/Languages/language-zh-TW.xml +++ b/src/Greenshot/Languages/language-zh-TW.xml @@ -93,8 +93,6 @@ Greenshot 不對這個程式做任何擔保。這個程式是自由軟體,您 複製路徑到剪貼簿 複製 裁剪 (C) - 裁剪 - 自動裁剪 選取工具 (ESC) 剪下 刪除 @@ -307,7 +305,7 @@ Greenshot 所有功能仍然可以直接從通知區圖示的內容功能表動 加入計數器 (I) 加入對話框 (S) - 縮放 (Z) + 縮放 縮放設定 維持長寬比 長度