diff --git a/.github/workflows/purge-cloudflare-cache.yml b/.github/workflows/purge-cloudflare-cache.yml new file mode 100644 index 000000000..a2d288faf --- /dev/null +++ b/.github/workflows/purge-cloudflare-cache.yml @@ -0,0 +1,14 @@ +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 new file mode 100644 index 000000000..9a92223ed --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,163 @@ +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 new file mode 100644 index 000000000..ba45b70d1 --- /dev/null +++ b/.github/workflows/update-gh-pages.yml @@ -0,0 +1,18 @@ +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 3afd2d7ab..7e96d5501 100644 --- a/.gitignore +++ b/.gitignore @@ -214,4 +214,6 @@ ModelManifest.xml *.credentials.cs # Rider files -.idea \ No newline at end of file +.idea + +/installer/Greenshot-INSTALLER-*.exe diff --git a/README.md b/README.md index 1f47983e3..a3e9696c7 100644 --- a/README.md +++ b/README.md @@ -21,4 +21,25 @@ Being easy to understand and configurable, Greenshot is an efficient tool for pr About this repository --------------------- -This repository is for Greenshot 1.3, currently in development, but is the next planned release +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. + diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..324758e42 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,5 @@ +# 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 deleted file mode 100644 index 558d40760..000000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,106 +0,0 @@ -# .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 new file mode 100644 index 000000000..7645259c5 --- /dev/null +++ b/build-and-deploy.ps1 @@ -0,0 +1,149 @@ +# 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 1215789b6..bdb64e2c4 100644 --- a/installer/additional_files/readme.txt +++ b/installer/additional_files/readme.txt @@ -7,7 +7,29 @@ CHANGE LOG: All details to our tickets can be found here: https://greenshot.atlassian.net -# Release notes for Greenshot 1.3 +# 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. 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. @@ -646,3 +668,5 @@ 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 23788205e..fa358ee7b 100644 --- a/installer/innosetup/setup.iss +++ b/installer/innosetup/setup.iss @@ -7,6 +7,7 @@ #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 @@ -82,33 +83,34 @@ 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\*.dll; DestDir: {app}\Plugins\Office; Components: plugins\office; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#PluginDir}\Greenshot.Plugin.Office\Greenshot.Plugin.Office.dll; DestDir: {app}\Plugins\Office; Components: plugins\office; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; ;JIRA Plugin -Source: {#PluginDir}\Greenshot.Plugin.Jira\*.dll; DestDir: {app}\Plugins\Jira; Components: plugins\jira; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#PluginDir}\Greenshot.Plugin.Jira\*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\*.dll; DestDir: {app}\Plugins\Imgur; Components: plugins\imgur; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#PluginDir}\Greenshot.Plugin.Imgur\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\*.dll; DestDir: {app}\Plugins\Box; Components: plugins\box; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#PluginDir}\Greenshot.Plugin.Box\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\*.dll; DestDir: {app}\Plugins\DropBox; Components: plugins\dropbox; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#PluginDir}\Greenshot.Plugin.DropBox\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\*.dll; DestDir: {app}\Plugins\Flickr; Components: plugins\flickr; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#PluginDir}\Greenshot.Plugin.Flickr\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\*.dll; DestDir: {app}\Plugins\Photobucket; Components: plugins\photobucket; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#PluginDir}\Greenshot.Plugin.Photobucket\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\*.dll; DestDir: {app}\Plugins\Confluence; Components: plugins\confluence; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#PluginDir}\Greenshot.Plugin.Confluence\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\*.dll; DestDir: {app}\Plugins\ExternalCommand; Components: plugins\externalcommand; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#PluginDir}\Greenshot.Plugin.ExternalCommand\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\*.dll; DestDir: {app}\Plugins\Win10; Components: plugins\win10; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +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; [Setup] ; changes associations is used when the installer installs new extensions, it clears the explorer icon cache @@ -125,21 +127,27 @@ AppVersion={#Version} ArchitecturesInstallIn64BitMode=x64 Compression=lzma2/ultra64 SolidCompression=yes -DefaultDirName={code:DefDirRoot}\{#ExeName} +DefaultDirName={autopf}\{#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 -; 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 +#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 UninstallDisplayIcon={app}\{#ExeName}.exe Uninstallable=true VersionInfoCompany={#ExeName} @@ -151,6 +159,7 @@ 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; @@ -169,22 +178,16 @@ 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 -; 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 +Root: HKA; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: string; ValueName: {#ExeName}; ValueData: """{app}\{#ExeName}.exe"""; Flags: uninsdeletevalue noerror; Tasks: startup ; Register our own filetype for all users -; 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 +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 [Icons] Name: {group}\{#ExeName}; Filename: {app}\{#ExeName}.exe; WorkingDir: {app}; AppUserModelID: "{#ExeName}" @@ -268,6 +271,7 @@ 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 @@ -280,6 +284,7 @@ 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} @@ -481,6 +486,7 @@ 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 @@ -530,23 +536,8 @@ 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); @@ -744,6 +735,19 @@ 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 new file mode 100644 index 000000000..554c2f634 --- /dev/null +++ b/nuget.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props index b6533ddd7..131878749 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,13 +1,13 @@  - Copyright © Greenshot 2004-2021 + Copyright © Greenshot 2004-2022 Greenshot https://getgreenshot.org/favicon.ico https://github.com/greenshot/greenshot git https://github.com/greenshot/greenshot GPL-3.0-only - 9 + latest 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 f2ce406a1..caa6a3db3 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -1,13 +1,65 @@ + + + + + + + + + + (); + 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(); + ]]> + + + - $(PkgMSBuildTasks)\tools\ + $(NuGetPackageRoot)msbuildtasks/1.5.0.235/tools/ - - + + - - + + + + + + + + + + + + - \ No newline at end of file + + + + + diff --git a/src/Greenshot.Base/Core/ClipboardHelper.cs b/src/Greenshot.Base/Core/ClipboardHelper.cs index 7a30a1143..bfe13c121 100644 --- a/src/Greenshot.Base/Core/ClipboardHelper.cs +++ b/src/Greenshot.Base/Core/ClipboardHelper.cs @@ -386,6 +386,7 @@ EndSelection:<<<<<<<4 /// IEnumerable{(MemoryStream,string)} private static IEnumerable<(MemoryStream stream,string filename)> IterateClipboardContent(IDataObject dataObject) { + if (dataObject == null) yield break; var fileDescriptors = AvailableFileDescriptors(dataObject); if (fileDescriptors == null) yield break; @@ -499,6 +500,10 @@ EndSelection:<<<<<<<4 public static Image GetImage() { IDataObject clipboardData = GetDataObject(); + if (clipboardData == null) + { + return null; + } // Return the first image foreach (var clipboardImage in GetImages(clipboardData)) { @@ -520,7 +525,7 @@ EndSelection:<<<<<<<4 Bitmap singleImage = GetImage(dataObject); if (singleImage != null) { - Log.InfoFormat($"Got {singleImage.GetType()} from clipboard with size {singleImage.Size}"); + Log.Info($"Got {singleImage.GetType()} from clipboard with size {singleImage.Size}"); yield return singleImage; yield break; } diff --git a/src/Greenshot.Base/Core/CoreConfiguration.cs b/src/Greenshot.Base/Core/CoreConfiguration.cs index 580af8918..3e085bc9b 100644 --- a/src/Greenshot.Base/Core/CoreConfiguration.cs +++ b/src/Greenshot.Base/Core/CoreConfiguration.cs @@ -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")] + [IniProperty("ClipboardHotkey", Description = "Hotkey for opening the clipboard contents into the editor", ExcludeIfNull = true)] public string ClipboardHotkey { get; set; } [IniProperty("IsFirstLaunch", Description = "Is this the first time launch?", DefaultValue = "true")] @@ -388,89 +388,64 @@ 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) - { - switch (property) + public override object GetDefault(string property) => + property switch { - 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; - } - + 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 + }; + /// /// This method will be called before converting the property, making to possible to correct a certain value /// Can be used when migration is needed @@ -540,8 +515,9 @@ namespace Greenshot.Base.Core OutputFileAutoReduceColors = false; } + bool isUpgradeFrom12 = LastSaveWithVersion?.StartsWith("1.2") ?? false; // Fix for excessive feed checking - if (UpdateCheckInterval != 0 && UpdateCheckInterval <= 7 && LastSaveWithVersion.StartsWith("1.2")) + if (UpdateCheckInterval != 0 && UpdateCheckInterval <= 7 && isUpgradeFrom12) { UpdateCheckInterval = 14; } diff --git a/src/Greenshot.Base/Core/PluginUtils.cs b/src/Greenshot.Base/Core/PluginUtils.cs index d41e545a5..76f08422b 100644 --- a/src/Greenshot.Base/Core/PluginUtils.cs +++ b/src/Greenshot.Base/Core/PluginUtils.cs @@ -24,6 +24,7 @@ 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; @@ -39,9 +40,9 @@ namespace Greenshot.Base.Core { private static readonly ILog Log = LogManager.GetLogger(typeof(PluginUtils)); private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); - private const string PathKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\"; private static readonly IDictionary ExeIconCache = new Dictionary(); - + private const string PathKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\"; + static PluginUtils() { CoreConfig.PropertyChanged += OnIconSizeChanged; @@ -84,7 +85,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); } } diff --git a/src/Greenshot.Base/Core/WindowDetails.cs b/src/Greenshot.Base/Core/WindowDetails.cs index 281dde6f4..9cdf5f370 100644 --- a/src/Greenshot.Base/Core/WindowDetails.cs +++ b/src/Greenshot.Base/Core/WindowDetails.cs @@ -42,8 +42,6 @@ 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[] { @@ -89,13 +87,7 @@ 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" @@ -108,20 +100,6 @@ 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 @@ -226,12 +204,6 @@ 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); @@ -467,11 +439,6 @@ namespace Greenshot.Base.Core { get { - if (IsMetroApp) - { - return !Visible; - } - return User32Api.IsIconic(Handle) || Location.X <= -32000; } set @@ -494,22 +461,6 @@ namespace Greenshot.Base.Core { get { - if (IsApp) - { - if (Visible) - { - foreach (var displayInfo in DisplayInfo.AllDisplayInfos) - { - if (WindowRectangle.Equals(displayInfo.Bounds)) - { - return true; - } - } - } - - return false; - } - return User32Api.IsZoomed(Handle); } set @@ -546,50 +497,6 @@ namespace Greenshot.Base.Core return false; } - if (IsApp) - { - var windowRectangle = WindowRectangle; - - foreach (var displayInfo in DisplayInfo.AllDisplayInfos) - { - if (!displayInfo.Bounds.Contains(windowRectangle)) continue; - if (windowRectangle.Equals(displayInfo.Bounds)) - { - // Fullscreen, it's "visible" when AppVisibilityOnMonitor says yes - // Although it might be the other App, this is not "very" important - NativeRect rect = displayInfo.Bounds; - IntPtr monitor = User32Api.MonitorFromRect(ref rect, MonitorFrom.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 User32Api.IsWindowVisible(Handle); } } @@ -643,69 +550,56 @@ namespace Greenshot.Base.Core { // Try to return a cached value long now = DateTime.Now.Ticks; - if (_previousWindowRectangle.IsEmpty || !_frozen) - { - if (!_previousWindowRectangle.IsEmpty && now - _lastWindowRectangleRetrieveTime <= CacheTime) - { - return _previousWindowRectangle; - } - NativeRect windowRect = new(); - if (DwmApi.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 (!_previousWindowRectangle.IsEmpty && _frozen) return _previousWindowRectangle; - if (gotFrameBounds && WindowsVersion.IsWindows10OrLater && !Maximised) + if (!_previousWindowRectangle.IsEmpty && now - _lastWindowRectangleRetrieveTime <= CacheTime) + { + return _previousWindowRectangle; + } + NativeRect windowRect = new(); + if (DwmApi.IsDwmEnabled) + { + bool gotFrameBounds = GetExtendedFrameBounds(out windowRect); + if (IsWin10App) + { + // Pre-Cache for maximized call, this is only on Windows 8 apps (full screen) + if (gotFrameBounds) { - // 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 (windowRect.IsEmpty) + if (gotFrameBounds && WindowsVersion.IsWindows10OrLater && !Maximised) { - if (!GetWindowRect(out windowRect)) - { - Win32Error error = Win32.GetLastErrorCode(); - Log.WarnFormat("Couldn't retrieve the windows rectangle: {0}", Win32.GetMessage(error)); - } + // 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; } - - // Correction for maximized windows, only if it's not an app - if (!HasParent && !IsApp && Maximised) - { - // Only if the border size can be retrieved - if (GetBorderSize(out var size)) - { - windowRect = new NativeRect(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 NativeRect anymore - if (windowRect.IsEmpty) - { - return _previousWindowRectangle; - } - - _previousWindowRectangle = windowRect; - return windowRect; } - return _previousWindowRectangle; + if (windowRect.IsEmpty) + { + if (!GetWindowRect(out windowRect)) + { + Win32Error error = Win32.GetLastErrorCode(); + Log.WarnFormat("Couldn't retrieve the windows rectangle: {0}", Win32.GetMessage(error)); + } + } + + _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; + } } @@ -928,7 +822,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 (!IsMetroApp && WindowCapture.IsGdiAllowed(thisWindowProcess)) + if (WindowCapture.IsGdiAllowed(thisWindowProcess)) { // we return null which causes the capturing code to try another method. return null; @@ -973,11 +867,8 @@ namespace Greenshot.Base.Core tempForm.BackColor = Color.Black; // Make sure everything is visible tempForm.Refresh(); - if (!IsMetroApp) - { - // Make sure the application window is active, so the colors & buttons are right - ToForeground(); - } + // Make sure the application window is active, so the colors & buttons are right + ToForeground(); // Make sure all changes are processed and visible Application.DoEvents(); @@ -1013,11 +904,8 @@ namespace Greenshot.Base.Core // Make sure everything is visible tempForm.Refresh(); - if (!IsMetroApp) - { - // Make sure the application window is active, so the colors & buttons are right - ToForeground(); - } + // Make sure the application window is active, so the colors & buttons are right + ToForeground(); // Make sure all changes are processed and visible Application.DoEvents(); @@ -1154,6 +1042,13 @@ 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 /// @@ -1164,6 +1059,10 @@ namespace Greenshot.Base.Core var result = DwmApi.DwmGetWindowAttribute(Handle, DwmWindowAttributes.ExtendedFrameBounds, out NativeRect rect, Marshal.SizeOf(typeof(NativeRect))); if (result.Succeeded()) { + if (IsHidden(rect)) + { + rect = NativeRect.Empty; + } rectangle = rect; return true; } @@ -1196,7 +1095,14 @@ namespace Greenshot.Base.Core var windowInfo = new WindowInfo(); // Get the Window Info for this window bool result = User32Api.GetWindowInfo(Handle, ref windowInfo); - rectangle = result ? windowInfo.Bounds : NativeRect.Empty; + if (IsHidden(windowInfo.Bounds)) + { + rectangle = NativeRect.Empty; + } + else + { + rectangle = result ? windowInfo.Bounds : NativeRect.Empty; + } return result; } @@ -1577,7 +1483,7 @@ namespace Greenshot.Base.Core // Skip everything which is not rendered "normally", trying to fix BUG-2017 var exWindowStyle = window.ExtendedWindowStyle; - if (!window.IsApp && !window.IsWin10App && (exWindowStyle & ExtendedWindowStyleFlags.WS_EX_NOREDIRECTIONBITMAP) != 0) + if (!window.IsWin10App && (exWindowStyle & ExtendedWindowStyleFlags.WS_EX_NOREDIRECTIONBITMAP) != 0) { return false; } @@ -1592,13 +1498,6 @@ namespace Greenshot.Base.Core public static IEnumerable GetVisibleWindows() { var screenBounds = DisplayInfo.ScreenBounds; - foreach (var window in GetAppWindows()) - { - if (IsVisible(window, screenBounds)) - { - yield return window; - } - } foreach (var window in GetAllWindows()) { @@ -1609,38 +1508,6 @@ 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 = User32Api.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 = User32Api.FindWindow(GutterClass, null); - if (gutterHandle != IntPtr.Zero) - { - yield return new WindowDetails(gutterHandle); - } - } - - nextHandle = User32Api.FindWindowEx(IntPtr.Zero, nextHandle, AppWindowClass, null); - } - } - /// /// Check if the window is a top level /// @@ -1671,7 +1538,7 @@ namespace Greenshot.Base.Core } // Skip everything which is not rendered "normally", trying to fix BUG-2017 - if (!window.IsApp && !window.IsWin10App && (exWindowStyle & ExtendedWindowStyleFlags.WS_EX_NOREDIRECTIONBITMAP) != 0) + if (!window.IsWin10App && (exWindowStyle & ExtendedWindowStyleFlags.WS_EX_NOREDIRECTIONBITMAP) != 0) { return false; } @@ -1707,14 +1574,6 @@ 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)) @@ -1790,27 +1649,6 @@ namespace Greenshot.Base.Core } } - /// - /// Get the AppLauncher - /// - /// - public static WindowDetails GetAppLauncher() - { - // Only if Windows 8 (or higher) - if (AppVisibility == null) - { - return null; - } - - IntPtr appLauncher = User32Api.FindWindow(ApplauncherClass, null); - if (appLauncher != IntPtr.Zero) - { - return new WindowDetails(appLauncher); - } - - return null; - } - /// /// Return true if the metro-app-launcher is visible /// @@ -1842,7 +1680,6 @@ 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($"IsCloaked: {IsCloaked}"); diff --git a/src/Greenshot.Base/Greenshot.Base.csproj b/src/Greenshot.Base/Greenshot.Base.csproj index 169bd9395..550682cd0 100644 --- a/src/Greenshot.Base/Greenshot.Base.csproj +++ b/src/Greenshot.Base/Greenshot.Base.csproj @@ -3,18 +3,22 @@ true + + none + false + - - - - - - - - - - + + + + + + + + + + diff --git a/src/Greenshot.Base/Interfaces/Plugin/AssemblyPluginIdentifierAttribute.cs b/src/Greenshot.Base/Interfaces/Plugin/AssemblyPluginIdentifierAttribute.cs new file mode 100644 index 000000000..2be14c7a8 --- /dev/null +++ b/src/Greenshot.Base/Interfaces/Plugin/AssemblyPluginIdentifierAttribute.cs @@ -0,0 +1,46 @@ +/* + * 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.Editor/Drawing/Adorners/ResizeAdorner.cs b/src/Greenshot.Editor/Drawing/Adorners/ResizeAdorner.cs index 523684232..263d5a31e 100644 --- a/src/Greenshot.Editor/Drawing/Adorners/ResizeAdorner.cs +++ b/src/Greenshot.Editor/Drawing/Adorners/ResizeAdorner.cs @@ -100,8 +100,10 @@ namespace Greenshot.Editor.Drawing.Adorners // reset "workbench" rectangle to current bounds _boundsAfterResize = _boundsBeforeResize; + var scaleOptions = (Owner as IHaveScaleOptions)?.GetScaleOptions(); + // calculate scaled rectangle - _boundsAfterResize = ScaleHelper.Scale(_boundsAfterResize, Position, new PointF(mouseEventArgs.X, mouseEventArgs.Y), ScaleHelper.GetScaleOptions()); + _boundsAfterResize = ScaleHelper.Scale(_boundsAfterResize, Position, new NativePointFloat(mouseEventArgs.X, mouseEventArgs.Y), scaleOptions); // apply scaled bounds to this DrawableContainer Owner.ApplyBounds(_boundsAfterResize); diff --git a/src/Greenshot.Editor/Drawing/CropContainer.cs b/src/Greenshot.Editor/Drawing/CropContainer.cs index 2aee4252f..eb2f7179e 100644 --- a/src/Greenshot.Editor/Drawing/CropContainer.cs +++ b/src/Greenshot.Editor/Drawing/CropContainer.cs @@ -247,10 +247,11 @@ namespace Greenshot.Editor.Drawing _boundsAfterResize = new NativeRectFloat( _boundsBeforeResize.Left, _boundsBeforeResize.Top, x - _boundsAfterResize.Left, y - _boundsAfterResize.Top); + + _boundsAfterResize = ScaleHelper.Scale(_boundsAfterResize, x, y, GetAngleRoundProcessor()); break; } } - _boundsAfterResize = ScaleHelper.Scale(_boundsBeforeResize, x, y, GetAngleRoundProcessor()); // apply scaled bounds to this DrawableContainer ApplyBounds(_boundsAfterResize); diff --git a/src/Greenshot.Editor/Drawing/DrawableContainer.cs b/src/Greenshot.Editor/Drawing/DrawableContainer.cs index be4638164..cac23dc3a 100644 --- a/src/Greenshot.Editor/Drawing/DrawableContainer.cs +++ b/src/Greenshot.Editor/Drawing/DrawableContainer.cs @@ -533,7 +533,8 @@ namespace Greenshot.Editor.Drawing // 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()); + var scaleOptions = (this as IHaveScaleOptions)?.GetScaleOptions(); + _boundsAfterResize = ScaleHelper.Scale(_boundsAfterResize, x, y, GetAngleRoundProcessor(), scaleOptions); // apply scaled bounds to this DrawableContainer ApplyBounds(_boundsAfterResize); @@ -666,9 +667,9 @@ namespace Greenshot.Editor.Drawing Height = points[1].Y - points[0].Y; } - protected virtual ScaleHelper.IDoubleProcessor GetAngleRoundProcessor() + protected virtual IDoubleProcessor GetAngleRoundProcessor() { - return ScaleHelper.ShapeAngleRoundBehavior.INSTANCE; + return ShapeAngleRoundBehavior.INSTANCE; } public virtual bool HasContextMenu => true; diff --git a/src/Greenshot.Editor/Drawing/FreehandContainer.cs b/src/Greenshot.Editor/Drawing/FreehandContainer.cs index b8a66be4e..9c66e1a8e 100644 --- a/src/Greenshot.Editor/Drawing/FreehandContainer.cs +++ b/src/Greenshot.Editor/Drawing/FreehandContainer.cs @@ -1,323 +1,325 @@ -/* - * 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 NativeRect myBounds = NativeRect.Empty; - private NativePoint lastMouse = NativePoint.Empty; - private readonly 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 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; + } + } } \ No newline at end of file diff --git a/src/Greenshot.Editor/Drawing/LineContainer.cs b/src/Greenshot.Editor/Drawing/LineContainer.cs index ef4a68707..f48dddec7 100644 --- a/src/Greenshot.Editor/Drawing/LineContainer.cs +++ b/src/Greenshot.Editor/Drawing/LineContainer.cs @@ -115,9 +115,9 @@ namespace Greenshot.Editor.Drawing return false; } - protected override ScaleHelper.IDoubleProcessor GetAngleRoundProcessor() + protected override IDoubleProcessor GetAngleRoundProcessor() { - return ScaleHelper.LineAngleRoundBehavior.INSTANCE; + return LineAngleRoundBehavior.INSTANCE; } } } \ No newline at end of file diff --git a/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs b/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs index 22dd13379..e1f37544b 100644 --- a/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs +++ b/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs @@ -39,10 +39,10 @@ namespace Greenshot.Editor.Drawing [Serializable] public class SpeechbubbleContainer : TextContainer { - private NativePoint _initialGripperPoint; + private Point _initialGripperPoint; // Only used for serializing the TargetGripper location - private NativePoint _storedTargetGripperLocation; + private Point _storedTargetGripperLocation; /// /// Store the current location of the target gripper @@ -120,7 +120,8 @@ namespace Greenshot.Editor.Drawing int xOffset = leftAligned ? -20 : 20; int yOffset = topAligned ? -20 : 20; - NativePoint newGripperLocation = _initialGripperPoint.Offset(xOffset, yOffset); + NativePoint initialGripperPoint = _initialGripperPoint; + NativePoint newGripperLocation = initialGripperPoint.Offset(xOffset, yOffset); if (TargetAdorner.Location != newGripperLocation) { diff --git a/src/Greenshot.Editor/Drawing/StepLabelContainer.cs b/src/Greenshot.Editor/Drawing/StepLabelContainer.cs index e5d93e514..7edecc4f8 100644 --- a/src/Greenshot.Editor/Drawing/StepLabelContainer.cs +++ b/src/Greenshot.Editor/Drawing/StepLabelContainer.cs @@ -24,6 +24,7 @@ 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; @@ -187,6 +188,8 @@ 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; @@ -205,9 +208,17 @@ namespace Greenshot.Editor.Drawing EllipseContainer.DrawEllipse(rect, graphics, rm, 0, Color.Transparent, fillColor, false); } - float fontSize = Math.Min(Math.Abs(Width), Math.Abs(Height)) / 1.4f; - using FontFamily fam = new FontFamily(FontFamily.GenericSansSerif.Name); - using Font font = new Font(fam, fontSize, FontStyle.Bold, GraphicsUnit.Pixel); + 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); TextContainer.DrawText(graphics, rect, 0, lineColor, false, _stringFormat, text, font); } diff --git a/src/Greenshot.Editor/Drawing/Surface.cs b/src/Greenshot.Editor/Drawing/Surface.cs index f050374a6..6d47a67a6 100644 --- a/src/Greenshot.Editor/Drawing/Surface.cs +++ b/src/Greenshot.Editor/Drawing/Surface.cs @@ -28,6 +28,7 @@ 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; @@ -40,6 +41,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.Memento; using log4net; @@ -722,6 +724,7 @@ 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 @@ -731,6 +734,10 @@ namespace Greenshot.Editor.Drawing SelectElements(loadedElements); FieldAggregator.BindElements(loadedElements); } + catch (SecurityAccessDeniedException) + { + throw; + } catch (Exception e) { LOG.Error("Error serializing elements from stream.", e); diff --git a/src/Greenshot.Editor/Drawing/SvgContainer.cs b/src/Greenshot.Editor/Drawing/SvgContainer.cs index 283f755e8..85b8cb43d 100644 --- a/src/Greenshot.Editor/Drawing/SvgContainer.cs +++ b/src/Greenshot.Editor/Drawing/SvgContainer.cs @@ -21,6 +21,7 @@ using System; using System.Drawing; +using System.IO; using Dapplo.Windows.Common.Structs; using Greenshot.Base.Core; using Greenshot.Base.Interfaces; @@ -34,14 +35,32 @@ namespace Greenshot.Editor.Drawing [Serializable] public class SvgContainer : VectorGraphicsContainer { - private readonly SvgDocument _svgDocument; + private MemoryStream _svgContent; - public SvgContainer(SvgDocument svgDocument, ISurface parent) : base(parent) + [NonSerialized] + private SvgDocument _svgDocument; + + public SvgContainer(Stream stream, ISurface parent) : base(parent) { - _svgDocument = svgDocument; - Size = new Size((int)svgDocument.Width, (int)svgDocument.Height); + _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); diff --git a/src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs b/src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs index 0c467c0b8..43da94c7d 100644 --- a/src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs +++ b/src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs @@ -25,6 +25,7 @@ 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 { @@ -35,28 +36,55 @@ namespace Greenshot.Editor.Drawing [Serializable] public abstract class VectorGraphicsContainer : DrawableContainer { - protected int RotationAngle; + 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 some other information. + /// Do not serialized, it can be rebuild with other information. /// - [NonSerialized] private Image _cachedImage; + [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(); } - private void Init() + /// + /// Init is called after creating the instance, and from OnDeserialized + /// This is the place to generate your adorners + /// + protected virtual void Init() { - CreateDefaultAdorners(); + // 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)); } /// @@ -96,7 +124,10 @@ namespace Greenshot.Editor.Drawing } _cachedImage ??= ComputeBitmap(); - + if (_cachedImage == null) + { + return; + } graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.CompositingQuality = CompositingQuality.HighQuality; @@ -106,9 +137,16 @@ namespace Greenshot.Editor.Drawing graphics.DrawImage(_cachedImage, Bounds); } + /// + /// Implement this to compute the new bitmap according to the size of the container + /// + /// Image protected abstract Image ComputeBitmap(); - private void ResetCachedBitmap() + /// + /// Dispose of the cached bitmap, forcing the code to regenerate it + /// + protected void ResetCachedBitmap() { _cachedImage?.Dispose(); _cachedImage = null; diff --git a/src/Greenshot.Editor/FileFormatHandlers/DibFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/DibFileFormatHandler.cs index ad129c4ba..b14a33bd0 100644 --- a/src/Greenshot.Editor/FileFormatHandlers/DibFileFormatHandler.cs +++ b/src/Greenshot.Editor/FileFormatHandlers/DibFileFormatHandler.cs @@ -100,8 +100,7 @@ namespace Greenshot.Editor.FileFormatHandlers bitmap = new Bitmap(infoHeader.Width, infoHeader.Height, -(int)(infoHeader.SizeImage / infoHeader.Height), infoHeader.BitCount == 32 ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb, - new IntPtr(handle.AddrOfPinnedObject().ToInt32() + infoHeader.OffsetToPixels + - (infoHeader.Height - 1) * (int)(infoHeader.SizeImage / infoHeader.Height)) + IntPtr.Add(handle.AddrOfPinnedObject(), (int)infoHeader.OffsetToPixels + (infoHeader.Height - 1) * (int)(infoHeader.SizeImage / infoHeader.Height)) ); } catch (Exception ex) diff --git a/src/Greenshot.Editor/FileFormatHandlers/SvgFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/SvgFileFormatHandler.cs index 14a33246e..76c5e85b9 100644 --- a/src/Greenshot.Editor/FileFormatHandlers/SvgFileFormatHandler.cs +++ b/src/Greenshot.Editor/FileFormatHandlers/SvgFileFormatHandler.cs @@ -1,89 +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 . - */ - -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) - { - SvgDocument svgDocument = null; - try - { - svgDocument = SvgDocument.Open(stream); - } - catch (Exception ex) - { - Log.Error("Can't load SVG", ex); - } - if (svgDocument != null) - { - yield return new SvgContainer(svgDocument, parent); - } - } - } -} +/* + * 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/Forms/ImageEditorForm.cs b/src/Greenshot.Editor/Forms/ImageEditorForm.cs index 87c3813c7..97263d8c2 100644 --- a/src/Greenshot.Editor/Forms/ImageEditorForm.cs +++ b/src/Greenshot.Editor/Forms/ImageEditorForm.cs @@ -1023,6 +1023,9 @@ 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)) diff --git a/src/Greenshot.Editor/Greenshot.Editor.csproj b/src/Greenshot.Editor/Greenshot.Editor.csproj index 7da9d555d..5dcd99bc1 100644 --- a/src/Greenshot.Editor/Greenshot.Editor.csproj +++ b/src/Greenshot.Editor/Greenshot.Editor.csproj @@ -2,6 +2,10 @@ True + + none + false + PreserveNewest diff --git a/src/Greenshot.Editor/Helpers/BinaryFormatterHelper.cs b/src/Greenshot.Editor/Helpers/BinaryFormatterHelper.cs new file mode 100644 index 000000000..11c7dbae6 --- /dev/null +++ b/src/Greenshot.Editor/Helpers/BinaryFormatterHelper.cs @@ -0,0 +1,124 @@ +/* + * 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/IDoubleProcessor.cs b/src/Greenshot.Editor/Helpers/IDoubleProcessor.cs new file mode 100644 index 000000000..beb426c68 --- /dev/null +++ b/src/Greenshot.Editor/Helpers/IDoubleProcessor.cs @@ -0,0 +1,28 @@ +/* + * 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.Editor.Helpers +{ + public interface IDoubleProcessor + { + double Process(double d); + } +} \ No newline at end of file diff --git a/src/Greenshot.Editor/Helpers/IHaveScaleOptions.cs b/src/Greenshot.Editor/Helpers/IHaveScaleOptions.cs new file mode 100644 index 000000000..3f1ed03ff --- /dev/null +++ b/src/Greenshot.Editor/Helpers/IHaveScaleOptions.cs @@ -0,0 +1,28 @@ +/* + * 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 . + */ + +namespace Greenshot.Editor.Helpers +{ + public interface IHaveScaleOptions + { + ScaleOptions GetScaleOptions(); + } +} diff --git a/src/Greenshot.Editor/Helpers/LineAngleRoundBehavior.cs b/src/Greenshot.Editor/Helpers/LineAngleRoundBehavior.cs new file mode 100644 index 000000000..6811f66cf --- /dev/null +++ b/src/Greenshot.Editor/Helpers/LineAngleRoundBehavior.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; + +namespace Greenshot.Editor.Helpers +{ + public class LineAngleRoundBehavior : IDoubleProcessor + { + public static readonly 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/Helpers/ScaleHelper.cs b/src/Greenshot.Editor/Helpers/ScaleHelper.cs index 4f4e7dc7a..8e4a76a13 100644 --- a/src/Greenshot.Editor/Helpers/ScaleHelper.cs +++ b/src/Greenshot.Editor/Helpers/ScaleHelper.cs @@ -33,32 +33,13 @@ 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) - /// a new SizeF object indicating the width and height the element should be scaled to + /// NativeSizeFloat object indicating the width and height the element should be scaled to public static NativeSizeFloat GetScaledSize(NativeSizeFloat currentSize, NativeSizeFloat targetSize, bool crop) { float wFactor = targetSize.Width / currentSize.Width; @@ -71,10 +52,10 @@ namespace Greenshot.Editor.Helpers /// /// calculates the position of an element depending on the desired alignment within a RectangleF /// - /// the bounds of the element to be aligned - /// the rectangle reference for alignment of the element + /// NativeRectFloat the bounds of the element to be aligned + /// NativeRectFloat with the rectangle 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 - /// a new RectangleF object with Location aligned aligned to targetRect + /// NativeRectFloat object with Location aligned aligned to targetRect public static NativeRectFloat GetAlignedRectangle(NativeRectFloat currentRect, NativeRectFloat targetRect, ContentAlignment alignment) { var newRect = new NativeRectFloat(targetRect.Location, currentRect.Size); @@ -96,9 +77,9 @@ namespace Greenshot.Editor.Helpers /// /// Calculates target size of a given rectangle scaled by dragging one of its handles (corners) /// - /// bounds of the current rectangle - /// 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 + /// 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 /// ScaleOptions to use when scaling /// NativeRectFloat scaled originalRectangle public static NativeRectFloat Scale(NativeRectFloat originalRectangle, Positions resizeHandlePosition, NativePointFloat resizeHandleCoords, ScaleOptions? options) @@ -134,9 +115,9 @@ namespace Greenshot.Editor.Helpers /// /// Calculates target size of a given rectangle scaled by dragging one of its handles (corners) /// - /// bounds of the current rectangle - /// 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 + /// 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) { @@ -158,9 +139,9 @@ 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. /// - /// bounds of the current rectangle - /// position of the handle/gripper being used for resized, see Position - /// coordinates of the used handle/gripper + /// 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) { @@ -203,7 +184,7 @@ namespace Greenshot.Editor.Helpers /// /// 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) { NativeSizeFloat newSize = selectedSize; @@ -221,29 +202,28 @@ namespace Greenshot.Editor.Helpers { // scaled rectangle (ratio) would be taller than original // keep width and tweak height to maintain aspect ratio - newSize = newSize.ChangeWidth(selectedSize.Width / originalRatio * flippedRatioSign); + newSize = newSize.ChangeHeight(selectedSize.Width / originalRatio * flippedRatioSign); } return newSize; } - public static NativeRectFloat Scale(NativeRect boundsBeforeResize, int cursorX, int cursorY) + /// + /// 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) { - return Scale(boundsBeforeResize, cursorX, cursorY, null); - } - - public static NativeRectFloat Scale(NativeRect boundsBeforeResize, int cursorX, int cursorY, IDoubleProcessor angleRoundBehavior) - { - return Scale(boundsBeforeResize, Positions.TopLeft, cursorX, cursorY, angleRoundBehavior); - } - - public static NativeRectFloat Scale(NativeRect boundsBeforeResize, Positions gripperPosition, int cursorX, int cursorY, IDoubleProcessor angleRoundBehavior) - { - ScaleOptions opts = GetScaleOptions(); + scaleOptions ??= GetScaleOptions(); NativeRectFloat result = boundsBeforeResize; - bool rationalScale = (opts & ScaleOptions.Rational) == ScaleOptions.Rational; - bool centeredScale = (opts & ScaleOptions.Centered) == ScaleOptions.Centered; + bool rationalScale = (scaleOptions & ScaleOptions.Rational) == ScaleOptions.Rational; + bool centeredScale = (scaleOptions & ScaleOptions.Centered) == ScaleOptions.Centered; if (rationalScale) { @@ -281,38 +261,5 @@ namespace Greenshot.Editor.Helpers if (maintainAspectRatio) opts |= ScaleOptions.Rational; return opts; } - - public interface IDoubleProcessor - { - double Process(double d); - } - - public class ShapeAngleRoundBehavior : IDoubleProcessor - { - public static readonly ShapeAngleRoundBehavior INSTANCE = new(); - - private ShapeAngleRoundBehavior() - { - } - - public double Process(double angle) - { - return Math.Round((angle + 45) / 90) * 90 - 45; - } - } - - public class LineAngleRoundBehavior : IDoubleProcessor - { - public static readonly 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/Helpers/ScaleOptions.cs b/src/Greenshot.Editor/Helpers/ScaleOptions.cs new file mode 100644 index 000000000..1f47f5c02 --- /dev/null +++ b/src/Greenshot.Editor/Helpers/ScaleOptions.cs @@ -0,0 +1,44 @@ +/* + * 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.Editor.Helpers +{ + [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 + } +} \ No newline at end of file diff --git a/src/Greenshot.Editor/Helpers/ShapeAngleRoundBehavior.cs b/src/Greenshot.Editor/Helpers/ShapeAngleRoundBehavior.cs new file mode 100644 index 000000000..ba173df9f --- /dev/null +++ b/src/Greenshot.Editor/Helpers/ShapeAngleRoundBehavior.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; + +namespace Greenshot.Editor.Helpers +{ + public class ShapeAngleRoundBehavior : IDoubleProcessor + { + public static readonly ShapeAngleRoundBehavior INSTANCE = new(); + + private ShapeAngleRoundBehavior() + { + } + + public double Process(double angle) + { + return Math.Round((angle + 45) / 90) * 90 - 45; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Box/BoxConfiguration.cs b/src/Greenshot.Plugin.Box/BoxConfiguration.cs index fdef95ae5..c745fe985 100644 --- a/src/Greenshot.Plugin.Box/BoxConfiguration.cs +++ b/src/Greenshot.Plugin.Box/BoxConfiguration.cs @@ -21,6 +21,7 @@ using System; using System.Windows.Forms; +using Greenshot.Base.Core; using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; using Greenshot.Plugin.Box.Forms; @@ -75,5 +76,20 @@ 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/Greenshot.Plugin.Box.Credentials.template b/src/Greenshot.Plugin.Box/Greenshot.Plugin.Box.Credentials.template index df6eaa0bd..da2dbc7ae 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 fced869ad..2b3410c8f 100644 --- a/src/Greenshot.Plugin.Box/Greenshot.Plugin.Box.csproj +++ b/src/Greenshot.Plugin.Box/Greenshot.Plugin.Box.csproj @@ -1,4 +1,8 @@  + + none + false + PreserveNewest diff --git a/src/Greenshot.Plugin.Box/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Box/Properties/AssemblyInfo.cs index 2ffb6f6fd..0379e4a65 100644 --- a/src/Greenshot.Plugin.Box/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Box/Properties/AssemblyInfo.cs @@ -21,11 +21,13 @@ 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/Greenshot.Plugin.Confluence.csproj b/src/Greenshot.Plugin.Confluence/Greenshot.Plugin.Confluence.csproj index d885ea3f5..0734c44e9 100644 --- a/src/Greenshot.Plugin.Confluence/Greenshot.Plugin.Confluence.csproj +++ b/src/Greenshot.Plugin.Confluence/Greenshot.Plugin.Confluence.csproj @@ -1,4 +1,8 @@  + + none + false + PreserveNewest diff --git a/src/Greenshot.Plugin.Confluence/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Confluence/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..8d2833e17 --- /dev/null +++ b/src/Greenshot.Plugin.Confluence/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +/* + * 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.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 Confluence")] +[assembly: AssemblyPluginIdentifier("Confluence 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.Dropbox/DropboxConfiguration.cs b/src/Greenshot.Plugin.Dropbox/DropboxConfiguration.cs index b9dd8bf0c..d79db8686 100644 --- a/src/Greenshot.Plugin.Dropbox/DropboxConfiguration.cs +++ b/src/Greenshot.Plugin.Dropbox/DropboxConfiguration.cs @@ -21,6 +21,7 @@ using System; using System.Windows.Forms; +using Greenshot.Base.Core; using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; using Greenshot.Plugin.Dropbox.Forms; @@ -69,5 +70,20 @@ namespace Greenshot.Plugin.Dropbox 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.Dropbox/Greenshot.Plugin.Dropbox.csproj b/src/Greenshot.Plugin.Dropbox/Greenshot.Plugin.Dropbox.csproj index 09eb988a5..7437b64aa 100644 --- a/src/Greenshot.Plugin.Dropbox/Greenshot.Plugin.Dropbox.csproj +++ b/src/Greenshot.Plugin.Dropbox/Greenshot.Plugin.Dropbox.csproj @@ -1,4 +1,8 @@  + + none + false + PreserveNewest diff --git a/src/Greenshot.Plugin.Dropbox/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Dropbox/Properties/AssemblyInfo.cs index d01359e25..b95b9af6d 100644 --- a/src/Greenshot.Plugin.Dropbox/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Dropbox/Properties/AssemblyInfo.cs @@ -21,11 +21,13 @@ 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/Greenshot.Plugin.ExternalCommand.csproj b/src/Greenshot.Plugin.ExternalCommand/Greenshot.Plugin.ExternalCommand.csproj index 50e39b62a..d0151b7c0 100644 --- a/src/Greenshot.Plugin.ExternalCommand/Greenshot.Plugin.ExternalCommand.csproj +++ b/src/Greenshot.Plugin.ExternalCommand/Greenshot.Plugin.ExternalCommand.csproj @@ -1,4 +1,8 @@  + + none + false + PreserveNewest diff --git a/src/Greenshot.Plugin.ExternalCommand/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.ExternalCommand/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..acbe80370 --- /dev/null +++ b/src/Greenshot.Plugin.ExternalCommand/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +/* + * 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 e4f602796..df5a428fa 100644 --- a/src/Greenshot.Plugin.Flickr/FlickrConfiguration.cs +++ b/src/Greenshot.Plugin.Flickr/FlickrConfiguration.cs @@ -20,6 +20,7 @@ */ using System.Windows.Forms; +using Greenshot.Base.Core; using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; using Greenshot.Plugin.Flickr.Forms; @@ -86,5 +87,21 @@ 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/Greenshot.Plugin.Flickr.csproj b/src/Greenshot.Plugin.Flickr/Greenshot.Plugin.Flickr.csproj index 5bc629bf1..8bdb8bcb4 100644 --- a/src/Greenshot.Plugin.Flickr/Greenshot.Plugin.Flickr.csproj +++ b/src/Greenshot.Plugin.Flickr/Greenshot.Plugin.Flickr.csproj @@ -1,4 +1,8 @@  + + none + false + PreserveNewest diff --git a/src/Greenshot.Plugin.Flickr/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Flickr/Properties/AssemblyInfo.cs index 30eafc83e..9701ef654 100644 --- a/src/Greenshot.Plugin.Flickr/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Flickr/Properties/AssemblyInfo.cs @@ -21,11 +21,13 @@ 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/Greenshot.Plugin.GooglePhotos.csproj b/src/Greenshot.Plugin.GooglePhotos/Greenshot.Plugin.GooglePhotos.csproj index f2bfb8be5..154410dd2 100644 --- a/src/Greenshot.Plugin.GooglePhotos/Greenshot.Plugin.GooglePhotos.csproj +++ b/src/Greenshot.Plugin.GooglePhotos/Greenshot.Plugin.GooglePhotos.csproj @@ -1,4 +1,8 @@  + + none + false + PreserveNewest diff --git a/src/Greenshot.Plugin.GooglePhotos/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.GooglePhotos/Properties/AssemblyInfo.cs index a49045818..3ff014500 100644 --- a/src/Greenshot.Plugin.GooglePhotos/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.GooglePhotos/Properties/AssemblyInfo.cs @@ -21,12 +21,17 @@ 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/Greenshot.Plugin.Imgur.csproj b/src/Greenshot.Plugin.Imgur/Greenshot.Plugin.Imgur.csproj index e0696e270..5cb8e0baa 100644 --- a/src/Greenshot.Plugin.Imgur/Greenshot.Plugin.Imgur.csproj +++ b/src/Greenshot.Plugin.Imgur/Greenshot.Plugin.Imgur.csproj @@ -1,4 +1,8 @@  + + none + false + PreserveNewest diff --git a/src/Greenshot.Plugin.Imgur/ImgurConfiguration.cs b/src/Greenshot.Plugin.Imgur/ImgurConfiguration.cs index 3bb695672..3dc145d0b 100644 --- a/src/Greenshot.Plugin.Imgur/ImgurConfiguration.cs +++ b/src/Greenshot.Plugin.Imgur/ImgurConfiguration.cs @@ -22,6 +22,7 @@ using System; using System.Collections.Generic; using System.Windows.Forms; +using Greenshot.Base.Core; using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; using Greenshot.Plugin.Imgur.Forms; @@ -84,6 +85,22 @@ 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 /// @@ -92,7 +109,7 @@ namespace Greenshot.Plugin.Imgur public override object GetDefault(string property) => property switch { - "ImgurUploadHistory" => new Dictionary(), + nameof(ImgurUploadHistory) => new Dictionary(), _ => null }; diff --git a/src/Greenshot.Plugin.Imgur/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Imgur/Properties/AssemblyInfo.cs index befa881fd..4f9d4afb5 100644 --- a/src/Greenshot.Plugin.Imgur/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Imgur/Properties/AssemblyInfo.cs @@ -21,11 +21,13 @@ 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/Greenshot.Plugin.Jira.csproj b/src/Greenshot.Plugin.Jira/Greenshot.Plugin.Jira.csproj index 40f2caf2d..d38eb063a 100644 --- a/src/Greenshot.Plugin.Jira/Greenshot.Plugin.Jira.csproj +++ b/src/Greenshot.Plugin.Jira/Greenshot.Plugin.Jira.csproj @@ -1,4 +1,8 @@  + + none + false + PreserveNewest @@ -6,7 +10,7 @@ - - + + \ No newline at end of file diff --git a/src/Greenshot.Plugin.Jira/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Jira/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..44f8800ce --- /dev/null +++ b/src/Greenshot.Plugin.Jira/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +/* + * 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/Destinations/ExcelDestination.cs b/src/Greenshot.Plugin.Office/Destinations/ExcelDestination.cs index fb1930c8c..1a671cf7a 100644 --- a/src/Greenshot.Plugin.Office/Destinations/ExcelDestination.cs +++ b/src/Greenshot.Plugin.Office/Destinations/ExcelDestination.cs @@ -42,7 +42,8 @@ namespace Greenshot.Plugin.Office.Destinations static ExcelDestination() { - ExePath = PluginUtils.GetExePath("EXCEL.EXE"); + ExePath = OfficeUtils.GetOfficeExePath("EXCEL.EXE") ?? PluginUtils.GetExePath("EXCEL.EXE"); + if (ExePath != null && File.Exists(ExePath)) { WindowDetails.AddProcessToExcludeFromFreeze("excel"); diff --git a/src/Greenshot.Plugin.Office/Destinations/OneNoteDestination.cs b/src/Greenshot.Plugin.Office/Destinations/OneNoteDestination.cs index 387810494..5f8da7a67 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 = PluginUtils.GetExePath("ONENOTE.EXE"); + exePath = OfficeUtils.GetOfficeExePath("ONENOTE.EXE") ?? 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 6d93c4096..3c77ea4f1 100644 --- a/src/Greenshot.Plugin.Office/Destinations/OutlookDestination.cs +++ b/src/Greenshot.Plugin.Office/Destinations/OutlookDestination.cs @@ -58,8 +58,7 @@ namespace Greenshot.Plugin.Office.Destinations { IsActiveFlag = true; } - - ExePath = PluginUtils.GetExePath("OUTLOOK.EXE"); + ExePath = OfficeUtils.GetOfficeExePath("OUTLOOK.EXE") ?? PluginUtils.GetExePath("OUTLOOK.EXE"); if (ExePath != null && File.Exists(ExePath)) { WindowDetails.AddProcessToExcludeFromFreeze("outlook"); diff --git a/src/Greenshot.Plugin.Office/Destinations/PowerpointDestination.cs b/src/Greenshot.Plugin.Office/Destinations/PowerpointDestination.cs index 3b9ce4bc8..010093027 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 = PluginUtils.GetExePath("POWERPNT.EXE"); + ExePath = OfficeUtils.GetOfficeExePath("POWERPNT.EXE") ?? PluginUtils.GetExePath("POWERPNT.EXE"); if (ExePath != null && File.Exists(ExePath)) { WindowDetails.AddProcessToExcludeFromFreeze("powerpnt"); diff --git a/src/Greenshot.Plugin.Office/Destinations/WordDestination.cs b/src/Greenshot.Plugin.Office/Destinations/WordDestination.cs index 99cf4693c..d6220a302 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 = PluginUtils.GetExePath("WINWORD.EXE"); + ExePath = OfficeUtils.GetOfficeExePath("WINWORD.EXE") ?? PluginUtils.GetExePath("WINWORD.EXE"); if (ExePath != null && !File.Exists(ExePath)) { ExePath = null; @@ -118,7 +118,7 @@ namespace Greenshot.Plugin.Office.Destinations if (!manuallyInitiated) { var documents = _wordExporter.GetWordDocuments().ToList(); - if (documents != null && documents.Count > 0) + if (documents is { 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 fdb555adc..982b86833 100644 --- a/src/Greenshot.Plugin.Office/Greenshot.Plugin.Office.csproj +++ b/src/Greenshot.Plugin.Office/Greenshot.Plugin.Office.csproj @@ -1,4 +1,8 @@  + + none + false + PreserveNewest @@ -78,23 +82,23 @@ - + true runtime - + true runtime - + true runtime - + true runtime - + true runtime diff --git a/src/Greenshot.Plugin.Office/OfficeUtils.cs b/src/Greenshot.Plugin.Office/OfficeUtils.cs new file mode 100644 index 000000000..305611c5d --- /dev/null +++ b/src/Greenshot.Plugin.Office/OfficeUtils.cs @@ -0,0 +1,45 @@ +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 9dcc75892..6b14a40f5 100644 --- a/src/Greenshot.Plugin.Office/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Office/Properties/AssemblyInfo.cs @@ -21,11 +21,13 @@ 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/Greenshot.Plugin.Photobucket.csproj b/src/Greenshot.Plugin.Photobucket/Greenshot.Plugin.Photobucket.csproj index 50e39b62a..d0151b7c0 100644 --- a/src/Greenshot.Plugin.Photobucket/Greenshot.Plugin.Photobucket.csproj +++ b/src/Greenshot.Plugin.Photobucket/Greenshot.Plugin.Photobucket.csproj @@ -1,4 +1,8 @@  + + none + false + PreserveNewest diff --git a/src/Greenshot.Plugin.Photobucket/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Photobucket/Properties/AssemblyInfo.cs index 783762320..231ca594d 100644 --- a/src/Greenshot.Plugin.Photobucket/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Photobucket/Properties/AssemblyInfo.cs @@ -21,12 +21,14 @@ 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/Greenshot.Plugin.Win10.csproj b/src/Greenshot.Plugin.Win10/Greenshot.Plugin.Win10.csproj index c1c1729e0..14126adb3 100644 --- a/src/Greenshot.Plugin.Win10/Greenshot.Plugin.Win10.csproj +++ b/src/Greenshot.Plugin.Win10/Greenshot.Plugin.Win10.csproj @@ -1,4 +1,8 @@  + + none + false + PreserveNewest diff --git a/src/Greenshot.Plugin.Win10/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Win10/Properties/AssemblyInfo.cs index d1b6cbd1b..3f4b5bef2 100644 --- a/src/Greenshot.Plugin.Win10/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Win10/Properties/AssemblyInfo.cs @@ -1,10 +1,33 @@ -using System.Reflection; +/* + * 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.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 b9120fee7..52005356c 100644 --- a/src/Greenshot.Plugin.Win10/ToastNotificationService.cs +++ b/src/Greenshot.Plugin.Win10/ToastNotificationService.cs @@ -95,7 +95,17 @@ namespace Greenshot.Plugin.Win10 } // Prepare the toast notifier. Be sure to specify the AppUserModelId on your application's shortcut! - var toastNotifier = ToastNotificationManagerCompat.CreateToastNotifier(); + Microsoft.Toolkit.Uwp.Notifications.ToastNotifierCompat toastNotifier = null; + try + { + toastNotifier = ToastNotificationManagerCompat.CreateToastNotifier(); + } + catch (Exception ex) + { + Log.Warn("Could not create a toast notifier.", ex); + + return; + } // 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.sln b/src/Greenshot.sln index 91ddf4314..b71e79bd3 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 16 -VisualStudioVersion = 16.0.29728.190 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34009.444 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Greenshot", "Greenshot\Greenshot.csproj", "{CD642BF4-D815-4D67-A0B5-C69F0B8231AF}" ProjectSection(ProjectDependencies) = postProject @@ -48,6 +48,7 @@ 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 80b985a5c..ab8a7c40c 100644 --- a/src/Greenshot/App.config +++ b/src/Greenshot/App.config @@ -11,8 +11,7 @@ - - + \ No newline at end of file diff --git a/src/Greenshot/Forms/CaptureForm.cs b/src/Greenshot/Forms/CaptureForm.cs index 68e57f040..107aa9096 100644 --- a/src/Greenshot/Forms/CaptureForm.cs +++ b/src/Greenshot/Forms/CaptureForm.cs @@ -197,7 +197,7 @@ namespace Greenshot.Forms private void CaptureForm_Resize(object sender, EventArgs e) { Log.DebugFormat("Resize was called, new size: {0}", this.Bounds); - if (Bounds.Equals(_capture.ScreenBounds)) + if (_capture.ScreenBounds.Equals(Bounds)) { // We have the correct size return; @@ -425,9 +425,11 @@ namespace Greenshot.Forms else if (_captureRect.Height > 0 && _captureRect.Width > 0) { // correct the GUI width to real width if Region mode - if (_captureMode == CaptureMode.Region || _captureMode == CaptureMode.Text) + if (_captureMode is CaptureMode.Region or CaptureMode.Text) { - _captureRect = _captureRect.Inflate(1, 1); + // 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); } // Go and process the capture diff --git a/src/Greenshot/Greenshot.csproj b/src/Greenshot/Greenshot.csproj index 78fa3f6c6..f0d97afe9 100644 --- a/src/Greenshot/Greenshot.csproj +++ b/src/Greenshot/Greenshot.csproj @@ -9,6 +9,10 @@ false false + + none + false + @@ -17,7 +21,8 @@ - + + @@ -75,6 +80,7 @@ + diff --git a/src/Greenshot/Helpers/CaptureHelper.cs b/src/Greenshot/Helpers/CaptureHelper.cs index c002c62ef..e51b51ebe 100644 --- a/src/Greenshot/Helpers/CaptureHelper.cs +++ b/src/Greenshot/Helpers/CaptureHelper.cs @@ -564,14 +564,6 @@ 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", @@ -984,7 +976,7 @@ namespace Greenshot.Helpers else { // Change to GDI, if allowed - if (!windowToCapture.IsMetroApp && WindowCapture.IsGdiAllowed(process)) + if (WindowCapture.IsGdiAllowed(process)) { if (!dwmEnabled && IsWpf(process)) { @@ -1000,7 +992,7 @@ namespace Greenshot.Helpers // Change to DWM, if enabled and allowed if (dwmEnabled) { - if (windowToCapture.IsMetroApp || WindowCapture.IsDwmAllowed(process)) + if (WindowCapture.IsDwmAllowed(process)) { windowCaptureMode = WindowCaptureMode.Aero; } @@ -1009,7 +1001,7 @@ namespace Greenshot.Helpers } else if (windowCaptureMode == WindowCaptureMode.Aero || windowCaptureMode == WindowCaptureMode.AeroTransparent) { - if (!dwmEnabled || (!windowToCapture.IsMetroApp && !WindowCapture.IsDwmAllowed(process))) + if (!dwmEnabled || !WindowCapture.IsDwmAllowed(process)) { // Take default screen windowCaptureMode = WindowCaptureMode.Screen; @@ -1115,7 +1107,7 @@ namespace Greenshot.Helpers break; case WindowCaptureMode.Aero: case WindowCaptureMode.AeroTransparent: - if (windowToCapture.IsMetroApp || WindowCapture.IsDwmAllowed(process)) + if (WindowCapture.IsDwmAllowed(process)) { tmpCapture = windowToCapture.CaptureDwmWindow(captureForWindow, windowCaptureMode, isAutoMode); } diff --git a/src/Greenshot/Helpers/PluginHelper.cs b/src/Greenshot/Helpers/PluginHelper.cs index 3ed3d97c3..e5724a4f5 100644 --- a/src/Greenshot/Helpers/PluginHelper.cs +++ b/src/Greenshot/Helpers/PluginHelper.cs @@ -219,17 +219,14 @@ namespace Greenshot.Helpers { var assembly = Assembly.LoadFrom(pluginFile); - 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)) + if (IsPluginExcludedByConfig(assembly, pluginFile) ) { - 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 assemblyName = assembly.GetName().Name; + var pluginEntryName = $"{assemblyName}.{assemblyName.Replace("Greenshot.Plugin.", string.Empty)}Plugin"; + var pluginEntryType = assembly.GetType(pluginEntryName, false, true); var plugin = (IGreenshotPlugin) Activator.CreateInstance(pluginEntryType); if (plugin != null) @@ -255,5 +252,54 @@ 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/Languages/language-ca-CA.xml b/src/Greenshot/Languages/language-ca-CA.xml index 423c55006..323f7849f 100644 --- a/src/Greenshot/Languages/language-ca-CA.xml +++ b/src/Greenshot/Languages/language-ca-CA.xml @@ -305,7 +305,7 @@ Malgrat això, encara es poden utilitzar totes les característiques de Greensho Afegeix un comptador (I) Afegeix una bafarada (S) - Canvia la mida + Canvia la mida (Z) 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 86d7f2207..cc409c66f 100644 --- a/src/Greenshot/Languages/language-cs-CZ.xml +++ b/src/Greenshot/Languages/language-cs-CZ.xml @@ -308,7 +308,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 + Změnit velikost (Z) Nastavení změny velikosti Zachovat poměr stran Šířka diff --git a/src/Greenshot/Languages/language-de-DE.xml b/src/Greenshot/Languages/language-de-DE.xml index a50ad0119..b636da112 100644 --- a/src/Greenshot/Languages/language-de-DE.xml +++ b/src/Greenshot/Languages/language-de-DE.xml @@ -312,7 +312,7 @@ Sie können aber auch alle Greenshot-Funktionen über das Kontextmenü des Green Zähler hinzufügen (I) Sprechblase hinzufügen (S) - Skalieren + Skalieren (Z) Einstellungen für Skalierung Seitenverhältnis beibehalten Breite diff --git a/src/Greenshot/Languages/language-en-US.xml b/src/Greenshot/Languages/language-en-US.xml index 5cac2280d..1403d3e9c 100644 --- a/src/Greenshot/Languages/language-en-US.xml +++ b/src/Greenshot/Languages/language-en-US.xml @@ -312,7 +312,7 @@ All Greenshot features still work directly from the tray icon context menu witho Add counter (I) Add speechbubble (S) - Resize + Resize (Z) Resize settings Maintain aspect ratio Width diff --git a/src/Greenshot/Languages/language-fr-FR.xml b/src/Greenshot/Languages/language-fr-FR.xml index 50cde5788..4ef502438 100644 --- a/src/Greenshot/Languages/language-fr-FR.xml +++ b/src/Greenshot/Languages/language-fr-FR.xml @@ -144,7 +144,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 + Redimensionner (Z) Maintenir le rapport L / H Hauteur Pourcentage diff --git a/src/Greenshot/Languages/language-id-ID.xml b/src/Greenshot/Languages/language-id-ID.xml index df31cd624..fcb887082 100644 --- a/src/Greenshot/Languages/language-id-ID.xml +++ b/src/Greenshot/Languages/language-id-ID.xml @@ -144,7 +144,7 @@ Juga, kami sangat terbantu apabila anda mengecek laporan lain yang sama dengan k Cetak Ulang {0} Reset ukuran - Ubah ukuran + Ubah ukuran (Z) Pertahankan aspek rasio Tinggi Persen diff --git a/src/Greenshot/Languages/language-it-IT.xml b/src/Greenshot/Languages/language-it-IT.xml index b0cb0e96f..89d68c988 100644 --- a/src/Greenshot/Languages/language-it-IT.xml +++ b/src/Greenshot/Languages/language-it-IT.xml @@ -322,7 +322,7 @@ In alternativa alle scorciatoie di tastiera, tutte le funzioni di Greenshot sono Aggiungi conteggio Aggiungi nuvoletta - Ridimensiona + Ridimensiona (Z) Impostazioni ridimensionamento Mantieni rapporto dimensioni Larghezza diff --git a/src/Greenshot/Languages/language-ja-JP.xml b/src/Greenshot/Languages/language-ja-JP.xml index 03638f0dc..36f5231f2 100644 --- a/src/Greenshot/Languages/language-ja-JP.xml +++ b/src/Greenshot/Languages/language-ja-JP.xml @@ -143,7 +143,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 e3366a18a..8a690952c 100644 --- a/src/Greenshot/Languages/language-kab-DZ.xml +++ b/src/Greenshot/Languages/language-kab-DZ.xml @@ -144,7 +144,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 + Snifel tahri/teɣzi (Z) 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 0bf90570e..144188ad8 100644 --- a/src/Greenshot/Languages/language-ko-KR.xml +++ b/src/Greenshot/Languages/language-ko-KR.xml @@ -304,7 +304,7 @@ ${hostname} PC명 카운터 더하기 (I) 설명선 더하기(S) - 크기조정 + 크기조정 (Z) 크기조정 설정 종횡비 유지 너비 diff --git a/src/Greenshot/Languages/language-lv-LV.xml b/src/Greenshot/Languages/language-lv-LV.xml index acbe9eacb..dea057944 100644 --- a/src/Greenshot/Languages/language-lv-LV.xml +++ b/src/Greenshot/Languages/language-lv-LV.xml @@ -306,7 +306,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 + Mainīt izmēru (Z) 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 65c008c01..d640b0a42 100644 --- a/src/Greenshot/Languages/language-nl-NL.xml +++ b/src/Greenshot/Languages/language-nl-NL.xml @@ -307,7 +307,7 @@ Alle Greenshot functies werken ook zonder sneltoetsen via het context menu.Teller toevoegen (I) Tekstballon toevoegen (S) - Grootte + Grootte (Z) Vergrotingsinstellingen Verhouding behouden Breedte diff --git a/src/Greenshot/Languages/language-pt-PT.xml b/src/Greenshot/Languages/language-pt-PT.xml index 53a3ea424..4da21743d 100644 --- a/src/Greenshot/Languages/language-pt-PT.xml +++ b/src/Greenshot/Languages/language-pt-PT.xml @@ -305,7 +305,7 @@ Todas as funcionalidades do Greenshot funcionam directamente através do menu de Adicionar contador (I) Adicionar balão de texto (S) - Redimensionar + Redimensionar (Z) Definições de Redimensionamento Manter proporções Largura diff --git a/src/Greenshot/Languages/language-sv-SE.xml b/src/Greenshot/Languages/language-sv-SE.xml index c67bfe8fe..c5b1517e2 100644 --- a/src/Greenshot/Languages/language-sv-SE.xml +++ b/src/Greenshot/Languages/language-sv-SE.xml @@ -305,7 +305,7 @@ Alla Greenshots funktioner fungerar fortfarande från snabbmenyn i aktivitetsfä Lägg till räknare Lägg till pratbubbla - Anpassa storlek + Anpassa storlek (Z) Storleksinställningar Behåll bildförhållande Bredd diff --git a/src/Greenshot/Languages/language-uk-UA.xml b/src/Greenshot/Languages/language-uk-UA.xml index 1eae26c97..057927676 100644 --- a/src/Greenshot/Languages/language-uk-UA.xml +++ b/src/Greenshot/Languages/language-uk-UA.xml @@ -306,7 +306,7 @@ ${hostname} назва комп’ютера Додати лічильник (Ш) Додати словесну бульбашку (І) - Змінити розмір + Змінити розмір (Z) Параметри зміни розміру Зберігати пропорції Ширина diff --git a/src/Greenshot/Languages/language-zh-TW.xml b/src/Greenshot/Languages/language-zh-TW.xml index 69f5e6696..63f8fce2f 100644 --- a/src/Greenshot/Languages/language-zh-TW.xml +++ b/src/Greenshot/Languages/language-zh-TW.xml @@ -307,7 +307,7 @@ Greenshot 所有功能仍然可以直接從通知區圖示的內容功能表動 加入計數器 (I) 加入對話框 (S) - 縮放 + 縮放 (Z) 縮放設定 維持長寬比 長度