Compare commits

...

45 commits

Author SHA1 Message Date
jklingen
c1ad1d4a93 Remove Debug Info from Release Builds
Some checks failed
Build and Deploy / build (push) Has been cancelled
Build and Deploy / deploy (push) Has been cancelled
2025-08-10 17:11:30 +02:00
jklingen
82702c4830
Chore: Update Change Log
Some checks failed
Build and Deploy / build (push) Has been cancelled
Build and Deploy / deploy (push) Has been cancelled
2025-08-09 16:43:42 +02:00
Christian Schulz
b69c415669
Fix: Define plugin name for exclusion in INI (#644)
Fixes #642 #648
2025-08-09 16:38:32 +02:00
jklingen
e81abdcd7b
Chore: Update Change Log with Latest Releases 2025-08-09 16:29:55 +02:00
jklingen
dbbfbb654e
Installer: Allow Choice between All-Users (Administrative) and Current-User Installation #625 (#641)
Some checks failed
Build and Deploy / build (push) Has been cancelled
Build and Deploy / deploy (push) Has been cancelled
Also fixes #546, #611, #598
2025-07-26 16:17:08 +02:00
jklingen
7360791872
Add Possibility to Publish Test Releases from Branches (#631)
... without causing a mess and a lot of confusion by using the same build numbers as in release branch.
2025-07-24 18:04:11 +02:00
jklingen
aa98a7ce78
Add Trademark and Logo Usage Policy 2025-07-18 11:24:21 +02:00
jklingen
dbfdfe9a05
Chore: Enhance Github Pages Rebuild Trigger with other release types
... to make sure the list on the website is always up to date, without manual interaction.
2025-07-17 17:40:11 +02:00
Thomas Braun
2121547387
added PrivilegesRequiredOverridesAllowed=commandline 2025-07-16 12:11:18 +02:00
jklingen
af04773497
Add workflow_dispatch Trigger to Build and Deploy Workflow
Some checks failed
Build and Deploy / build (push) Has been cancelled
Build and Deploy / deploy (push) Has been cancelled
2025-07-15 12:38:22 +02:00
Nathan_B
8e98cdbfb5
Add Hotkey for Resize Button in Editor (#480) 2025-05-23 15:02:44 +02:00
Jens Glathe
789a569706
fix: handle picky Win11 ToastNotificationService (#487) 2025-05-23 14:55:54 +02:00
Julien Richard
6400b08a8c
Fix scaling with fixed ratio (#514) 2025-05-23 14:43:28 +02:00
Nathan_B
1f0ae08a52
#572 Fix Error when Opening .greenshot File with Arrows 2025-05-22 18:42:02 +02:00
jklingen
95d4c1c2d1
Publish Unsigned Release on Commit and Purge CloudFlare Cache on Pages Build (#583) 2025-05-21 12:53:32 +02:00
Christian Schulz
cc063b6426
Calculate optimal font size for StepLabel #457 (#460) 2025-05-21 07:40:19 +02:00
Thomas Braun
ff260bae52 Merge branch 'release/1.3' of https://github.com/greenshot/greenshot into release/1.3 2025-05-16 15:53:24 +02:00
Thomas Braun
15d5888090 changed token 2025-05-16 15:52:31 +02:00
jklingen
c8f424b72e
Fix Reference to Token 2025-05-16 15:49:26 +02:00
Thomas Braun
262307e61e fixed branch changes 2025-05-16 15:34:51 +02:00
jklingen
cac566c70a
Add Release Script (#581) 2025-05-16 15:17:26 +02:00
jklingen
245f6f261b
Enhance Readme with Link to Releases (#549) 2024-08-18 11:22:45 +02:00
jklingen
bb7a374390
Create SECURITY.md 2023-10-03 16:11:09 +02:00
Jens Glathe
a3e65fee6f
add install option to disable the default win11 prtscr tool (#484)
Signed-off-by: Jens Glathe <jens.glathe@oldschoolsolutions.biz>
2023-04-16 19:25:14 +02:00
Robin Krom
f862e79485 Fixed SvgContainer support for the .greenshot file 2023-03-27 22:43:03 +02:00
Robin Krom
099e656963 Brought back IconContainer support to the .greenshot format, Svg is still causing issue. 2023-03-27 22:27:02 +02:00
Robin Krom
a152e2883f This should improve the backwards compatibility for the .greenshot file from earlier versions. This was also addressed in #375 2023-03-27 21:51:01 +02:00
Robin Krom
7e005f741a
Fixed #441, swapped left & top. 2022-10-05 22:29:01 +02:00
Robin Krom
4c6707b468
Fixed #432, inflate is not the best way of increasing a rectangle size. 2022-09-28 20:53:57 +02:00
Robin Krom
9634f8abbc
Updated dependencies. 2022-09-04 15:35:38 +02:00
Robin Krom
029d47f479
Added some NPE protections for BUG-2991 2022-09-03 15:11:16 +02:00
Robin Krom
511034a34b
Fixed the return value of the GetOfficeExePath, this should be null so it's clear the file hasn't been found. 2022-08-17 23:12:55 +02:00
Robin Krom
296dc9f340
Formatting 2022-08-17 23:09:44 +02:00
Robin Krom
af3c22c38c
Optimized the code to find the office executable even more. 2022-08-17 23:08:59 +02:00
Robin Krom
3e88093846
Some aftercare for #431, moved the code to find the path for the office executables to the office plugin, as this is where it's needed. Also optimized the code a bit, using modern features. 2022-08-17 23:01:24 +02:00
jdavila71
ba8ed074c8
Fix of BUG-2951, issues with finding Office installations (#431) 2022-08-17 22:01:01 +02:00
Robin Krom
2b5e45e33e
Fixed a log error, and made the ClipboardHotkey optional. 2022-06-30 23:47:38 +02:00
Robin Krom
bfa8e2444e
This should hopefully fix an overflow exception while decoding the dib format. 2022-06-30 23:34:51 +02:00
Robin Krom
48675b01f0
Modified the configuration handling, added code to remove tokens when upgrading from 1.2 as these no longer work. See #421 2022-06-30 22:57:55 +02:00
Robin Krom
36a285ebd4
Fix capturing Maximised windows. 2022-06-28 22:17:56 +02:00
Robin Krom
f50f205b70
Removed old Windows 8 logic, which caused #403 (which is a very unlucky issue number LOL)
This should actually make Greenshot a tiny bit faster and use less resources, but I am not 100% sure about the effects as some windows still seem to have the window class for Windows 8.
2022-06-28 16:28:26 +02:00
Robin Krom
4c7494dd74
Some left over fixes for the move to Dapplo.Windows, also updated dependencies. 2022-06-26 01:40:53 +02:00
Robin Krom
3f6f9863f8
Removed copying all the dependencies for every plugin, this should fix #413. 2022-06-03 11:23:27 +02:00
Christian Schulz
bd03e18ddc
Bugfix crop selection scaling #404 (#407)
Disable scaling for horizontal and vertical crop modes.
2022-05-21 22:57:20 +02:00
Robin Krom
94591e5b14
Small fixes to the VectorGraphicsContainer, to align it with the possible coming EmojiContainer. Still having issues with Scaling code, this needs to be fixed. [skip ci] 2022-04-14 11:58:52 +02:00
96 changed files with 1979 additions and 1068 deletions

View file

@ -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 }}

163
.github/workflows/release.yml vendored Normal file
View file

@ -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

18
.github/workflows/update-gh-pages.yml vendored Normal file
View file

@ -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

4
.gitignore vendored
View file

@ -214,4 +214,6 @@ ModelManifest.xml
*.credentials.cs *.credentials.cs
# Rider files # Rider files
.idea .idea
/installer/Greenshot-INSTALLER-*.exe

View file

@ -21,4 +21,25 @@ Being easy to understand and configurable, Greenshot is an efficient tool for pr
About this repository 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.

5
SECURITY.md Normal file
View file

@ -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.

View file

@ -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

149
build-and-deploy.ps1 Normal file
View file

@ -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."

View file

@ -7,7 +7,29 @@ CHANGE LOG:
All details to our tickets can be found here: https://greenshot.atlassian.net 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. 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 ] * when clicking two overlapping elements, the one created later gets selected [ 1725175 ]
* created textboxes can now be edited with a doubleclick [ 1704408 ] * created textboxes can now be edited with a doubleclick [ 1704408 ]
* selected font is now stored in the application config file [ 1704411 ] * selected font is now stored in the application config file [ 1704411 ]

View file

@ -7,6 +7,7 @@
#define BinDir "bin\Release\net472" #define BinDir "bin\Release\net472"
#define ReleaseDir "..\..\src\Greenshot\bin\Release\net472" #define ReleaseDir "..\..\src\Greenshot\bin\Release\net472"
#define PluginDir "..\..\src\Greenshot\bin\Release\net472\Plugins" #define PluginDir "..\..\src\Greenshot\bin\Release\net472\Plugins"
#define CertumThumbprint GetEnv('CertumThumbprint')
; Include the scripts to install .NET Framework ; Include the scripts to install .NET Framework
; See https://www.codeproject.com/KB/install/dotnetfx_innosetup_instal.aspx ; 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; Source: {#LanguagesDir}\*zh-TW*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\zhTW; Flags: overwritereadonly ignoreversion replacesameversion;
;Office Plugin ;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 ;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; Source: {#BaseDir}\Greenshot.Plugin.Jira\Languages\language_jira*.xml; DestDir: {app}\Languages\Plugins\Jira; Components: plugins\jira; Flags: overwritereadonly ignoreversion replacesameversion;
;Imgur Plugin ;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; Source: {#BaseDir}\Greenshot.Plugin.Imgur\Languages\language_imgur*.xml; DestDir: {app}\Languages\Plugins\Imgur; Components: plugins\imgur; Flags: overwritereadonly ignoreversion replacesameversion;
;Box Plugin ;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; Source: {#BaseDir}\Greenshot.Plugin.Box\Languages\language_box*.xml; DestDir: {app}\Languages\Plugins\Box; Components: plugins\box; Flags: overwritereadonly ignoreversion replacesameversion;
;DropBox Plugin ;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; Source: {#BaseDir}\Greenshot.Plugin.DropBox\Languages\language_dropbox*.xml; DestDir: {app}\Languages\Plugins\DropBox; Components: plugins\dropbox; Flags: overwritereadonly ignoreversion replacesameversion;
;Flickr Plugin ;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; Source: {#BaseDir}\Greenshot.Plugin.Flickr\Languages\language_flickr*.xml; DestDir: {app}\Languages\Plugins\Flickr; Components: plugins\flickr; Flags: overwritereadonly ignoreversion replacesameversion;
;Photobucket Plugin ;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; Source: {#BaseDir}\Greenshot.Plugin.Photobucket\Languages\language_photo*.xml; DestDir: {app}\Languages\Plugins\Photobucket; Components: plugins\photobucket; Flags: overwritereadonly ignoreversion replacesameversion;
;Confluence Plugin ;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; Source: {#BaseDir}\Greenshot.Plugin.Confluence\Languages\language_confluence*.xml; DestDir: {app}\Languages\Plugins\Confluence; Components: plugins\confluence; Flags: overwritereadonly ignoreversion replacesameversion;
;ExternalCommand Plugin ;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; Source: {#BaseDir}\Greenshot.Plugin.ExternalCommand\Languages\language_externalcommand*.xml; DestDir: {app}\Languages\Plugins\ExternalCommand; Components: plugins\externalcommand; Flags: overwritereadonly ignoreversion replacesameversion;
;Win 10 Plugin ;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] [Setup]
; changes associations is used when the installer installs new extensions, it clears the explorer icon cache ; changes associations is used when the installer installs new extensions, it clears the explorer icon cache
@ -125,21 +127,27 @@ AppVersion={#Version}
ArchitecturesInstallIn64BitMode=x64 ArchitecturesInstallIn64BitMode=x64
Compression=lzma2/ultra64 Compression=lzma2/ultra64
SolidCompression=yes SolidCompression=yes
DefaultDirName={code:DefDirRoot}\{#ExeName} DefaultDirName={autopf}\{#ExeName}
DefaultGroupName={#ExeName} DefaultGroupName={#ExeName}
InfoBeforeFile=..\additional_files\readme.txt InfoBeforeFile=..\additional_files\readme.txt
LicenseFile=..\additional_files\license.txt LicenseFile=..\additional_files\license.txt
LanguageDetectionMethod=uilanguage LanguageDetectionMethod=uilanguage
MinVersion=6.1sp1 MinVersion=6.1sp1
OutputBaseFilename={#ExeName}-INSTALLER-{#Version}-UNSTABLE
OutputDir=..\ 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 PrivilegesRequired=lowest
SetupIconFile=..\..\src\Greenshot\icons\applicationIcon\icon.ico SetupIconFile=..\..\src\Greenshot\icons\applicationIcon\icon.ico
; Create a SHA1 signature #if CertumThumbprint != ""
; SignTool=SignTool sign /debug /fd sha1 /tr https://time.certum.pl /td sha1 $f OutputBaseFilename={#ExeName}-INSTALLER-{#Version}-UNSTABLE
; Append a SHA256 to the previous SHA1 signature (this is what as does) SignTool=SignTool sign /sha1 "{#CertumThumbprint}" /tr http://time.certum.pl /td sha256 /fd sha256 /v $f
; SignTool=SignTool sign /debug /as /fd sha256 /tr https://time.certum.pl /td sha256 $f SignedUninstaller=yes
; SignedUninstaller=yes #else
OutputBaseFilename={#ExeName}-INSTALLER-{#Version}-UNSTABLE-UNSIGNED
#endif
UninstallDisplayIcon={app}\{#ExeName}.exe UninstallDisplayIcon={app}\{#ExeName}.exe
Uninstallable=true Uninstallable=true
VersionInfoCompany={#ExeName} VersionInfoCompany={#ExeName}
@ -151,6 +159,7 @@ VersionInfoVersion={#Version}
WizardImageFile=installer-large.bmp WizardImageFile=installer-large.bmp
; Reference a bitmap, max size 55x58 ; Reference a bitmap, max size 55x58
WizardSmallImageFile=installer-small.bmp WizardSmallImageFile=installer-small.bmp
[Registry] [Registry]
; Delete all startup entries, so we don't have leftover values ; 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; 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; Root: HKLM; Subkey: Software\Classes\Greenshot; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror;
; Create the startup entries if requested to do so ; Create the startup entries if requested to do so
; HKEY_LOCAL_USER - for current user only Root: HKA; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: string; ValueName: {#ExeName}; ValueData: """{app}\{#ExeName}.exe"""; Flags: uninsdeletevalue noerror; Tasks: startup
Root: HKCU; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: string; ValueName: {#ExeName}; ValueData: """{app}\{#ExeName}.exe"""; Permissions: users-modify; Flags: uninsdeletevalue noerror; Tasks: startup; Check: IsRegularUser
; HKEY_LOCAL_MACHINE - for all users when admin
Root: HKLM; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: string; ValueName: {#ExeName}; ValueData: """{app}\{#ExeName}.exe"""; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Tasks: startup; Check: not IsRegularUser
; Register our own filetype for all users ; Register our own filetype for all users
; HKEY_LOCAL_USER - for current user only Root: HKA; Subkey: Software\Classes\.greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot"; Flags: uninsdeletevalue noerror
Root: HKCU; Subkey: Software\Classes\.greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot"; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser Root: HKA; Subkey: Software\Classes\Greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot File"; Flags: uninsdeletevalue noerror
Root: HKCU; Subkey: Software\Classes\Greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot File"; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser Root: HKA; Subkey: Software\Classes\Greenshot\DefaultIcon; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE,0"""; Flags: uninsdeletevalue noerror
Root: HKCU; Subkey: Software\Classes\Greenshot\DefaultIcon; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE,0"""; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser Root: HKA; Subkey: Software\Classes\Greenshot\shell\open\command; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE"" --openfile ""%1"""; Flags: uninsdeletevalue noerror
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 ; Disable the default PRTSCR Snipping Tool in Windows 11
Root: HKLM; Subkey: Software\Classes\.greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot"; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser Root: HKCU; Subkey: Control Panel\Keyboard; ValueType: dword; ValueName: "PrintScreenKeyForSnippingEnabled"; ValueData: "0"; Flags: uninsdeletevalue; Check: ShouldDisableSnippingTool
Root: HKLM; Subkey: Software\Classes\Greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot File"; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser
Root: HKLM; Subkey: Software\Classes\Greenshot\DefaultIcon; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE,0"""; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser
Root: HKLM; Subkey: Software\Classes\Greenshot\shell\open\command; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE"" --openfile ""%1"""; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser
[Icons] [Icons]
Name: {group}\{#ExeName}; Filename: {app}\{#ExeName}.exe; WorkingDir: {app}; AppUserModelID: "{#ExeName}" 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.UninstallIconDescription=Uninstall
en.ShowLicense=Show license en.ShowLicense=Show license
en.ShowReadme=Show Readme en.ShowReadme=Show Readme
en.disablewin11snippingtool=Disable Win11 default PrtScr snipping tool
de.confluence=Confluence Plug-in de.confluence=Confluence Plug-in
de.default=Standard installation de.default=Standard installation
@ -280,6 +284,7 @@ de.optimize=Optimierung der Leistung, kann etwas dauern.
de.startgreenshot={#ExeName} starten de.startgreenshot={#ExeName} starten
de.startup={#ExeName} starten wenn Windows hochfährt de.startup={#ExeName} starten wenn Windows hochfährt
de.win10=Windows 10 Plug-in de.win10=Windows 10 Plug-in
de.disablewin11snippingtool=Deaktiviere das Standard Windows 11 Snipping Tool auf "Druck"
es.confluence=Extensión para Confluence es.confluence=Extensión para Confluence
es.default=${default} es.default=${default}
@ -481,6 +486,7 @@ Name: "compact"; Description: "{code:CompactInstall}"
Name: "custom"; Description: "{code:CustomInstall}"; Flags: iscustom Name: "custom"; Description: "{code:CustomInstall}"; Flags: iscustom
[Components] [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: "greenshot"; Description: "Greenshot"; Types: default full compact custom; Flags: fixed
;Name: "plugins\networkimport"; Description: "Network Import Plugin"; Types: full ;Name: "plugins\networkimport"; Description: "Network Import Plugin"; Types: full
Name: "plugins\box"; Description: {cm:box}; Types: full custom; Flags: disablenouninstallwarning 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\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\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') Name: "languages\zhTW"; Description: {cm:zhTW}; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('9')
[Code] [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; function FullInstall(Param : String) : String;
begin begin
result := SetupMessage(msgFullInstallation); result := SetupMessage(msgFullInstallation);
@ -744,6 +735,19 @@ begin
Result := IsWindowsVersionOrNewer(10, 0); Result := IsWindowsVersionOrNewer(10, 0);
end; 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] [Run]
Filename: "{app}\{#ExeName}.exe"; Description: "{cm:startgreenshot}"; Parameters: "{code:GetParamsForGS}"; WorkingDir: "{app}"; Flags: nowait postinstall runasoriginaluser 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 Filename: "https://getgreenshot.org/thank-you/?language={language}&version={#Version}"; Flags: shellexec runasoriginaluser

6
nuget.config Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>

View file

@ -1,13 +1,13 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Copyright>Copyright © Greenshot 2004-2021</Copyright> <Copyright>Copyright © Greenshot 2004-2022</Copyright>
<Authors>Greenshot</Authors> <Authors>Greenshot</Authors>
<PackageIconUrl>https://getgreenshot.org/favicon.ico</PackageIconUrl> <PackageIconUrl>https://getgreenshot.org/favicon.ico</PackageIconUrl>
<RepositoryUrl>https://github.com/greenshot/greenshot</RepositoryUrl> <RepositoryUrl>https://github.com/greenshot/greenshot</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PackageProjectUrl>https://github.com/greenshot/greenshot</PackageProjectUrl> <PackageProjectUrl>https://github.com/greenshot/greenshot</PackageProjectUrl>
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression> <PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
<LangVersion>9</LangVersion> <LangVersion>latest</LangVersion>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<RuntimeIdentifiers>win10-x64;win10-x86;win-x64;win-x86</RuntimeIdentifiers> <RuntimeIdentifiers>win10-x64;win10-x86;win-x64;win-x86</RuntimeIdentifiers>
@ -46,7 +46,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="!$(MSBuildProjectName.Contains('Tests')) And $(MSBuildProjectName.StartsWith('Greenshot'))"> <ItemGroup Condition="!$(MSBuildProjectName.Contains('Tests')) And $(MSBuildProjectName.StartsWith('Greenshot'))">
<PackageReference Include="Nerdbank.GitVersioning" Version="3.4.255"> <PackageReference Include="Nerdbank.GitVersioning" Version="3.5.113">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference> </PackageReference>

View file

@ -1,13 +1,65 @@
<Project> <Project>
<UsingTask TaskName="ApplyTokenReplacements" TaskFactory="CodeTaskFactory" AssemblyName="Microsoft.Build.Tasks.Core">
<ParameterGroup>
<InputLines ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
<Tokens ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
<OutputLines ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" />
</ParameterGroup>
<Task>
<Reference Include="System.Text.RegularExpressions" />
<Code Type="Fragment" Language="cs">
<![CDATA[
var output = new List<ITaskItem>();
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();
]]>
</Code>
</Task>
</UsingTask>
<PropertyGroup Condition="Exists('$(ProjectDir)$(ProjectName).Credentials.template')"> <PropertyGroup Condition="Exists('$(ProjectDir)$(ProjectName).Credentials.template')">
<MSBuildCommunityTasksPath>$(PkgMSBuildTasks)\tools\</MSBuildCommunityTasksPath> <MSBuildCommunityTasksPath>$(NuGetPackageRoot)msbuildtasks/1.5.0.235/tools/</MSBuildCommunityTasksPath>
</PropertyGroup> </PropertyGroup>
<Import Project="$(MSBuildCommunityTasksPath)MSBuild.Community.Tasks.Targets" Condition="Exists('$(ProjectDir)$(ProjectName).Credentials.template')"/> <Import Project="$(MSBuildCommunityTasksPath)MSBuild.Community.Tasks.Targets" Condition="Exists('$(ProjectDir)$(ProjectName).Credentials.template')" />
<Target Name="ProcessTemplates" BeforeTargets="PrepareForBuild" Condition="Exists('$(ProjectDir)$(ProjectName).Credentials.template')"> <Target Name="ProcessTemplates" BeforeTargets="PrepareForBuild" Condition="Exists('$(ProjectDir)$(ProjectName).Credentials.template')">
<Message Text="Processing: $(ProjectDir)$(ProjectName).Credentials.template" Importance="high"/> <Message Text="Processing: $(ProjectDir)$(ProjectName).Credentials.template" Importance="high"/>
<TemplateFile Template="$(ProjectDir)$(ProjectName).Credentials.template" OutputFilename="$(ProjectDir)$(ProjectName).Credentials.cs" Tokens="@(Tokens)" />
<ReadLinesFromFile File="$(ProjectDir)$(ProjectName).Credentials.template">
<Output TaskParameter="Lines" ItemName="TemplateLines" />
</ReadLinesFromFile>
<ApplyTokenReplacements InputLines="@(TemplateLines)" Tokens="@(Tokens)">
<Output TaskParameter="OutputLines" ItemName="ProcessedLines" />
</ApplyTokenReplacements>
<WriteLinesToFile
File="$(ProjectDir)$(ProjectName).Credentials.cs"
Lines="@(ProcessedLines)"
Overwrite="true" />
</Target> </Target>
</Project> </Project>

View file

@ -386,6 +386,7 @@ EndSelection:<<<<<<<4
/// <returns>IEnumerable{(MemoryStream,string)}</returns> /// <returns>IEnumerable{(MemoryStream,string)}</returns>
private static IEnumerable<(MemoryStream stream,string filename)> IterateClipboardContent(IDataObject dataObject) private static IEnumerable<(MemoryStream stream,string filename)> IterateClipboardContent(IDataObject dataObject)
{ {
if (dataObject == null) yield break;
var fileDescriptors = AvailableFileDescriptors(dataObject); var fileDescriptors = AvailableFileDescriptors(dataObject);
if (fileDescriptors == null) yield break; if (fileDescriptors == null) yield break;
@ -499,6 +500,10 @@ EndSelection:<<<<<<<4
public static Image GetImage() public static Image GetImage()
{ {
IDataObject clipboardData = GetDataObject(); IDataObject clipboardData = GetDataObject();
if (clipboardData == null)
{
return null;
}
// Return the first image // Return the first image
foreach (var clipboardImage in GetImages(clipboardData)) foreach (var clipboardImage in GetImages(clipboardData))
{ {
@ -520,7 +525,7 @@ EndSelection:<<<<<<<4
Bitmap singleImage = GetImage(dataObject); Bitmap singleImage = GetImage(dataObject);
if (singleImage != null) 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 return singleImage;
yield break; yield break;
} }

View file

@ -59,7 +59,7 @@ namespace Greenshot.Base.Core
[IniProperty("IEHotkey", Description = "Hotkey for starting the IE capture", DefaultValue = "Shift + Ctrl + PrintScreen")] [IniProperty("IEHotkey", Description = "Hotkey for starting the IE capture", DefaultValue = "Shift + Ctrl + PrintScreen")]
public string IEHotkey { get; set; } 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; } public string ClipboardHotkey { get; set; }
[IniProperty("IsFirstLaunch", Description = "Is this the first time launch?", DefaultValue = "true")] [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); 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);
}
/// <summary> /// <summary>
/// Supply values we can't put as defaults /// Supply values we can't put as defaults
/// </summary> /// </summary>
/// <param name="property">The property to return a default for</param> /// <param name="property">The property to return a default for</param>
/// <returns>object with the default value for the supplied property</returns> /// <returns>object with the default value for the supplied property</returns>
public override object GetDefault(string property) public override object GetDefault(string property) =>
{ property switch
switch (property)
{ {
case nameof(ExcludePlugins): nameof(ExcludePlugins) => new List<string>(),
case nameof(IncludePlugins): nameof(IncludePlugins) => new List<string>(),
return new List<string>(); nameof(OutputFileAsFullpath) => IniConfig.IsPortable ? Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots\dummy.png") : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "dummy.png"),
case nameof(OutputFileAsFullpath): nameof(OutputFilePath) => CreateOutputFilePath(),
if (IniConfig.IsPortable) nameof(DWMBackgroundColor) => Color.Transparent,
{ nameof(ActiveTitleFixes) => new List<string> {
return Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots\dummy.png"); "Firefox",
} "IE",
"Chrome"
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "dummy.png"); },
case nameof(OutputFilePath): nameof(TitleFixMatcher) => new Dictionary<string, string> {
if (IniConfig.IsPortable) { "Firefox", " - Mozilla Firefox.*" },
{ { "IE", " - (Microsoft|Windows) Internet Explorer.*" },
string pafOutputFilePath = Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots"); { "Chrome", " - Google Chrome.*" }
if (!Directory.Exists(pafOutputFilePath)) },
{ nameof(TitleFixReplacer) => new Dictionary<string, string> {
try { "Firefox", string.Empty },
{ { "IE", string.Empty },
Directory.CreateDirectory(pafOutputFilePath); { "Chrome", string.Empty }
return pafOutputFilePath; },
} _ => null
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<string>
{
"Firefox",
"IE",
"Chrome"
};
case nameof(TitleFixMatcher):
return new Dictionary<string, string>
{
{
"Firefox", " - Mozilla Firefox.*"
},
{
"IE", " - (Microsoft|Windows) Internet Explorer.*"
},
{
"Chrome", " - Google Chrome.*"
}
};
case nameof(TitleFixReplacer):
return new Dictionary<string, string>
{
{
"Firefox", string.Empty
},
{
"IE", string.Empty
},
{
"Chrome", string.Empty
}
};
}
return null;
}
/// <summary> /// <summary>
/// This method will be called before converting the property, making to possible to correct a certain value /// This method will be called before converting the property, making to possible to correct a certain value
/// Can be used when migration is needed /// Can be used when migration is needed
@ -540,8 +515,9 @@ namespace Greenshot.Base.Core
OutputFileAutoReduceColors = false; OutputFileAutoReduceColors = false;
} }
bool isUpgradeFrom12 = LastSaveWithVersion?.StartsWith("1.2") ?? false;
// Fix for excessive feed checking // Fix for excessive feed checking
if (UpdateCheckInterval != 0 && UpdateCheckInterval <= 7 && LastSaveWithVersion.StartsWith("1.2")) if (UpdateCheckInterval != 0 && UpdateCheckInterval <= 7 && isUpgradeFrom12)
{ {
UpdateCheckInterval = 14; UpdateCheckInterval = 14;
} }

View file

@ -24,6 +24,7 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Linq;
using System.Windows.Forms; using System.Windows.Forms;
using Dapplo.Windows.Icons; using Dapplo.Windows.Icons;
using Greenshot.Base.IniFile; using Greenshot.Base.IniFile;
@ -39,9 +40,9 @@ namespace Greenshot.Base.Core
{ {
private static readonly ILog Log = LogManager.GetLogger(typeof(PluginUtils)); private static readonly ILog Log = LogManager.GetLogger(typeof(PluginUtils));
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>(); private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
private const string PathKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\";
private static readonly IDictionary<string, Image> ExeIconCache = new Dictionary<string, Image>(); private static readonly IDictionary<string, Image> ExeIconCache = new Dictionary<string, Image>();
private const string PathKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\";
static PluginUtils() static PluginUtils()
{ {
CoreConfig.PropertyChanged += OnIconSizeChanged; CoreConfig.PropertyChanged += OnIconSizeChanged;
@ -84,7 +85,7 @@ namespace Greenshot.Base.Core
if (key != null) if (key != null)
{ {
// "" is the default key, which should point to the requested location // "" is the default key, which should point to the requested location
return (string) key.GetValue(string.Empty); return (string)key.GetValue(string.Empty);
} }
} }

View file

@ -42,8 +42,6 @@ namespace Greenshot.Base.Core
{ {
private const string AppWindowClass = "Windows.UI.Core.CoreWindow"; //Used for Windows 8(.1) 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 AppFrameWindowClass = "ApplicationFrameWindow"; // Windows 10 uses ApplicationFrameWindow
private const string ApplauncherClass = "ImmersiveLauncher";
private const string GutterClass = "ImmersiveGutter";
private static readonly IList<string> IgnoreClasses = new List<string>(new[] private static readonly IList<string> IgnoreClasses = new List<string>(new[]
{ {
@ -89,13 +87,7 @@ namespace Greenshot.Base.Core
private IntPtr _parentHandle = IntPtr.Zero; private IntPtr _parentHandle = IntPtr.Zero;
private WindowDetails _parent; private WindowDetails _parent;
private bool _frozen; private bool _frozen;
/// <summary>
/// This checks if the window is a Windows 8 App
/// For Windows 10 most normal code works, as it's hosted inside "ApplicationFrameWindow"
/// </summary>
public bool IsApp => AppWindowClass.Equals(ClassName);
/// <summary> /// <summary>
/// This checks if the window is a Windows 10 App /// This checks if the window is a Windows 10 App
/// For Windows 10 apps are hosted inside "ApplicationFrameWindow" /// For Windows 10 apps are hosted inside "ApplicationFrameWindow"
@ -108,20 +100,6 @@ namespace Greenshot.Base.Core
public bool IsBackgroundWin10App => WindowsVersion.IsWindows10OrLater && AppFrameWindowClass.Equals(ClassName) && public bool IsBackgroundWin10App => WindowsVersion.IsWindows10OrLater && AppFrameWindowClass.Equals(ClassName) &&
!Children.Any(window => string.Equals(window.ClassName, AppWindowClass)); !Children.Any(window => string.Equals(window.ClassName, AppWindowClass));
/// <summary>
/// Check if the window is the metro gutter (sizeable separator)
/// </summary>
public bool IsGutter => GutterClass.Equals(ClassName);
/// <summary>
/// Test if this window is for the App-Launcher
/// </summary>
public bool IsAppLauncher => ApplauncherClass.Equals(ClassName);
/// <summary>
/// Check if this window is the window of a metro app
/// </summary>
public bool IsMetroApp => IsAppLauncher || IsApp;
/// <summary> /// <summary>
/// To allow items to be compared, the hash code /// To allow items to be compared, the hash code
@ -226,12 +204,6 @@ namespace Greenshot.Base.Core
Log.Warn(ex); Log.Warn(ex);
} }
if (IsMetroApp)
{
// No method yet to get the metro icon
return null;
}
try try
{ {
return PluginUtils.GetCachedExeIcon(ProcessPath, 0); return PluginUtils.GetCachedExeIcon(ProcessPath, 0);
@ -467,11 +439,6 @@ namespace Greenshot.Base.Core
{ {
get get
{ {
if (IsMetroApp)
{
return !Visible;
}
return User32Api.IsIconic(Handle) || Location.X <= -32000; return User32Api.IsIconic(Handle) || Location.X <= -32000;
} }
set set
@ -494,22 +461,6 @@ namespace Greenshot.Base.Core
{ {
get get
{ {
if (IsApp)
{
if (Visible)
{
foreach (var displayInfo in DisplayInfo.AllDisplayInfos)
{
if (WindowRectangle.Equals(displayInfo.Bounds))
{
return true;
}
}
}
return false;
}
return User32Api.IsZoomed(Handle); return User32Api.IsZoomed(Handle);
} }
set set
@ -546,50 +497,6 @@ namespace Greenshot.Base.Core
return false; 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); return User32Api.IsWindowVisible(Handle);
} }
} }
@ -643,69 +550,56 @@ namespace Greenshot.Base.Core
{ {
// Try to return a cached value // Try to return a cached value
long now = DateTime.Now.Ticks; long now = DateTime.Now.Ticks;
if (_previousWindowRectangle.IsEmpty || !_frozen) if (!_previousWindowRectangle.IsEmpty && _frozen) return _previousWindowRectangle;
{
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 (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; _previousWindowRectangle = windowRect;
_lastWindowRectangleRetrieveTime = now; _lastWindowRectangleRetrieveTime = now;
return windowRect;
} }
} }
if (windowRect.IsEmpty) if (gotFrameBounds && WindowsVersion.IsWindows10OrLater && !Maximised)
{ {
if (!GetWindowRect(out windowRect)) // 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?
Win32Error error = Win32.GetLastErrorCode(); windowRect = windowRect.Inflate(Conf.Win10BorderCrop);
Log.WarnFormat("Couldn't retrieve the windows rectangle: {0}", Win32.GetMessage(error)); _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) // if GDI is allowed.. (a screenshot won't be better than we comes if we continue)
using Process thisWindowProcess = Process; 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. // we return null which causes the capturing code to try another method.
return null; return null;
@ -973,11 +867,8 @@ namespace Greenshot.Base.Core
tempForm.BackColor = Color.Black; tempForm.BackColor = Color.Black;
// Make sure everything is visible // Make sure everything is visible
tempForm.Refresh(); 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 // Make sure all changes are processed and visible
Application.DoEvents(); Application.DoEvents();
@ -1013,11 +904,8 @@ namespace Greenshot.Base.Core
// Make sure everything is visible // Make sure everything is visible
tempForm.Refresh(); 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 // Make sure all changes are processed and visible
Application.DoEvents(); Application.DoEvents();
@ -1154,6 +1042,13 @@ namespace Greenshot.Base.Core
return targetBuffer.UnlockAndReturnBitmap(); return targetBuffer.UnlockAndReturnBitmap();
} }
/// <summary>
/// If a window is hidden (Iconic), it also has the specified dimensions.
/// </summary>
/// <param name="rect">NativeRect</param>
/// <returns>bool true if hidden</returns>
private bool IsHidden(NativeRect rect) => rect.Width == 65535 && rect.Height == 65535 && rect.Left == 32767 && rect.Top == 32767;
/// <summary> /// <summary>
/// Helper method to get the window size for DWM Windows /// Helper method to get the window size for DWM Windows
/// </summary> /// </summary>
@ -1164,6 +1059,10 @@ namespace Greenshot.Base.Core
var result = DwmApi.DwmGetWindowAttribute(Handle, DwmWindowAttributes.ExtendedFrameBounds, out NativeRect rect, Marshal.SizeOf(typeof(NativeRect))); var result = DwmApi.DwmGetWindowAttribute(Handle, DwmWindowAttributes.ExtendedFrameBounds, out NativeRect rect, Marshal.SizeOf(typeof(NativeRect)));
if (result.Succeeded()) if (result.Succeeded())
{ {
if (IsHidden(rect))
{
rect = NativeRect.Empty;
}
rectangle = rect; rectangle = rect;
return true; return true;
} }
@ -1196,7 +1095,14 @@ namespace Greenshot.Base.Core
var windowInfo = new WindowInfo(); var windowInfo = new WindowInfo();
// Get the Window Info for this window // Get the Window Info for this window
bool result = User32Api.GetWindowInfo(Handle, ref windowInfo); 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; return result;
} }
@ -1577,7 +1483,7 @@ namespace Greenshot.Base.Core
// Skip everything which is not rendered "normally", trying to fix BUG-2017 // Skip everything which is not rendered "normally", trying to fix BUG-2017
var exWindowStyle = window.ExtendedWindowStyle; 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; return false;
} }
@ -1592,13 +1498,6 @@ namespace Greenshot.Base.Core
public static IEnumerable<WindowDetails> GetVisibleWindows() public static IEnumerable<WindowDetails> GetVisibleWindows()
{ {
var screenBounds = DisplayInfo.ScreenBounds; var screenBounds = DisplayInfo.ScreenBounds;
foreach (var window in GetAppWindows())
{
if (IsVisible(window, screenBounds))
{
yield return window;
}
}
foreach (var window in GetAllWindows()) foreach (var window in GetAllWindows())
{ {
@ -1609,38 +1508,6 @@ namespace Greenshot.Base.Core
} }
} }
/// <summary>
/// Get the WindowDetails for all Metro Apps
/// These are all Windows with Classname "Windows.UI.Core.CoreWindow"
/// </summary>
/// <returns>List WindowDetails with visible metro apps</returns>
public static IEnumerable<WindowDetails> 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);
}
}
/// <summary> /// <summary>
/// Check if the window is a top level /// Check if the window is a top level
/// </summary> /// </summary>
@ -1671,7 +1538,7 @@ namespace Greenshot.Base.Core
} }
// Skip everything which is not rendered "normally", trying to fix BUG-2017 // 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; return false;
} }
@ -1707,14 +1574,6 @@ namespace Greenshot.Base.Core
/// <returns>List WindowDetails with all the top level windows</returns> /// <returns>List WindowDetails with all the top level windows</returns>
public static IEnumerable<WindowDetails> GetTopLevelWindows() public static IEnumerable<WindowDetails> GetTopLevelWindows()
{ {
foreach (var possibleTopLevel in GetAppWindows())
{
if (IsTopLevel(possibleTopLevel))
{
yield return possibleTopLevel;
}
}
foreach (var possibleTopLevel in GetAllWindows()) foreach (var possibleTopLevel in GetAllWindows())
{ {
if (IsTopLevel(possibleTopLevel)) if (IsTopLevel(possibleTopLevel))
@ -1790,27 +1649,6 @@ namespace Greenshot.Base.Core
} }
} }
/// <summary>
/// Get the AppLauncher
/// </summary>
/// <returns></returns>
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;
}
/// <summary> /// <summary>
/// Return true if the metro-app-launcher is visible /// Return true if the metro-app-launcher is visible
/// </summary> /// </summary>
@ -1842,7 +1680,6 @@ namespace Greenshot.Base.Core
result.AppendLine($"Size: {WindowRectangle.Size}"); result.AppendLine($"Size: {WindowRectangle.Size}");
result.AppendLine($"HasParent: {HasParent}"); result.AppendLine($"HasParent: {HasParent}");
result.AppendLine($"IsWin10App: {IsWin10App}"); result.AppendLine($"IsWin10App: {IsWin10App}");
result.AppendLine($"IsApp: {IsApp}");
result.AppendLine($"Visible: {Visible}"); result.AppendLine($"Visible: {Visible}");
result.AppendLine($"IsWindowVisible: {User32Api.IsWindowVisible(Handle)}"); result.AppendLine($"IsWindowVisible: {User32Api.IsWindowVisible(Handle)}");
result.AppendLine($"IsCloaked: {IsCloaked}"); result.AppendLine($"IsCloaked: {IsCloaked}");

View file

@ -3,18 +3,22 @@
<PropertyGroup> <PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Dapplo.HttpExtensions.JsonNet" Version="1.0.18" /> <PackageReference Include="Dapplo.HttpExtensions.JsonNet" Version="1.1.2" />
<PackageReference Include="Dapplo.Windows.Clipboard" Version="1.0.21" /> <PackageReference Include="Dapplo.Windows.Clipboard" Version="1.0.28" />
<PackageReference Include="Dapplo.Windows.Dpi" Version="1.0.21" /> <PackageReference Include="Dapplo.Windows.Dpi" Version="1.0.28" />
<PackageReference Include="Dapplo.Windows.Gdi32" Version="1.0.21" /> <PackageReference Include="Dapplo.Windows.Gdi32" Version="1.0.28" />
<PackageReference Include="Dapplo.Windows.Icons" Version="1.0.21" /> <PackageReference Include="Dapplo.Windows.Icons" Version="1.0.28" />
<PackageReference Include="Dapplo.Windows.Kernel32" Version="1.0.21" /> <PackageReference Include="Dapplo.Windows.Kernel32" Version="1.0.28" />
<PackageReference Include="Dapplo.Windows.Multimedia" Version="1.0.21" /> <PackageReference Include="Dapplo.Windows.Multimedia" Version="1.0.28" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.42" /> <PackageReference Include="HtmlAgilityPack" Version="1.11.46" />
<PackageReference Include="log4net" version="2.0.14" /> <PackageReference Include="log4net" version="2.0.15" />
<PackageReference Include="Svg" Version="3.4.1" /> <PackageReference Include="Svg" Version="3.4.3" />
<Reference Include="Accessibility" /> <Reference Include="Accessibility" />
<Reference Include="CustomMarshalers" /> <Reference Include="CustomMarshalers" />
</ItemGroup> </ItemGroup>

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
using System;
namespace Greenshot.Base.Interfaces.Plugin
{
/// <summary>
/// Attribute to specify a custom plugin identifier at assembly level
/// </summary>
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
public class AssemblyPluginIdentifierAttribute : Attribute
{
/// <summary>
/// The identifier used for the plugin in configuration
/// </summary>
public string Identifier { get; }
/// <summary>
/// Constructor for the plugin identifier attribute
/// </summary>
/// <param name="identifier">The identifier for the plugin in configuration</param>
public AssemblyPluginIdentifierAttribute(string identifier)
{
Identifier = identifier;
}
}
}

View file

@ -100,8 +100,10 @@ namespace Greenshot.Editor.Drawing.Adorners
// reset "workbench" rectangle to current bounds // reset "workbench" rectangle to current bounds
_boundsAfterResize = _boundsBeforeResize; _boundsAfterResize = _boundsBeforeResize;
var scaleOptions = (Owner as IHaveScaleOptions)?.GetScaleOptions();
// calculate scaled rectangle // 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 // apply scaled bounds to this DrawableContainer
Owner.ApplyBounds(_boundsAfterResize); Owner.ApplyBounds(_boundsAfterResize);

View file

@ -247,10 +247,11 @@ namespace Greenshot.Editor.Drawing
_boundsAfterResize = new NativeRectFloat( _boundsAfterResize = new NativeRectFloat(
_boundsBeforeResize.Left, _boundsBeforeResize.Top, _boundsBeforeResize.Left, _boundsBeforeResize.Top,
x - _boundsAfterResize.Left, y - _boundsAfterResize.Top); x - _boundsAfterResize.Left, y - _boundsAfterResize.Top);
_boundsAfterResize = ScaleHelper.Scale(_boundsAfterResize, x, y, GetAngleRoundProcessor());
break; break;
} }
} }
_boundsAfterResize = ScaleHelper.Scale(_boundsBeforeResize, x, y, GetAngleRoundProcessor());
// apply scaled bounds to this DrawableContainer // apply scaled bounds to this DrawableContainer
ApplyBounds(_boundsAfterResize); ApplyBounds(_boundsAfterResize);

View file

@ -533,7 +533,8 @@ namespace Greenshot.Editor.Drawing
// reset "workbench" rectangle to current bounds // reset "workbench" rectangle to current bounds
_boundsAfterResize = new NativeRectFloat(_boundsBeforeResize.Left, _boundsBeforeResize.Top, x - _boundsAfterResize.Left, y - _boundsAfterResize.Top); _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 // apply scaled bounds to this DrawableContainer
ApplyBounds(_boundsAfterResize); ApplyBounds(_boundsAfterResize);
@ -666,9 +667,9 @@ namespace Greenshot.Editor.Drawing
Height = points[1].Y - points[0].Y; 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; public virtual bool HasContextMenu => true;

View file

@ -1,323 +1,325 @@
/* /*
* Greenshot - a free and open source screenshot tool * Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
* *
* For more information see: https://getgreenshot.org/ * For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or * the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using Dapplo.Windows.Common.Structs; using Dapplo.Windows.Common.Structs;
using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing; using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Drawing.Fields;
using Greenshot.Editor.Helpers; using Greenshot.Editor.Helpers;
namespace Greenshot.Editor.Drawing namespace Greenshot.Editor.Drawing
{ {
/// <summary> /// <summary>
/// Description of PathContainer. /// Description of PathContainer.
/// </summary> /// </summary>
[Serializable] [Serializable]
public class FreehandContainer : DrawableContainer public class FreehandContainer : DrawableContainer
{ {
private static readonly float[] PointOffset = private static readonly float[] PointOffset =
{ {
0.5f, 0.25f, 0.75f 0.5f, 0.25f, 0.75f
}; };
[NonSerialized] private GraphicsPath freehandPath = new GraphicsPath(); [NonSerialized]
private NativeRect myBounds = NativeRect.Empty; private GraphicsPath freehandPath = new GraphicsPath();
private NativePoint lastMouse = NativePoint.Empty;
private readonly List<Point> capturePoints = new List<Point>(); private Rectangle myBounds = NativeRect.Empty;
private bool isRecalculated; private Point lastMouse = NativePoint.Empty;
private List<Point> capturePoints = new List<Point>();
/// <summary> private bool isRecalculated;
/// Constructor
/// </summary> /// <summary>
public FreehandContainer(ISurface parent) : base(parent) /// Constructor
{ /// </summary>
Width = parent.Image.Width; public FreehandContainer(ISurface parent) : base(parent)
Height = parent.Image.Height; {
Top = 0; Width = parent.Image.Width;
Left = 0; Height = parent.Image.Height;
} Top = 0;
Left = 0;
protected override void InitializeFields() }
{
AddField(GetType(), FieldType.LINE_THICKNESS, 3); protected override void InitializeFields()
AddField(GetType(), FieldType.LINE_COLOR, Color.Red); {
} AddField(GetType(), FieldType.LINE_THICKNESS, 3);
AddField(GetType(), FieldType.LINE_COLOR, Color.Red);
public override void Transform(Matrix matrix) }
{
Point[] points = capturePoints.ToArray(); public override void Transform(Matrix matrix)
{
matrix.TransformPoints(points); Point[] points = capturePoints.ToArray();
capturePoints.Clear();
capturePoints.AddRange(points); matrix.TransformPoints(points);
RecalculatePath(); capturePoints.Clear();
} capturePoints.AddRange(points);
RecalculatePath();
protected override void OnDeserialized(StreamingContext context) }
{
RecalculatePath(); protected override void OnDeserialized(StreamingContext context)
} {
RecalculatePath();
/// <summary> }
/// This Dispose is called from the Dispose and the Destructor.
/// </summary> /// <summary>
/// <param name="disposing">When disposing==true all non-managed resources should be freed too!</param> /// This Dispose is called from the Dispose and the Destructor.
protected override void Dispose(bool disposing) /// </summary>
{ /// <param name="disposing">When disposing==true all non-managed resources should be freed too!</param>
base.Dispose(disposing); protected override void Dispose(bool disposing)
if (disposing) {
{ base.Dispose(disposing);
freehandPath?.Dispose(); if (disposing)
} {
freehandPath?.Dispose();
freehandPath = null; }
}
freehandPath = null;
/// <summary> }
/// Called from Surface (the parent) when the drawing begins (mouse-down)
/// </summary> /// <summary>
/// <returns>true if the surface doesn't need to handle the event</returns> /// Called from Surface (the parent) when the drawing begins (mouse-down)
public override bool HandleMouseDown(int mouseX, int mouseY) /// </summary>
{ /// <returns>true if the surface doesn't need to handle the event</returns>
lastMouse = new Point(mouseX, mouseY); public override bool HandleMouseDown(int mouseX, int mouseY)
capturePoints.Add(lastMouse); {
return true; lastMouse = new Point(mouseX, mouseY);
} capturePoints.Add(lastMouse);
return true;
/// <summary> }
/// Called from Surface (the parent) if a mouse move is made while drawing
/// </summary> /// <summary>
/// <returns>true if the surface doesn't need to handle the event</returns> /// Called from Surface (the parent) if a mouse move is made while drawing
public override bool HandleMouseMove(int mouseX, int mouseY) /// </summary>
{ /// <returns>true if the surface doesn't need to handle the event</returns>
Point previousPoint = capturePoints[capturePoints.Count - 1]; public override bool HandleMouseMove(int mouseX, int mouseY)
{
if (GeometryHelper.Distance2D(previousPoint.X, previousPoint.Y, mouseX, mouseY) >= 2 * EditorConfig.FreehandSensitivity) Point previousPoint = capturePoints[capturePoints.Count - 1];
{
capturePoints.Add(new Point(mouseX, mouseY)); 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; 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)); //path.AddCurve(new Point[]{lastMouse, new Point(mouseX, mouseY)});
// Only re-calculate the bounds & redraw when we added something to the path lastMouse = new Point(mouseX, mouseY);
myBounds = Rectangle.Round(freehandPath.GetBounds()); freehandPath.AddLine(lastMouse, new Point(mouseX, mouseY));
// Only re-calculate the bounds & redraw when we added something to the path
Invalidate(); myBounds = Rectangle.Round(freehandPath.GetBounds());
return true;
} Invalidate();
return true;
/// <summary> }
/// Called when the surface finishes drawing the element
/// </summary> /// <summary>
public override void HandleMouseUp(int mouseX, int mouseY) /// Called when the surface finishes drawing the element
{ /// </summary>
// Make sure we don't loose the ending point public override void HandleMouseUp(int mouseX, int mouseY)
if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) >= EditorConfig.FreehandSensitivity) {
{ // Make sure we don't loose the ending point
capturePoints.Add(new Point(mouseX, mouseY)); if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) >= EditorConfig.FreehandSensitivity)
} {
capturePoints.Add(new Point(mouseX, mouseY));
RecalculatePath(); }
}
RecalculatePath();
/// <summary> }
/// Here we recalculate the freehand path by smoothing out the lines with Beziers.
/// </summary> /// <summary>
private void RecalculatePath() /// Here we recalculate the freehand path by smoothing out the lines with Beziers.
{ /// </summary>
// Store the previous path, to dispose it later when we are finished private void RecalculatePath()
var previousFreehandPath = freehandPath; {
var newFreehandPath = new GraphicsPath(); // Store the previous path, to dispose it later when we are finished
var previousFreehandPath = freehandPath;
// Here we can put some cleanup... like losing all the uninteresting points. var newFreehandPath = new GraphicsPath();
if (capturePoints.Count >= 3)
{ // Here we can put some cleanup... like losing all the uninteresting points.
int index = 0; if (capturePoints.Count >= 3)
while ((capturePoints.Count - 1) % 3 != 0) {
{ int index = 0;
// duplicate points, first at 50% than 25% than 75% while ((capturePoints.Count - 1) % 3 != 0)
capturePoints.Insert((int) (capturePoints.Count * PointOffset[index]), capturePoints[(int) (capturePoints.Count * PointOffset[index++])]); {
} // 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.AddBeziers(capturePoints.ToArray());
{ }
newFreehandPath.AddLine(capturePoints[0], capturePoints[1]); else if (capturePoints.Count == 2)
} {
newFreehandPath.AddLine(capturePoints[0], capturePoints[1]);
// Recalculate the bounds }
myBounds = Rectangle.Round(newFreehandPath.GetBounds());
// Recalculate the bounds
// assign myBounds = Rectangle.Round(newFreehandPath.GetBounds());
isRecalculated = true;
freehandPath = newFreehandPath; // assign
isRecalculated = true;
// dispose previous freehandPath = newFreehandPath;
previousFreehandPath?.Dispose();
} // dispose previous
previousFreehandPath?.Dispose();
/// <summary> }
/// Do the drawing of the freehand "stroke"
/// </summary> /// <summary>
/// <param name="graphics"></param> /// Do the drawing of the freehand "stroke"
/// <param name="renderMode"></param> /// </summary>
public override void Draw(Graphics graphics, RenderMode renderMode) /// <param name="graphics"></param>
{ /// <param name="renderMode"></param>
graphics.SmoothingMode = SmoothingMode.HighQuality; public override void Draw(Graphics graphics, RenderMode renderMode)
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; {
graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingQuality = CompositingQuality.HighQuality;
int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR);
using var pen = new Pen(lineColor) int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS);
{ Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR);
Width = lineThickness using var pen = new Pen(lineColor)
}; {
if (!(pen.Width > 0)) Width = lineThickness
{ };
return; if (!(pen.Width > 0))
} {
return;
// Make sure the lines are nicely rounded }
pen.EndCap = LineCap.Round;
pen.StartCap = LineCap.Round; // Make sure the lines are nicely rounded
pen.LineJoin = LineJoin.Round; pen.EndCap = LineCap.Round;
// Move to where we need to draw pen.StartCap = LineCap.Round;
graphics.TranslateTransform(Left, Top); pen.LineJoin = LineJoin.Round;
var currentFreehandPath = freehandPath; // Move to where we need to draw
if (currentFreehandPath != null) graphics.TranslateTransform(Left, Top);
{ var currentFreehandPath = freehandPath;
if (isRecalculated && Selected && renderMode == RenderMode.EDIT) if (currentFreehandPath != null)
{ {
isRecalculated = false; if (isRecalculated && Selected && renderMode == RenderMode.EDIT)
DrawSelectionBorder(graphics, pen, currentFreehandPath); {
} isRecalculated = false;
DrawSelectionBorder(graphics, pen, currentFreehandPath);
graphics.DrawPath(pen, currentFreehandPath); }
}
graphics.DrawPath(pen, currentFreehandPath);
// Move back, otherwise everything is shifted }
graphics.TranslateTransform(-Left, -Top);
} // Move back, otherwise everything is shifted
graphics.TranslateTransform(-Left, -Top);
/// <summary> }
/// Draw a selectionborder around the freehand path
/// </summary> /// <summary>
/// <param name="graphics">Graphics</param> /// Draw a selectionborder around the freehand path
/// <param name="linePen">Pen</param> /// </summary>
/// <param name="path">GraphicsPath</param> /// <param name="graphics">Graphics</param>
protected static void DrawSelectionBorder(Graphics graphics, Pen linePen, GraphicsPath path) /// <param name="linePen">Pen</param>
{ /// <param name="path">GraphicsPath</param>
using var selectionPen = (Pen) linePen.Clone(); protected static void DrawSelectionBorder(Graphics graphics, Pen linePen, GraphicsPath path)
using var selectionPath = (GraphicsPath) path.Clone(); {
selectionPen.Width += 5; using var selectionPen = (Pen) linePen.Clone();
selectionPen.Color = Color.FromArgb(120, Color.LightSeaGreen); using var selectionPath = (GraphicsPath) path.Clone();
graphics.DrawPath(selectionPen, selectionPath); selectionPen.Width += 5;
selectionPath.Widen(selectionPen); selectionPen.Color = Color.FromArgb(120, Color.LightSeaGreen);
selectionPen.DashPattern = new float[] graphics.DrawPath(selectionPen, selectionPath);
{ selectionPath.Widen(selectionPen);
2, 2 selectionPen.DashPattern = new float[]
}; {
selectionPen.Color = Color.LightSeaGreen; 2, 2
selectionPen.Width = 1; };
graphics.DrawPath(selectionPen, selectionPath); selectionPen.Color = Color.LightSeaGreen;
} selectionPen.Width = 1;
graphics.DrawPath(selectionPen, selectionPath);
/// <summary> }
/// Get the bounds in which we have something drawn, plus safety margin, these are not the normal bounds...
/// </summary> /// <summary>
public override NativeRect DrawingBounds /// Get the bounds in which we have something drawn, plus safety margin, these are not the normal bounds...
{ /// </summary>
get public override NativeRect DrawingBounds
{ {
if (!myBounds.IsEmpty) get
{ {
int lineThickness = Math.Max(10, GetFieldValueAsInt(FieldType.LINE_THICKNESS)); if (!myBounds.IsEmpty)
int safetyMargin = 10; {
return new NativeRect(myBounds.Left + Left - (safetyMargin + lineThickness), myBounds.Top + Top - (safetyMargin + lineThickness), int lineThickness = Math.Max(10, GetFieldValueAsInt(FieldType.LINE_THICKNESS));
myBounds.Width + 2 * (lineThickness + safetyMargin), myBounds.Height + 2 * (lineThickness + safetyMargin)); 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); if (_parent?.Image is Image image)
} {
return new NativeRect(0, 0, image.Width, image.Height);
return NativeRect.Empty; }
}
} return NativeRect.Empty;
}
/// <summary> }
/// FreehandContainer are regarded equal if they are of the same type and their paths are equal.
/// </summary> /// <summary>
/// <param name="obj">object</param> /// FreehandContainer are regarded equal if they are of the same type and their paths are equal.
/// <returns>bool</returns> /// </summary>
public override bool Equals(object obj) /// <param name="obj">object</param>
{ /// <returns>bool</returns>
bool ret = false; public override bool Equals(object obj)
if (obj == null || GetType() != obj.GetType()) {
{ bool ret = false;
return false; if (obj == null || GetType() != obj.GetType())
} {
return false;
if (obj is FreehandContainer other && Equals(freehandPath, other.freehandPath)) }
{
ret = true; if (obj is FreehandContainer other && Equals(freehandPath, other.freehandPath))
} {
ret = true;
return ret; }
}
return ret;
public override int GetHashCode() }
{
return freehandPath?.GetHashCode() ?? 0; public override int GetHashCode()
} {
return freehandPath?.GetHashCode() ?? 0;
public override bool ClickableAt(int x, int y) }
{
bool returnValue = base.ClickableAt(x, y); public override bool ClickableAt(int x, int y)
if (returnValue) {
{ bool returnValue = base.ClickableAt(x, y);
int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); if (returnValue)
using var pen = new Pen(Color.White) {
{ int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS);
Width = lineThickness + 10 using var pen = new Pen(Color.White)
}; {
returnValue = freehandPath.IsOutlineVisible(x - Left, y - Top, pen); Width = lineThickness + 10
} };
returnValue = freehandPath.IsOutlineVisible(x - Left, y - Top, pen);
return returnValue; }
}
} return returnValue;
}
}
} }

View file

@ -115,9 +115,9 @@ namespace Greenshot.Editor.Drawing
return false; return false;
} }
protected override ScaleHelper.IDoubleProcessor GetAngleRoundProcessor() protected override IDoubleProcessor GetAngleRoundProcessor()
{ {
return ScaleHelper.LineAngleRoundBehavior.INSTANCE; return LineAngleRoundBehavior.INSTANCE;
} }
} }
} }

View file

@ -39,10 +39,10 @@ namespace Greenshot.Editor.Drawing
[Serializable] [Serializable]
public class SpeechbubbleContainer : TextContainer public class SpeechbubbleContainer : TextContainer
{ {
private NativePoint _initialGripperPoint; private Point _initialGripperPoint;
// Only used for serializing the TargetGripper location // Only used for serializing the TargetGripper location
private NativePoint _storedTargetGripperLocation; private Point _storedTargetGripperLocation;
/// <summary> /// <summary>
/// Store the current location of the target gripper /// Store the current location of the target gripper
@ -120,7 +120,8 @@ namespace Greenshot.Editor.Drawing
int xOffset = leftAligned ? -20 : 20; int xOffset = leftAligned ? -20 : 20;
int yOffset = topAligned ? -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) if (TargetAdorner.Location != newGripperLocation)
{ {

View file

@ -24,6 +24,7 @@ using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Drawing.Text; using System.Drawing.Text;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Windows.Forms;
using Dapplo.Windows.Common.Extensions; using Dapplo.Windows.Common.Extensions;
using Dapplo.Windows.Common.Structs; using Dapplo.Windows.Common.Structs;
using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces;
@ -187,6 +188,8 @@ namespace Greenshot.Editor.Drawing
/// <param name="rm"></param> /// <param name="rm"></param>
public override void Draw(Graphics graphics, RenderMode rm) public override void Draw(Graphics graphics, RenderMode rm)
{ {
if (Width == 0 || Height == 0) { return; }
graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.CompositingQuality = CompositingQuality.HighQuality;
@ -205,9 +208,17 @@ namespace Greenshot.Editor.Drawing
EllipseContainer.DrawEllipse(rect, graphics, rm, 0, Color.Transparent, fillColor, false); 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.GenericSansSerif.Name);
using FontFamily fam = new FontFamily(FontFamily.GenericSansSerif.Name);
using Font font = new Font(fam, fontSize, FontStyle.Bold, GraphicsUnit.Pixel); //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); TextContainer.DrawText(graphics, rect, 0, lineColor, false, _stringFormat, text, font);
} }

View file

@ -28,6 +28,7 @@ using System.Drawing.Imaging;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Serialization.Formatters.Binary;
using System.ServiceModel.Security;
using System.Windows.Forms; using System.Windows.Forms;
using Dapplo.Windows.Common.Extensions; using Dapplo.Windows.Common.Extensions;
using Dapplo.Windows.Common.Structs; using Dapplo.Windows.Common.Structs;
@ -40,6 +41,7 @@ using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Base.Interfaces.Drawing.Adorners; using Greenshot.Base.Interfaces.Drawing.Adorners;
using Greenshot.Editor.Configuration; using Greenshot.Editor.Configuration;
using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Drawing.Fields;
using Greenshot.Editor.Helpers;
using Greenshot.Editor.Memento; using Greenshot.Editor.Memento;
using log4net; using log4net;
@ -722,6 +724,7 @@ namespace Greenshot.Editor.Drawing
try try
{ {
BinaryFormatter binaryRead = new BinaryFormatter(); BinaryFormatter binaryRead = new BinaryFormatter();
binaryRead.Binder = new BinaryFormatterHelper();
IDrawableContainerList loadedElements = (IDrawableContainerList) binaryRead.Deserialize(streamRead); IDrawableContainerList loadedElements = (IDrawableContainerList) binaryRead.Deserialize(streamRead);
loadedElements.Parent = this; loadedElements.Parent = this;
// Make sure the steplabels are sorted according to their number // Make sure the steplabels are sorted according to their number
@ -731,6 +734,10 @@ namespace Greenshot.Editor.Drawing
SelectElements(loadedElements); SelectElements(loadedElements);
FieldAggregator.BindElements(loadedElements); FieldAggregator.BindElements(loadedElements);
} }
catch (SecurityAccessDeniedException)
{
throw;
}
catch (Exception e) catch (Exception e)
{ {
LOG.Error("Error serializing elements from stream.", e); LOG.Error("Error serializing elements from stream.", e);

View file

@ -21,6 +21,7 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.IO;
using Dapplo.Windows.Common.Structs; using Dapplo.Windows.Common.Structs;
using Greenshot.Base.Core; using Greenshot.Base.Core;
using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces;
@ -34,14 +35,32 @@ namespace Greenshot.Editor.Drawing
[Serializable] [Serializable]
public class SvgContainer : VectorGraphicsContainer 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; _svgContent = new MemoryStream();
Size = new Size((int)svgDocument.Width, (int)svgDocument.Height); 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<SvgDocument>(_svgContent);
}
protected override Image ComputeBitmap() protected override Image ComputeBitmap()
{ {
//var image = ImageHelper.CreateEmpty(Width, Height, PixelFormat.Format32bppArgb, Color.Transparent); //var image = ImageHelper.CreateEmpty(Width, Height, PixelFormat.Format32bppArgb, Color.Transparent);

View file

@ -25,6 +25,7 @@ using System.Drawing.Drawing2D;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing; using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing.Adorners;
namespace Greenshot.Editor.Drawing namespace Greenshot.Editor.Drawing
{ {
@ -35,28 +36,55 @@ namespace Greenshot.Editor.Drawing
[Serializable] [Serializable]
public abstract class VectorGraphicsContainer : DrawableContainer public abstract class VectorGraphicsContainer : DrawableContainer
{ {
protected int RotationAngle; private int _rotationAngle;
protected int RotationAngle
{
get => _rotationAngle;
set => _rotationAngle = value;
}
/// <summary> /// <summary>
/// This is the cached version of the bitmap, pre-rendered to save performance /// 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.
/// </summary> /// </summary>
[NonSerialized] private Image _cachedImage; [NonSerialized]
private Image _cachedImage;
/// <summary>
/// Constructor takes care of calling Init
/// </summary>
/// <param name="parent">ISurface</param>
public VectorGraphicsContainer(ISurface parent) : base(parent) public VectorGraphicsContainer(ISurface parent) : base(parent)
{ {
Init(); Init();
} }
/// <summary>
/// Make sure Init is called after deserializing
/// </summary>
/// <param name="streamingContext">StreamingContext</param>
protected override void OnDeserialized(StreamingContext streamingContext) protected override void OnDeserialized(StreamingContext streamingContext)
{ {
base.OnDeserialized(streamingContext); base.OnDeserialized(streamingContext);
Init(); Init();
} }
private void Init() /// <summary>
/// Init is called after creating the instance, and from OnDeserialized
/// This is the place to generate your adorners
/// </summary>
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));
} }
/// <summary> /// <summary>
@ -96,7 +124,10 @@ namespace Greenshot.Editor.Drawing
} }
_cachedImage ??= ComputeBitmap(); _cachedImage ??= ComputeBitmap();
if (_cachedImage == null)
{
return;
}
graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.CompositingQuality = CompositingQuality.HighQuality;
@ -106,9 +137,16 @@ namespace Greenshot.Editor.Drawing
graphics.DrawImage(_cachedImage, Bounds); graphics.DrawImage(_cachedImage, Bounds);
} }
/// <summary>
/// Implement this to compute the new bitmap according to the size of the container
/// </summary>
/// <returns>Image</returns>
protected abstract Image ComputeBitmap(); protected abstract Image ComputeBitmap();
private void ResetCachedBitmap() /// <summary>
/// Dispose of the cached bitmap, forcing the code to regenerate it
/// </summary>
protected void ResetCachedBitmap()
{ {
_cachedImage?.Dispose(); _cachedImage?.Dispose();
_cachedImage = null; _cachedImage = null;

View file

@ -100,8 +100,7 @@ namespace Greenshot.Editor.FileFormatHandlers
bitmap = new Bitmap(infoHeader.Width, infoHeader.Height, bitmap = new Bitmap(infoHeader.Width, infoHeader.Height,
-(int)(infoHeader.SizeImage / infoHeader.Height), -(int)(infoHeader.SizeImage / infoHeader.Height),
infoHeader.BitCount == 32 ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb, infoHeader.BitCount == 32 ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb,
new IntPtr(handle.AddrOfPinnedObject().ToInt32() + infoHeader.OffsetToPixels + IntPtr.Add(handle.AddrOfPinnedObject(), (int)infoHeader.OffsetToPixels + (infoHeader.Height - 1) * (int)(infoHeader.SizeImage / infoHeader.Height))
(infoHeader.Height - 1) * (int)(infoHeader.SizeImage / infoHeader.Height))
); );
} }
catch (Exception ex) catch (Exception ex)

View file

@ -1,89 +1,89 @@
/* /*
* Greenshot - a free and open source screenshot tool * Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
* *
* For more information see: https://getgreenshot.org/ * For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or * the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing; using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Base.Interfaces.Plugin; using Greenshot.Base.Interfaces.Plugin;
using Greenshot.Editor.Drawing; using Greenshot.Editor.Drawing;
using log4net; using log4net;
using Svg; using Svg;
namespace Greenshot.Editor.FileFormatHandlers namespace Greenshot.Editor.FileFormatHandlers
{ {
/// <summary> /// <summary>
/// This handled the loading of SVG images to the editor /// This handled the loading of SVG images to the editor
/// </summary> /// </summary>
public class SvgFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler public class SvgFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler
{ {
private static readonly ILog Log = LogManager.GetLogger(typeof(SvgFileFormatHandler)); private static readonly ILog Log = LogManager.GetLogger(typeof(SvgFileFormatHandler));
private readonly IReadOnlyCollection<string> _ourExtensions = new[] { ".svg" }; private readonly IReadOnlyCollection<string> _ourExtensions = new[] { ".svg" };
public SvgFileFormatHandler() public SvgFileFormatHandler()
{ {
SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions; SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions;
SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions; SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions;
} }
public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap)
{ {
var svgDocument = SvgDocument.Open<SvgDocument>(stream); var svgDocument = SvgDocument.Open<SvgDocument>(stream);
try try
{ {
bitmap = svgDocument.Draw(); bitmap = svgDocument.Draw();
return true; return true;
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error("Can't load SVG", ex); Log.Error("Can't load SVG", ex);
} }
bitmap = null; bitmap = null;
return false; return false;
} }
public override bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) public override bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null)
{ {
// TODO: Implement this // TODO: Implement this
return false; return false;
} }
public override IEnumerable<IDrawableContainer> LoadDrawablesFromStream(Stream stream, string extension, ISurface parent = null) public override IEnumerable<IDrawableContainer> LoadDrawablesFromStream(Stream stream, string extension, ISurface parent = null)
{ {
SvgDocument svgDocument = null; SvgContainer svgContainer = null;
try try
{ {
svgDocument = SvgDocument.Open<SvgDocument>(stream); svgContainer = new SvgContainer(stream, parent);
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error("Can't load SVG", ex); Log.Error("Can't load SVG", ex);
} }
if (svgDocument != null) if (svgContainer != null)
{ {
yield return new SvgContainer(svgDocument, parent); yield return svgContainer;
} }
} }
} }
} }

View file

@ -1023,6 +1023,9 @@ namespace Greenshot.Editor.Forms
case Keys.C: case Keys.C:
BtnCropClick(sender, e); BtnCropClick(sender, e);
break; break;
case Keys.Z:
BtnResizeClick(sender, e);
break;
} }
} }
else if (e.Modifiers.Equals(Keys.Control)) else if (e.Modifiers.Equals(Keys.Control))

View file

@ -2,6 +2,10 @@
<PropertyGroup> <PropertyGroup>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="Languages\language*.xml"> <None Include="Languages\language*.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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
{
/// <summary>
/// This helps to map the serialization of the old .greenshot file to the newer.
/// It also prevents misuse.
/// </summary>
internal class BinaryFormatterHelper : SerializationBinder
{
private static readonly ILog LOG = LogManager.GetLogger(typeof(BinaryFormatterHelper));
private static readonly IDictionary<string, Type> TypeMapper = new Dictionary<string, Type>
{
{"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<IFieldHolder>)},
{"System.Collections.Generic.List`1[[Greenshot.Base.Interfaces.Drawing.IField", typeof(List<IField>)},
{"System.Collections.Generic.List`1[[System.Drawing.Point", typeof(List<System.Drawing.Point>)},
{"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) },
};
/// <summary>
/// Do the type mapping
/// </summary>
/// <param name="assemblyName">Assembly for the type that was serialized</param>
/// <param name="typeName">Type that was serialized</param>
/// <returns>Type which was mapped</returns>
/// <exception cref="SecurityAccessDeniedException">If something smells fishy</exception>
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}");
}
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
namespace Greenshot.Editor.Helpers
{
public interface IDoubleProcessor
{
double Process(double d);
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
namespace Greenshot.Editor.Helpers
{
public interface IHaveScaleOptions
{
ScaleOptions GetScaleOptions();
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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;
}
}
}

View file

@ -33,32 +33,13 @@ namespace Greenshot.Editor.Helpers
/// </summary> /// </summary>
public static class ScaleHelper public static class ScaleHelper
{ {
[Flags]
public enum ScaleOptions
{
/// <summary>
/// Default scale behavior.
/// </summary>
Default = 0x00,
/// <summary>
/// Scale a rectangle in two our four directions, mirrored at it's center coordinates
/// </summary>
Centered = 0x01,
/// <summary>
/// Scale a rectangle maintaining it's aspect ratio
/// </summary>
Rational = 0x02
}
/// <summary> /// <summary>
/// calculates the Size an element must be resized to, in order to fit another element, keeping aspect ratio /// calculates the Size an element must be resized to, in order to fit another element, keeping aspect ratio
/// </summary> /// </summary>
/// <param name="currentSize">the size of the element to be resized</param> /// <param name="currentSize">the size of the element to be resized</param>
/// <param name="targetSize">the target size of the element</param> /// <param name="targetSize">the target size of the element</param>
/// <param name="crop">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)</param> /// <param name="crop">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)</param>
/// <returns>a new SizeF object indicating the width and height the element should be scaled to</returns> /// <returns>NativeSizeFloat object indicating the width and height the element should be scaled to</returns>
public static NativeSizeFloat GetScaledSize(NativeSizeFloat currentSize, NativeSizeFloat targetSize, bool crop) public static NativeSizeFloat GetScaledSize(NativeSizeFloat currentSize, NativeSizeFloat targetSize, bool crop)
{ {
float wFactor = targetSize.Width / currentSize.Width; float wFactor = targetSize.Width / currentSize.Width;
@ -71,10 +52,10 @@ namespace Greenshot.Editor.Helpers
/// <summary> /// <summary>
/// calculates the position of an element depending on the desired alignment within a RectangleF /// calculates the position of an element depending on the desired alignment within a RectangleF
/// </summary> /// </summary>
/// <param name="currentRect">the bounds of the element to be aligned</param> /// <param name="currentRect">NativeRectFloat the bounds of the element to be aligned</param>
/// <param name="targetRect">the rectangle reference for alignment of the element</param> /// <param name="targetRect">NativeRectFloat with the rectangle for alignment of the element</param>
/// <param name="alignment">the System.Drawing.ContentAlignment value indicating how the element is to be aligned should the width or height differ from targetSize</param> /// <param name="alignment">the System.Drawing.ContentAlignment value indicating how the element is to be aligned should the width or height differ from targetSize</param>
/// <returns>a new RectangleF object with Location aligned aligned to targetRect</returns> /// <returns>NativeRectFloat object with Location aligned aligned to targetRect</returns>
public static NativeRectFloat GetAlignedRectangle(NativeRectFloat currentRect, NativeRectFloat targetRect, ContentAlignment alignment) public static NativeRectFloat GetAlignedRectangle(NativeRectFloat currentRect, NativeRectFloat targetRect, ContentAlignment alignment)
{ {
var newRect = new NativeRectFloat(targetRect.Location, currentRect.Size); var newRect = new NativeRectFloat(targetRect.Location, currentRect.Size);
@ -96,9 +77,9 @@ namespace Greenshot.Editor.Helpers
/// <summary> /// <summary>
/// Calculates target size of a given rectangle scaled by dragging one of its handles (corners) /// Calculates target size of a given rectangle scaled by dragging one of its handles (corners)
/// </summary> /// </summary>
/// <param name="originalRectangle">bounds of the current rectangle</param> /// <param name="originalRectangle">NativeRectFloat bounds of the current rectangle</param>
/// <param name="resizeHandlePosition">position of the handle/gripper being used for resized, see constants in Gripper.cs, e.g. Gripper.POSITION_TOP_LEFT</param> /// <param name="resizeHandlePosition">Positions with the position of the handle/gripper being used for resized, see constants in Gripper.cs, e.g. Gripper.POSITION_TOP_LEFT</param>
/// <param name="resizeHandleCoords">coordinates of the used handle/gripper</param> /// <param name="resizeHandleCoords">NativePointFloat coordinates of the used handle/gripper</param>
/// <param name="options">ScaleOptions to use when scaling</param> /// <param name="options">ScaleOptions to use when scaling</param>
/// <returns>NativeRectFloat scaled originalRectangle</returns> /// <returns>NativeRectFloat scaled originalRectangle</returns>
public static NativeRectFloat Scale(NativeRectFloat originalRectangle, Positions resizeHandlePosition, NativePointFloat resizeHandleCoords, ScaleOptions? options) public static NativeRectFloat Scale(NativeRectFloat originalRectangle, Positions resizeHandlePosition, NativePointFloat resizeHandleCoords, ScaleOptions? options)
@ -134,9 +115,9 @@ namespace Greenshot.Editor.Helpers
/// <summary> /// <summary>
/// Calculates target size of a given rectangle scaled by dragging one of its handles (corners) /// Calculates target size of a given rectangle scaled by dragging one of its handles (corners)
/// </summary> /// </summary>
/// <param name="originalRectangle">bounds of the current rectangle</param> /// <param name="originalRectangle">NativeRectFloat bounds of the current rectangle</param>
/// <param name="resizeHandlePosition">position of the handle/gripper being used for resized, see constants in Gripper.cs, e.g. Gripper.POSITION_TOP_LEFT</param> /// <param name="resizeHandlePosition">Positions with the position of the handle/gripper being used for resized, see constants in Gripper.cs, e.g. Gripper.POSITION_TOP_LEFT</param>
/// <param name="resizeHandleCoords">coordinates of the used handle/gripper</param> /// <param name="resizeHandleCoords">NativePointFloat with coordinates of the used handle/gripper</param>
/// <returns>NativeRectFloat with the scaled originalRectangle</returns> /// <returns>NativeRectFloat with the scaled originalRectangle</returns>
private static NativeRectFloat Scale(NativeRectFloat originalRectangle, Positions resizeHandlePosition, NativePointFloat resizeHandleCoords) 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 /// To avoid objects growing near infinity unexpectedly in certain combinations, the adjustment will choose the
/// option resulting in the smaller rectangle. /// option resulting in the smaller rectangle.
/// </summary> /// </summary>
/// <param name="originalRectangle">bounds of the current rectangle</param> /// <param name="originalRectangle">NativeRectFloat with the bounds of the current rectangle</param>
/// <param name="resizeHandlePosition">position of the handle/gripper being used for resized, see Position</param> /// <param name="resizeHandlePosition">Positions with the position of the handle/gripper being used for resized, see Position</param>
/// <param name="resizeHandleCoords">coordinates of the used handle/gripper</param> /// <param name="resizeHandleCoords">NativePointFloat with coordinates of the used handle/gripper</param>
/// <returns>NativePointFloat with the adjusted coordinates</returns> /// <returns>NativePointFloat with the adjusted coordinates</returns>
private static NativePointFloat AdjustCoordsForRationalScale(NativeRectFloat originalRectangle, Positions resizeHandlePosition, NativePointFloat resizeHandleCoords) private static NativePointFloat AdjustCoordsForRationalScale(NativeRectFloat originalRectangle, Positions resizeHandlePosition, NativePointFloat resizeHandleCoords)
{ {
@ -203,7 +184,7 @@ namespace Greenshot.Editor.Helpers
/// </summary> /// </summary>
/// <param name="originalSize">NativeSizeFloat to be considered for keeping aspect ratio</param> /// <param name="originalSize">NativeSizeFloat to be considered for keeping aspect ratio</param>
/// <param name="selectedSize">NativeSizeFloat selection size (i.e. the size we'd produce if we wouldn't keep aspect ratio)</param> /// <param name="selectedSize">NativeSizeFloat selection size (i.e. the size we'd produce if we wouldn't keep aspect ratio)</param>
/// <returns></returns> /// <returns>NativeSizeFloat</returns>
private static NativeSizeFloat GetNewSizeForRationalScale(NativeSizeFloat originalSize, NativeSizeFloat selectedSize) private static NativeSizeFloat GetNewSizeForRationalScale(NativeSizeFloat originalSize, NativeSizeFloat selectedSize)
{ {
NativeSizeFloat newSize = selectedSize; NativeSizeFloat newSize = selectedSize;
@ -221,29 +202,28 @@ namespace Greenshot.Editor.Helpers
{ {
// scaled rectangle (ratio) would be taller than original // scaled rectangle (ratio) would be taller than original
// keep width and tweak height to maintain aspect ratio // 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; return newSize;
} }
public static NativeRectFloat Scale(NativeRect boundsBeforeResize, int cursorX, int cursorY) /// <summary>
/// Scale the boundsBeforeResize with the specified position and new location, using the angle angleRoundBehavior
/// </summary>
/// <param name="boundsBeforeResize">NativeRect</param>
/// <param name="cursorX">int</param>
/// <param name="cursorY">int</param>
/// <param name="angleRoundBehavior">IDoubleProcessor</param>
/// <param name="scaleOptions">ScaleOptions</param>
/// <returns>NativeRectFloat</returns>
public static NativeRectFloat Scale(NativeRect boundsBeforeResize, int cursorX, int cursorY, IDoubleProcessor angleRoundBehavior, ScaleOptions? scaleOptions = null)
{ {
return Scale(boundsBeforeResize, cursorX, cursorY, null); scaleOptions ??= GetScaleOptions();
}
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();
NativeRectFloat result = boundsBeforeResize; NativeRectFloat result = boundsBeforeResize;
bool rationalScale = (opts & ScaleOptions.Rational) == ScaleOptions.Rational; bool rationalScale = (scaleOptions & ScaleOptions.Rational) == ScaleOptions.Rational;
bool centeredScale = (opts & ScaleOptions.Centered) == ScaleOptions.Centered; bool centeredScale = (scaleOptions & ScaleOptions.Centered) == ScaleOptions.Centered;
if (rationalScale) if (rationalScale)
{ {
@ -281,38 +261,5 @@ namespace Greenshot.Editor.Helpers
if (maintainAspectRatio) opts |= ScaleOptions.Rational; if (maintainAspectRatio) opts |= ScaleOptions.Rational;
return opts; 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;
}
}
} }
} }

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
using System;
namespace Greenshot.Editor.Helpers
{
[Flags]
public enum ScaleOptions
{
/// <summary>
/// Default scale behavior.
/// </summary>
Default = 0x00,
/// <summary>
/// Scale a rectangle in two our four directions, mirrored at it's center coordinates
/// </summary>
Centered = 0x01,
/// <summary>
/// Scale a rectangle maintaining it's aspect ratio
/// </summary>
Rational = 0x02
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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;
}
}
}

View file

@ -21,6 +21,7 @@
using System; using System;
using System.Windows.Forms; using System.Windows.Forms;
using Greenshot.Base.Core;
using Greenshot.Base.Core.Enums; using Greenshot.Base.Core.Enums;
using Greenshot.Base.IniFile; using Greenshot.Base.IniFile;
using Greenshot.Plugin.Box.Forms; using Greenshot.Plugin.Box.Forms;
@ -75,5 +76,20 @@ namespace Greenshot.Plugin.Box
return false; return false;
} }
/// <summary>
/// Upgrade certain values
/// </summary>
public override void AfterLoad()
{
var coreConfiguration = IniConfig.GetIniSection<CoreConfiguration>();
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;
}
} }
} }

View file

@ -1,4 +1,4 @@
/* /*
* Greenshot - a free and open source screenshot tool * Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
* *

View file

@ -1,4 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="Languages\language*.xml"> <None Include="Languages\language*.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View file

@ -21,11 +21,13 @@
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Greenshot.Base.Interfaces.Plugin;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyDescription("A plugin to upload images to Box")] [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. // 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. // If you need to expose a type to COM, use [ComVisible(true)] on that type.

View file

@ -1,4 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="Languages\language*.xml"> <None Include="Languages\language*.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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)]

View file

@ -21,6 +21,7 @@
using System; using System;
using System.Windows.Forms; using System.Windows.Forms;
using Greenshot.Base.Core;
using Greenshot.Base.Core.Enums; using Greenshot.Base.Core.Enums;
using Greenshot.Base.IniFile; using Greenshot.Base.IniFile;
using Greenshot.Plugin.Dropbox.Forms; using Greenshot.Plugin.Dropbox.Forms;
@ -69,5 +70,20 @@ namespace Greenshot.Plugin.Dropbox
return false; return false;
} }
/// <summary>
/// Upgrade certain values
/// </summary>
public override void AfterLoad()
{
var coreConfiguration = IniConfig.GetIniSection<CoreConfiguration>();
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;
}
} }
} }

View file

@ -1,4 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="Languages\language*.xml"> <None Include="Languages\language*.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View file

@ -21,11 +21,13 @@
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Greenshot.Base.Interfaces.Plugin;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyDescription("A plugin to upload images to Dropbox")] [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. // 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. // If you need to expose a type to COM, use [ComVisible(true)] on that type.

View file

@ -1,4 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="Languages\language*.xml"> <None Include="Languages\language*.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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)]

View file

@ -20,6 +20,7 @@
*/ */
using System.Windows.Forms; using System.Windows.Forms;
using Greenshot.Base.Core;
using Greenshot.Base.Core.Enums; using Greenshot.Base.Core.Enums;
using Greenshot.Base.IniFile; using Greenshot.Base.IniFile;
using Greenshot.Plugin.Flickr.Forms; using Greenshot.Plugin.Flickr.Forms;
@ -86,5 +87,21 @@ namespace Greenshot.Plugin.Flickr
return false; return false;
} }
/// <summary>
/// Upgrade certain values
/// </summary>
public override void AfterLoad()
{
var coreConfiguration = IniConfig.GetIniSection<CoreConfiguration>();
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;
}
} }
} }

View file

@ -1,4 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="Languages\language*.xml"> <None Include="Languages\language*.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View file

@ -21,11 +21,13 @@
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Greenshot.Base.Interfaces.Plugin;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyDescription("A plugin to upload images to Flickr")] [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. // 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. // If you need to expose a type to COM, use [ComVisible(true)] on that type.

View file

@ -1,4 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="Languages\language*.xml"> <None Include="Languages\language*.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View file

@ -21,12 +21,17 @@
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Greenshot.Base.Interfaces.Plugin;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyDescription("A plugin to upload images to GooglePhotos")] [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. // 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. // If you need to expose a type to COM, use [ComVisible(true)] on that type.
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View file

@ -1,4 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="Languages\language*.xml"> <None Include="Languages\language*.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View file

@ -22,6 +22,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Windows.Forms; using System.Windows.Forms;
using Greenshot.Base.Core;
using Greenshot.Base.Core.Enums; using Greenshot.Base.Core.Enums;
using Greenshot.Base.IniFile; using Greenshot.Base.IniFile;
using Greenshot.Plugin.Imgur.Forms; using Greenshot.Plugin.Imgur.Forms;
@ -84,6 +85,22 @@ namespace Greenshot.Plugin.Imgur
public Dictionary<string, ImgurInfo> runtimeImgurHistory = new Dictionary<string, ImgurInfo>(); public Dictionary<string, ImgurInfo> runtimeImgurHistory = new Dictionary<string, ImgurInfo>();
public int Credits { get; set; } public int Credits { get; set; }
/// <summary>
/// Upgrade certain values
/// </summary>
public override void AfterLoad()
{
var coreConfiguration = IniConfig.GetIniSection<CoreConfiguration>();
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;
}
/// <summary> /// <summary>
/// Supply values we can't put as defaults /// Supply values we can't put as defaults
/// </summary> /// </summary>
@ -92,7 +109,7 @@ namespace Greenshot.Plugin.Imgur
public override object GetDefault(string property) => public override object GetDefault(string property) =>
property switch property switch
{ {
"ImgurUploadHistory" => new Dictionary<string, string>(), nameof(ImgurUploadHistory) => new Dictionary<string, string>(),
_ => null _ => null
}; };

View file

@ -21,11 +21,13 @@
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Greenshot.Base.Interfaces.Plugin;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyDescription("A plugin to upload images to Imgur")] [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. // 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. // If you need to expose a type to COM, use [ComVisible(true)] on that type.

View file

@ -1,4 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="Languages\language*.xml"> <None Include="Languages\language*.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@ -6,7 +10,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Greenshot.Base\Greenshot.Base.csproj" /> <ProjectReference Include="..\Greenshot.Base\Greenshot.Base.csproj" />
<PackageReference Include="Dapplo.Jira" version="1.1.44" /> <PackageReference Include="Dapplo.Jira" version="1.1.46" />
<PackageReference Include="Dapplo.Jira.SvgWinForms" Version="1.1.44" /> <PackageReference Include="Dapplo.Jira.SvgWinForms" Version="1.1.46" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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)]

View file

@ -42,7 +42,8 @@ namespace Greenshot.Plugin.Office.Destinations
static ExcelDestination() static ExcelDestination()
{ {
ExePath = PluginUtils.GetExePath("EXCEL.EXE"); ExePath = OfficeUtils.GetOfficeExePath("EXCEL.EXE") ?? PluginUtils.GetExePath("EXCEL.EXE");
if (ExePath != null && File.Exists(ExePath)) if (ExePath != null && File.Exists(ExePath))
{ {
WindowDetails.AddProcessToExcludeFromFreeze("excel"); WindowDetails.AddProcessToExcludeFromFreeze("excel");

View file

@ -41,7 +41,7 @@ namespace Greenshot.Plugin.Office.Destinations
static OneNoteDestination() static OneNoteDestination()
{ {
exePath = PluginUtils.GetExePath("ONENOTE.EXE"); exePath = OfficeUtils.GetOfficeExePath("ONENOTE.EXE") ?? PluginUtils.GetExePath("ONENOTE.EXE");
if (exePath != null && File.Exists(exePath)) if (exePath != null && File.Exists(exePath))
{ {
WindowDetails.AddProcessToExcludeFromFreeze("onenote"); WindowDetails.AddProcessToExcludeFromFreeze("onenote");

View file

@ -58,8 +58,7 @@ namespace Greenshot.Plugin.Office.Destinations
{ {
IsActiveFlag = true; IsActiveFlag = true;
} }
ExePath = OfficeUtils.GetOfficeExePath("OUTLOOK.EXE") ?? PluginUtils.GetExePath("OUTLOOK.EXE");
ExePath = PluginUtils.GetExePath("OUTLOOK.EXE");
if (ExePath != null && File.Exists(ExePath)) if (ExePath != null && File.Exists(ExePath))
{ {
WindowDetails.AddProcessToExcludeFromFreeze("outlook"); WindowDetails.AddProcessToExcludeFromFreeze("outlook");

View file

@ -45,7 +45,7 @@ namespace Greenshot.Plugin.Office.Destinations
static PowerpointDestination() static PowerpointDestination()
{ {
ExePath = PluginUtils.GetExePath("POWERPNT.EXE"); ExePath = OfficeUtils.GetOfficeExePath("POWERPNT.EXE") ?? PluginUtils.GetExePath("POWERPNT.EXE");
if (ExePath != null && File.Exists(ExePath)) if (ExePath != null && File.Exists(ExePath))
{ {
WindowDetails.AddProcessToExcludeFromFreeze("powerpnt"); WindowDetails.AddProcessToExcludeFromFreeze("powerpnt");

View file

@ -46,7 +46,7 @@ namespace Greenshot.Plugin.Office.Destinations
static WordDestination() static WordDestination()
{ {
ExePath = PluginUtils.GetExePath("WINWORD.EXE"); ExePath = OfficeUtils.GetOfficeExePath("WINWORD.EXE") ?? PluginUtils.GetExePath("WINWORD.EXE");
if (ExePath != null && !File.Exists(ExePath)) if (ExePath != null && !File.Exists(ExePath))
{ {
ExePath = null; ExePath = null;
@ -118,7 +118,7 @@ namespace Greenshot.Plugin.Office.Destinations
if (!manuallyInitiated) if (!manuallyInitiated)
{ {
var documents = _wordExporter.GetWordDocuments().ToList(); var documents = _wordExporter.GetWordDocuments().ToList();
if (documents != null && documents.Count > 0) if (documents is { Count: > 0 })
{ {
var destinations = new List<IDestination> var destinations = new List<IDestination>
{ {

View file

@ -1,4 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="Languages\language*.xml"> <None Include="Languages\language*.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@ -78,23 +82,23 @@
</ItemGroup> </ItemGroup>
</Target> </Target>
<ItemGroup> <ItemGroup>
<PackageReference Include="Interop.Microsoft.Office.Interop.OneNote" Version="1.1.0"> <PackageReference Include="Interop.Microsoft.Office.Interop.OneNote" Version="1.1.0.2">
<EmbedInteropTypes>true</EmbedInteropTypes> <EmbedInteropTypes>true</EmbedInteropTypes>
<ExcludeAssets>runtime</ExcludeAssets> <ExcludeAssets>runtime</ExcludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Office.Interop.Excel" Version="15.0.4795.1000"> <PackageReference Include="Microsoft.Office.Interop.Excel" Version="15.0.4795.1001">
<EmbedInteropTypes>true</EmbedInteropTypes> <EmbedInteropTypes>true</EmbedInteropTypes>
<ExcludeAssets>runtime</ExcludeAssets> <ExcludeAssets>runtime</ExcludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Office.Interop.Outlook" Version="15.0.4797.1003"> <PackageReference Include="Microsoft.Office.Interop.Outlook" Version="15.0.4797.1004">
<EmbedInteropTypes>true</EmbedInteropTypes> <EmbedInteropTypes>true</EmbedInteropTypes>
<ExcludeAssets>runtime</ExcludeAssets> <ExcludeAssets>runtime</ExcludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Office.Interop.PowerPoint" Version="15.0.4420.1017"> <PackageReference Include="Microsoft.Office.Interop.PowerPoint" Version="15.0.4420.1018">
<EmbedInteropTypes>true</EmbedInteropTypes> <EmbedInteropTypes>true</EmbedInteropTypes>
<ExcludeAssets>runtime</ExcludeAssets> <ExcludeAssets>runtime</ExcludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Office.Interop.Word" Version="15.0.4797.1003"> <PackageReference Include="Microsoft.Office.Interop.Word" Version="15.0.4797.1004">
<EmbedInteropTypes>true</EmbedInteropTypes> <EmbedInteropTypes>true</EmbedInteropTypes>
<ExcludeAssets>runtime</ExcludeAssets> <ExcludeAssets>runtime</ExcludeAssets>
</PackageReference> </PackageReference>

View file

@ -0,0 +1,45 @@
using System.Linq;
using Microsoft.Win32;
namespace Greenshot.Plugin.Office
{
/// <summary>
/// A small utility class for helping with office
/// </summary>
internal static class OfficeUtils
{
private static readonly string[] OfficeRootKeys = { @"SOFTWARE\Microsoft\Office", @"SOFTWARE\WOW6432Node\Microsoft\Office" };
/// <summary>
/// Get the path to the office exe
/// </summary>
/// <param name="exeName">Name of the office executable</param>
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;
}
}
}

View file

@ -21,11 +21,13 @@
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Greenshot.Base.Interfaces.Plugin;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyDescription("A plugin to export images to Office applications")] [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. // 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. // If you need to expose a type to COM, use [ComVisible(true)] on that type.

View file

@ -1,4 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="Languages\language*.xml"> <None Include="Languages\language*.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View file

@ -21,12 +21,14 @@
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Greenshot.Base.Interfaces.Plugin;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyDescription("A plugin to upload images to Photobucket")] [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. // 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. // If you need to expose a type to COM, use [ComVisible(true)] on that type.

View file

@ -1,4 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="Languages\language*.xml"> <None Include="Languages\language*.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Greenshot.Base.Interfaces.Plugin;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyDescription("A plug-in for Windows 10 only functionality")] [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 // 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 // to COM components. If you need to access a type in this assembly from

View file

@ -95,7 +95,17 @@ namespace Greenshot.Plugin.Win10
} }
// Prepare the toast notifier. Be sure to specify the AppUserModelId on your application's shortcut! // 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/ // 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 try

View file

@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16 # Visual Studio Version 17
VisualStudioVersion = 16.0.29728.190 VisualStudioVersion = 17.7.34009.444
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Greenshot", "Greenshot\Greenshot.csproj", "{CD642BF4-D815-4D67-A0B5-C69F0B8231AF}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Greenshot", "Greenshot\Greenshot.csproj", "{CD642BF4-D815-4D67-A0B5-C69F0B8231AF}"
ProjectSection(ProjectDependencies) = postProject ProjectSection(ProjectDependencies) = postProject
@ -48,6 +48,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
..\azure-pipelines.yml = ..\azure-pipelines.yml ..\azure-pipelines.yml = ..\azure-pipelines.yml
Directory.Build.props = Directory.Build.props Directory.Build.props = Directory.Build.props
Directory.Build.targets = Directory.Build.targets Directory.Build.targets = Directory.Build.targets
..\.github\workflows\release.yml = ..\.github\workflows\release.yml
EndProjectSection EndProjectSection
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Greenshot.Editor", "Greenshot.Editor\Greenshot.Editor.csproj", "{148D3C8B-D6EC-4A7D-80E9-243A81F19DD2}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Greenshot.Editor", "Greenshot.Editor\Greenshot.Editor.csproj", "{148D3C8B-D6EC-4A7D-80E9-243A81F19DD2}"

View file

@ -11,8 +11,7 @@
<loadFromRemoteSources enabled="true" /> <loadFromRemoteSources enabled="true" />
<relativeBindForResources enabled="true" /> <relativeBindForResources enabled="true" />
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="Addons" /> <probing privatePath="Addons,App\Greenshot" />
<probing privatePath="App\Greenshot" />
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
</configuration> </configuration>

View file

@ -197,7 +197,7 @@ namespace Greenshot.Forms
private void CaptureForm_Resize(object sender, EventArgs e) private void CaptureForm_Resize(object sender, EventArgs e)
{ {
Log.DebugFormat("Resize was called, new size: {0}", this.Bounds); 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 // We have the correct size
return; return;
@ -425,9 +425,11 @@ namespace Greenshot.Forms
else if (_captureRect.Height > 0 && _captureRect.Width > 0) else if (_captureRect.Height > 0 && _captureRect.Width > 0)
{ {
// correct the GUI width to real width if Region mode // 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 // Go and process the capture

View file

@ -9,6 +9,10 @@
<EmbeddedResourceUseDependentUponConvention>false</EmbeddedResourceUseDependentUponConvention> <EmbeddedResourceUseDependentUponConvention>false</EmbeddedResourceUseDependentUponConvention>
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems> <EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="Languages\language*.xml"> <None Include="Languages\language*.xml">
@ -17,7 +21,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Tools.InnoSetup" version="6.2.0" GeneratePathProperty="true" /> <PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.13.9" />
<PackageReference Include="Tools.InnoSetup" version="6.2.1" GeneratePathProperty="true" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -75,6 +80,7 @@
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Release'"> <Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Release'">
<SetEnvironmentVariableTask Name="BuildVersionSimple" Value="$(BuildVersionSimple)" /> <SetEnvironmentVariableTask Name="BuildVersionSimple" Value="$(BuildVersionSimple)" />
<SetEnvironmentVariableTask Name="AssemblyInformationalVersion" Value="$(AssemblyInformationalVersion)" /> <SetEnvironmentVariableTask Name="AssemblyInformationalVersion" Value="$(AssemblyInformationalVersion)" />
<Exec Command="if not &quot;$(CertumThumbprint)&quot; == &quot;&quot; signtool.exe sign /sha1 &quot;$(CertumThumbprint)&quot; /tr http://time.certum.pl /td sha256 /fd sha256 /v &quot;$(OutDir)Greenshot.exe&quot;" />
<Exec Command="$(PkgTools_InnoSetup)\tools\ISCC.exe $(SolutionDir)\..\installer\innosetup\setup.iss" /> <Exec Command="$(PkgTools_InnoSetup)\tools\ISCC.exe $(SolutionDir)\..\installer\innosetup\setup.iss" />
</Target> </Target>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'"> <PropertyGroup Condition="'$(Configuration)' == 'Debug'">

View file

@ -564,14 +564,6 @@ namespace Greenshot.Helpers
{ {
_windows = new List<WindowDetails>(); _windows = new List<WindowDetails>();
// 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) Thread getWindowDetailsThread = new Thread(RetrieveWindowDetails)
{ {
Name = "Retrieve window details", Name = "Retrieve window details",
@ -984,7 +976,7 @@ namespace Greenshot.Helpers
else else
{ {
// Change to GDI, if allowed // Change to GDI, if allowed
if (!windowToCapture.IsMetroApp && WindowCapture.IsGdiAllowed(process)) if (WindowCapture.IsGdiAllowed(process))
{ {
if (!dwmEnabled && IsWpf(process)) if (!dwmEnabled && IsWpf(process))
{ {
@ -1000,7 +992,7 @@ namespace Greenshot.Helpers
// Change to DWM, if enabled and allowed // Change to DWM, if enabled and allowed
if (dwmEnabled) if (dwmEnabled)
{ {
if (windowToCapture.IsMetroApp || WindowCapture.IsDwmAllowed(process)) if (WindowCapture.IsDwmAllowed(process))
{ {
windowCaptureMode = WindowCaptureMode.Aero; windowCaptureMode = WindowCaptureMode.Aero;
} }
@ -1009,7 +1001,7 @@ namespace Greenshot.Helpers
} }
else if (windowCaptureMode == WindowCaptureMode.Aero || windowCaptureMode == WindowCaptureMode.AeroTransparent) else if (windowCaptureMode == WindowCaptureMode.Aero || windowCaptureMode == WindowCaptureMode.AeroTransparent)
{ {
if (!dwmEnabled || (!windowToCapture.IsMetroApp && !WindowCapture.IsDwmAllowed(process))) if (!dwmEnabled || !WindowCapture.IsDwmAllowed(process))
{ {
// Take default screen // Take default screen
windowCaptureMode = WindowCaptureMode.Screen; windowCaptureMode = WindowCaptureMode.Screen;
@ -1115,7 +1107,7 @@ namespace Greenshot.Helpers
break; break;
case WindowCaptureMode.Aero: case WindowCaptureMode.Aero:
case WindowCaptureMode.AeroTransparent: case WindowCaptureMode.AeroTransparent:
if (windowToCapture.IsMetroApp || WindowCapture.IsDwmAllowed(process)) if (WindowCapture.IsDwmAllowed(process))
{ {
tmpCapture = windowToCapture.CaptureDwmWindow(captureForWindow, windowCaptureMode, isAutoMode); tmpCapture = windowToCapture.CaptureDwmWindow(captureForWindow, windowCaptureMode, isAutoMode);
} }

View file

@ -219,17 +219,14 @@ namespace Greenshot.Helpers
{ {
var assembly = Assembly.LoadFrom(pluginFile); var assembly = Assembly.LoadFrom(pluginFile);
var assemblyName = assembly.GetName().Name; if (IsPluginExcludedByConfig(assembly, pluginFile) )
var pluginEntryName = $"{assemblyName}.{assemblyName.Replace("Greenshot.Plugin.", string.Empty)}Plugin";
var pluginEntryType = assembly.GetType(pluginEntryName, false, true);
if (CoreConfig.ExcludePlugins != null && CoreConfig.ExcludePlugins.Contains(pluginEntryName))
{ {
Log.WarnFormat("Exclude list: {0}", string.Join(",", CoreConfig.ExcludePlugins));
Log.WarnFormat("Skipping the excluded plugin {0} with version {1} from {2}", pluginEntryName, assembly.GetName().Version, pluginFile);
continue; 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); var plugin = (IGreenshotPlugin) Activator.CreateInstance(pluginEntryType);
if (plugin != null) if (plugin != null)
@ -255,5 +252,54 @@ namespace Greenshot.Helpers
} }
} }
} }
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// Retrieves the plugin identifier for the specified assembly.
/// </summary>
private string GetPluginIdentifier(Assembly assembly, string pluginFile)
{
// Try to find PluginIdentifierAttribute
var attribute = assembly
.GetCustomAttributes<AssemblyPluginIdentifierAttribute>()
.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;
}
} }
} }

View file

@ -305,7 +305,7 @@ Malgrat això, encara es poden utilitzar totes les característiques de Greensho
<resource name="editor_counter">Afegeix un comptador (I)</resource> <resource name="editor_counter">Afegeix un comptador (I)</resource>
<resource name="editor_speechbubble">Afegeix una bafarada (S)</resource> <resource name="editor_speechbubble">Afegeix una bafarada (S)</resource>
<resource name="editor_resize">Canvia la mida</resource> <resource name="editor_resize">Canvia la mida (Z)</resource>
<resource name="editor_resize_settings">Configuració del canvi de mida</resource> <resource name="editor_resize_settings">Configuració del canvi de mida</resource>
<resource name="editor_resize_aspectratio">Manté la relació d'aspecte</resource> <resource name="editor_resize_aspectratio">Manté la relació d'aspecte</resource>
<resource name="editor_resize_width">Amplada</resource> <resource name="editor_resize_width">Amplada</resource>

View file

@ -308,7 +308,7 @@ Všechny funkce Greenshotu jsou stále dostupné přímo z místní nabídky bez
<resource name="editor_counter">Přidat počítadlo (I)</resource> <resource name="editor_counter">Přidat počítadlo (I)</resource>
<resource name="editor_speechbubble">Přidat textovou bublinu (S)</resource> <resource name="editor_speechbubble">Přidat textovou bublinu (S)</resource>
<resource name="editor_resize">Změnit velikost</resource> <resource name="editor_resize">Změnit velikost (Z)</resource>
<resource name="editor_resize_settings">Nastavení změny velikosti</resource> <resource name="editor_resize_settings">Nastavení změny velikosti</resource>
<resource name="editor_resize_aspectratio">Zachovat poměr stran</resource> <resource name="editor_resize_aspectratio">Zachovat poměr stran</resource>
<resource name="editor_resize_width">Šířka</resource> <resource name="editor_resize_width">Šířka</resource>

View file

@ -312,7 +312,7 @@ Sie können aber auch alle Greenshot-Funktionen über das Kontextmenü des Green
<resource name="editor_counter">Zähler hinzufügen (I)</resource> <resource name="editor_counter">Zähler hinzufügen (I)</resource>
<resource name="editor_speechbubble">Sprechblase hinzufügen (S)</resource> <resource name="editor_speechbubble">Sprechblase hinzufügen (S)</resource>
<resource name="editor_resize">Skalieren</resource> <resource name="editor_resize">Skalieren (Z)</resource>
<resource name="editor_resize_settings">Einstellungen für Skalierung</resource> <resource name="editor_resize_settings">Einstellungen für Skalierung</resource>
<resource name="editor_resize_aspectratio">Seitenverhältnis beibehalten</resource> <resource name="editor_resize_aspectratio">Seitenverhältnis beibehalten</resource>
<resource name="editor_resize_width">Breite</resource> <resource name="editor_resize_width">Breite</resource>

View file

@ -312,7 +312,7 @@ All Greenshot features still work directly from the tray icon context menu witho
<resource name="editor_counter">Add counter (I)</resource> <resource name="editor_counter">Add counter (I)</resource>
<resource name="editor_speechbubble">Add speechbubble (S)</resource> <resource name="editor_speechbubble">Add speechbubble (S)</resource>
<resource name="editor_resize">Resize</resource> <resource name="editor_resize">Resize (Z)</resource>
<resource name="editor_resize_settings">Resize settings</resource> <resource name="editor_resize_settings">Resize settings</resource>
<resource name="editor_resize_aspectratio">Maintain aspect ratio</resource> <resource name="editor_resize_aspectratio">Maintain aspect ratio</resource>
<resource name="editor_resize_width">Width</resource> <resource name="editor_resize_width">Width</resource>

View file

@ -144,7 +144,7 @@ De plus, nous apprécierions beaucoup que vous preniez la peine de vérifier si
<resource name="editor_print">Imprimer</resource> <resource name="editor_print">Imprimer</resource>
<resource name="editor_redo">Rétablir {0}</resource> <resource name="editor_redo">Rétablir {0}</resource>
<resource name="editor_resetsize">Réinitialiser la taille</resource> <resource name="editor_resetsize">Réinitialiser la taille</resource>
<resource name="editor_resize">Redimensionner</resource> <resource name="editor_resize">Redimensionner (Z)</resource>
<resource name="editor_resize_aspectratio">Maintenir le rapport L / H</resource> <resource name="editor_resize_aspectratio">Maintenir le rapport L / H</resource>
<resource name="editor_resize_height">Hauteur</resource> <resource name="editor_resize_height">Hauteur</resource>
<resource name="editor_resize_percent">Pourcentage</resource> <resource name="editor_resize_percent">Pourcentage</resource>

View file

@ -144,7 +144,7 @@ Juga, kami sangat terbantu apabila anda mengecek laporan lain yang sama dengan k
<resource name="editor_print">Cetak</resource> <resource name="editor_print">Cetak</resource>
<resource name="editor_redo">Ulang {0}</resource> <resource name="editor_redo">Ulang {0}</resource>
<resource name="editor_resetsize">Reset ukuran</resource> <resource name="editor_resetsize">Reset ukuran</resource>
<resource name="editor_resize">Ubah ukuran</resource> <resource name="editor_resize">Ubah ukuran (Z)</resource>
<resource name="editor_resize_aspectratio">Pertahankan aspek rasio</resource> <resource name="editor_resize_aspectratio">Pertahankan aspek rasio</resource>
<resource name="editor_resize_height">Tinggi</resource> <resource name="editor_resize_height">Tinggi</resource>
<resource name="editor_resize_percent">Persen</resource> <resource name="editor_resize_percent">Persen</resource>

View file

@ -322,7 +322,7 @@ In alternativa alle scorciatoie di tastiera, tutte le funzioni di Greenshot sono
<resource name="editor_counter">Aggiungi conteggio</resource> <resource name="editor_counter">Aggiungi conteggio</resource>
<resource name="editor_speechbubble">Aggiungi nuvoletta</resource> <resource name="editor_speechbubble">Aggiungi nuvoletta</resource>
<resource name="editor_resize">Ridimensiona</resource> <resource name="editor_resize">Ridimensiona (Z)</resource>
<resource name="editor_resize_settings">Impostazioni ridimensionamento</resource> <resource name="editor_resize_settings">Impostazioni ridimensionamento</resource>
<resource name="editor_resize_aspectratio">Mantieni rapporto dimensioni</resource> <resource name="editor_resize_aspectratio">Mantieni rapporto dimensioni</resource>
<resource name="editor_resize_width">Larghezza</resource> <resource name="editor_resize_width">Larghezza</resource>

View file

@ -143,7 +143,7 @@ Greenshot には一切の保障がありません。GNU General Public License
<resource name="editor_print">印刷</resource> <resource name="editor_print">印刷</resource>
<resource name="editor_redo">やり直し{0}</resource> <resource name="editor_redo">やり直し{0}</resource>
<resource name="editor_resetsize">サイズをリセット</resource> <resource name="editor_resetsize">サイズをリセット</resource>
<resource name="editor_resize">リサイズ</resource> <resource name="editor_resize">リサイズ (Z)</resource>
<resource name="editor_resize_aspectratio">縦横比を維持する</resource> <resource name="editor_resize_aspectratio">縦横比を維持する</resource>
<resource name="editor_resize_height">高さ</resource> <resource name="editor_resize_height">高さ</resource>
<resource name="editor_resize_percent">パーセント</resource> <resource name="editor_resize_percent">パーセント</resource>

View file

@ -144,7 +144,7 @@ Rnu ɣur-s, nḥemmel aṭas ma yella tesneqdeḍ aneqqis igebren ugur-agi. (Tze
<resource name="editor_print">Siggez</resource> <resource name="editor_print">Siggez</resource>
<resource name="editor_redo">Err-d {0}</resource> <resource name="editor_redo">Err-d {0}</resource>
<resource name="editor_resetsize">Wennez teɣzi</resource> <resource name="editor_resetsize">Wennez teɣzi</resource>
<resource name="editor_resize">Snifel tahri/teɣzi</resource> <resource name="editor_resize">Snifel tahri/teɣzi (Z)</resource>
<resource name="editor_resize_aspectratio">Eǧǧ afmiḍi Teɣ / Teh</resource> <resource name="editor_resize_aspectratio">Eǧǧ afmiḍi Teɣ / Teh</resource>
<resource name="editor_resize_height">Awrir</resource> <resource name="editor_resize_height">Awrir</resource>
<resource name="editor_resize_percent">Afmiḍi</resource> <resource name="editor_resize_percent">Afmiḍi</resource>

View file

@ -304,7 +304,7 @@ ${hostname} PC명
<resource name="editor_counter">카운터 더하기 (I)</resource> <resource name="editor_counter">카운터 더하기 (I)</resource>
<resource name="editor_speechbubble">설명선 더하기(S)</resource> <resource name="editor_speechbubble">설명선 더하기(S)</resource>
<resource name="editor_resize">크기조정</resource> <resource name="editor_resize">크기조정 (Z)</resource>
<resource name="editor_resize_settings">크기조정 설정</resource> <resource name="editor_resize_settings">크기조정 설정</resource>
<resource name="editor_resize_aspectratio">종횡비 유지</resource> <resource name="editor_resize_aspectratio">종횡비 유지</resource>
<resource name="editor_resize_width">너비</resource> <resource name="editor_resize_width">너비</resource>

View file

@ -306,7 +306,7 @@ Arī bez karstiem taustiņiem visas darbības iespējams veikt izmantojot „Gre
<resource name="editor_counter">Pievienot skaitli (I)</resource> <resource name="editor_counter">Pievienot skaitli (I)</resource>
<resource name="editor_speechbubble">Pievienot teksta norādi (S)</resource> <resource name="editor_speechbubble">Pievienot teksta norādi (S)</resource>
<resource name="editor_resize">Mainīt izmēru</resource> <resource name="editor_resize">Mainīt izmēru (Z)</resource>
<resource name="editor_resize_settings">Izmēra maiņas iestatījumi</resource> <resource name="editor_resize_settings">Izmēra maiņas iestatījumi</resource>
<resource name="editor_resize_aspectratio">Saglabāt izmēru attiecības</resource> <resource name="editor_resize_aspectratio">Saglabāt izmēru attiecības</resource>
<resource name="editor_resize_width">Platums</resource> <resource name="editor_resize_width">Platums</resource>

View file

@ -307,7 +307,7 @@ Alle Greenshot functies werken ook zonder sneltoetsen via het context menu.</res
<resource name="editor_counter">Teller toevoegen (I)</resource> <resource name="editor_counter">Teller toevoegen (I)</resource>
<resource name="editor_speechbubble">Tekstballon toevoegen (S)</resource> <resource name="editor_speechbubble">Tekstballon toevoegen (S)</resource>
<resource name="editor_resize">Grootte</resource> <resource name="editor_resize">Grootte (Z)</resource>
<resource name="editor_resize_settings">Vergrotingsinstellingen</resource> <resource name="editor_resize_settings">Vergrotingsinstellingen</resource>
<resource name="editor_resize_aspectratio">Verhouding behouden</resource> <resource name="editor_resize_aspectratio">Verhouding behouden</resource>
<resource name="editor_resize_width">Breedte</resource> <resource name="editor_resize_width">Breedte</resource>

View file

@ -305,7 +305,7 @@ Todas as funcionalidades do Greenshot funcionam directamente através do menu de
<resource name="editor_counter">Adicionar contador (I)</resource> <resource name="editor_counter">Adicionar contador (I)</resource>
<resource name="editor_speechbubble">Adicionar balão de texto (S)</resource> <resource name="editor_speechbubble">Adicionar balão de texto (S)</resource>
<resource name="editor_resize">Redimensionar</resource> <resource name="editor_resize">Redimensionar (Z)</resource>
<resource name="editor_resize_settings">Definições de Redimensionamento</resource> <resource name="editor_resize_settings">Definições de Redimensionamento</resource>
<resource name="editor_resize_aspectratio">Manter proporções</resource> <resource name="editor_resize_aspectratio">Manter proporções</resource>
<resource name="editor_resize_width">Largura</resource> <resource name="editor_resize_width">Largura</resource>

View file

@ -305,7 +305,7 @@ Alla Greenshots funktioner fungerar fortfarande från snabbmenyn i aktivitetsfä
<resource name="editor_counter">Lägg till räknare</resource> <resource name="editor_counter">Lägg till räknare</resource>
<resource name="editor_speechbubble">Lägg till pratbubbla</resource> <resource name="editor_speechbubble">Lägg till pratbubbla</resource>
<resource name="editor_resize">Anpassa storlek</resource> <resource name="editor_resize">Anpassa storlek (Z)</resource>
<resource name="editor_resize_settings">Storleksinställningar</resource> <resource name="editor_resize_settings">Storleksinställningar</resource>
<resource name="editor_resize_aspectratio">Behåll bildförhållande</resource> <resource name="editor_resize_aspectratio">Behåll bildförhållande</resource>
<resource name="editor_resize_width">Bredd</resource> <resource name="editor_resize_width">Bredd</resource>

View file

@ -306,7 +306,7 @@ ${hostname} назва комп’ютера
<resource name="editor_counter">Додати лічильник (Ш)</resource> <resource name="editor_counter">Додати лічильник (Ш)</resource>
<resource name="editor_speechbubble">Додати словесну бульбашку (І)</resource> <resource name="editor_speechbubble">Додати словесну бульбашку (І)</resource>
<resource name="editor_resize">Змінити розмір</resource> <resource name="editor_resize">Змінити розмір (Z)</resource>
<resource name="editor_resize_settings">Параметри зміни розміру</resource> <resource name="editor_resize_settings">Параметри зміни розміру</resource>
<resource name="editor_resize_aspectratio">Зберігати пропорції</resource> <resource name="editor_resize_aspectratio">Зберігати пропорції</resource>
<resource name="editor_resize_width">Ширина</resource> <resource name="editor_resize_width">Ширина</resource>

View file

@ -307,7 +307,7 @@ Greenshot 所有功能仍然可以直接從通知區圖示的內容功能表動
<resource name="editor_counter">加入計數器 (I)</resource> <resource name="editor_counter">加入計數器 (I)</resource>
<resource name="editor_speechbubble">加入對話框 (S)</resource> <resource name="editor_speechbubble">加入對話框 (S)</resource>
<resource name="editor_resize">縮放</resource> <resource name="editor_resize">縮放 (Z)</resource>
<resource name="editor_resize_settings">縮放設定</resource> <resource name="editor_resize_settings">縮放設定</resource>
<resource name="editor_resize_aspectratio">維持長寬比</resource> <resource name="editor_resize_aspectratio">維持長寬比</resource>
<resource name="editor_resize_width">長度</resource> <resource name="editor_resize_width">長度</resource>